diff options
author | Fredrik Roubert <fredrik@roubert.name> | 2022-05-16 08:07:31 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-16 08:07:31 +0300 |
commit | 5251dfb5f83d42f89e0dcbdb65ed14ed5c6ee115 (patch) | |
tree | 5aa5a31ba29e17acf7a8f7d11aa5684e9fa01cfb | |
parent | e3d8183d1b13f450ea9a9e4eda21973220111cb5 (diff) |
DIRSTUDIO-1296: Decode RFC 4517 Postal Address syntax upon export (#32)
6 files changed, 154 insertions, 1 deletions
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportCsvRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportCsvRunnable.java index 7d4382ae4..e6195ba77 100644 --- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportCsvRunnable.java +++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportCsvRunnable.java @@ -30,7 +30,9 @@ import java.util.HashMap; import java.util.Map; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.directory.api.ldap.model.constants.SchemaConstants; import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.schema.AttributeType; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; import org.apache.directory.studio.connection.core.Connection; import org.apache.directory.studio.connection.core.jobs.StudioConnectionRunnableWithProgress; @@ -41,6 +43,7 @@ import org.apache.directory.studio.ldapbrowser.core.model.AttributeDescription; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.apache.directory.studio.ldapbrowser.core.model.SearchParameter; import org.apache.directory.studio.ldapbrowser.core.utils.JNDIUtils; +import org.apache.directory.studio.ldapbrowser.core.utils.Utils; import org.apache.directory.studio.ldifparser.LdifUtils; import org.apache.directory.studio.ldifparser.model.LdifEnumeration; import org.apache.directory.studio.ldifparser.model.container.LdifContainer; @@ -288,6 +291,11 @@ public class ExportCsvRunnable implements StudioConnectionRunnableWithProgress if ( attributeMap.containsKey( oidString ) ) { String value = attributeMap.get( oidString ); + AttributeType type = browserConnection.getSchema().getAttributeTypeDescription( attributeName ); + if ( SchemaConstants.POSTAL_ADDRESS_SYNTAX.equals( type.getSyntaxOid() ) ) + { + value = Utils.decodePostalAddress( value, lineSeparator ); + } appendValue( quoteCharacter, sb, value ); } diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportOdfRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportOdfRunnable.java index 191eab35a..abbed1d05 100644 --- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportOdfRunnable.java +++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportOdfRunnable.java @@ -26,7 +26,9 @@ import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.directory.api.ldap.model.constants.SchemaConstants; import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.schema.AttributeType; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; import org.apache.directory.studio.connection.core.Connection; import org.apache.directory.studio.connection.core.jobs.StudioConnectionRunnableWithProgress; @@ -36,6 +38,7 @@ import org.apache.directory.studio.ldapbrowser.core.BrowserCorePlugin; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.apache.directory.studio.ldapbrowser.core.model.SearchParameter; import org.apache.directory.studio.ldapbrowser.core.utils.JNDIUtils; +import org.apache.directory.studio.ldapbrowser.core.utils.Utils; import org.apache.directory.studio.ldifparser.model.LdifEnumeration; import org.apache.directory.studio.ldifparser.model.container.LdifContainer; import org.apache.directory.studio.ldifparser.model.container.LdifContentRecord; @@ -290,6 +293,13 @@ public class ExportOdfRunnable implements StudioConnectionRunnableWithProgress short cellNum = headerRowAttributeNameMap.get( attributeName ).shortValue(); Cell cell = row.getCellByIndex( cellNum ); cell.setValueType( ValueType.STRING.name() ); + AttributeType type = browserConnection.getSchema().getAttributeTypeDescription( attributeName ); + if ( SchemaConstants.POSTAL_ADDRESS_SYNTAX.equals( type.getSyntaxOid() ) ) + { + // https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocument-v1.3-os-part4-formula.html#__RefHeading__1017970_715980110 + value = Utils.decodePostalAddress( value, "\n" ); //$NON-NLS-1$ + cell.setTextWrapped( true ); + } cell.setStringValue( value ); } } diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportXlsRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportXlsRunnable.java index cde55db3a..203002223 100644 --- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportXlsRunnable.java +++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportXlsRunnable.java @@ -27,7 +27,9 @@ import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.directory.api.ldap.model.constants.SchemaConstants; import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.schema.AttributeType; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; import org.apache.directory.studio.connection.core.Connection; import org.apache.directory.studio.connection.core.jobs.StudioConnectionRunnableWithProgress; @@ -37,6 +39,7 @@ import org.apache.directory.studio.ldapbrowser.core.BrowserCorePlugin; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.apache.directory.studio.ldapbrowser.core.model.SearchParameter; import org.apache.directory.studio.ldapbrowser.core.utils.JNDIUtils; +import org.apache.directory.studio.ldapbrowser.core.utils.Utils; import org.apache.directory.studio.ldifparser.model.LdifEnumeration; import org.apache.directory.studio.ldifparser.model.container.LdifContainer; import org.apache.directory.studio.ldifparser.model.container.LdifContentRecord; @@ -45,6 +48,7 @@ import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; import org.eclipse.core.runtime.Preferences; @@ -280,6 +284,9 @@ public class ExportXlsRunnable implements StudioConnectionRunnableWithProgress Map<String, String> attributeMap = ExportCsvRunnable.getAttributeMap( null, record, valueDelimiter, "UTF-16", //$NON-NLS-1$ binaryEncoding ); + CellStyle wrapStyle = sheet.getWorkbook().createCellStyle(); + wrapStyle.setWrapText( true ); + // output attributes HSSFRow row = sheet.createRow( sheet.getLastRowNum() + 1 ); if ( exportDn ) @@ -303,6 +310,13 @@ public class ExportXlsRunnable implements StudioConnectionRunnableWithProgress { int cellNum = headerRowAttributeNameMap.get( attributeName ).shortValue(); HSSFCell cell = createStringCell( row, cellNum ); + AttributeType type = browserConnection.getSchema().getAttributeTypeDescription( attributeName ); + if ( SchemaConstants.POSTAL_ADDRESS_SYNTAX.equals( type.getSyntaxOid() ) ) + { + // https://poi.apache.org/components/spreadsheet/quick-guide.html#NewLinesInCells + value = Utils.decodePostalAddress( value, "\n" ); //$NON-NLS-1$ + cell.setCellStyle( wrapStyle ); + } cell.setCellValue( value ); } } diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java index 2cc7bad60..bb20a39cd 100644 --- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java +++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java @@ -657,4 +657,31 @@ public class Utils return LdifAttrValLine.create( attribute.getDescription(), value.getStringValue() ); } } + + + /** + * Decodes the RFC 4517 Postal Address syntax. + * + * <pre> + * PostalAddress = line *( DOLLAR line ) + * line = 1*line-char + * line-char = %x00-23 + * / (%x5C "24") ; escaped "$" + * / %x25-5B + * / (%x5C "5C") ; escaped "\" + * / %x5D-7F + * / UTFMB + * </pre> + * + * @param input the encoded string + * @param separator the separator to output between address lines + * @return the decoded string + */ + public static String decodePostalAddress( String input, String separator ) + { + return input.replace( "$", separator ) //$NON-NLS-1$ + .replace( "\\24", "$" ) //$NON-NLS-1$ //$NON-NLS-2$ + .replace( "\\5C", "\\" ) //$NON-NLS-1$ //$NON-NLS-2$ + .replace( "\\5c", "\\" ); //$NON-NLS-1$ //$NON-NLS-2$ + } } diff --git a/plugins/ldapbrowser.core/src/test/java/org/apache/directory/studio/ldapbrowser/core/utils/UtilsTest.java b/plugins/ldapbrowser.core/src/test/java/org/apache/directory/studio/ldapbrowser/core/utils/UtilsTest.java new file mode 100644 index 000000000..d7c76ca57 --- /dev/null +++ b/plugins/ldapbrowser.core/src/test/java/org/apache/directory/studio/ldapbrowser/core/utils/UtilsTest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.directory.studio.ldapbrowser.core.utils; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + + +public class UtilsTest +{ + @Test + public void testPostalAddressTrivial() + { + assertEquals( "abc", Utils.decodePostalAddress( "abc", "!" ) ); + } + + + @Test + public void testPostalAddressEscaped() + { + assertEquals( "!", Utils.decodePostalAddress( "$", "!" ) ); + assertEquals( "$", Utils.decodePostalAddress( "\\24", "!" ) ); + assertEquals( "\\", Utils.decodePostalAddress( "\\5C", "!" ) ); + assertEquals( "\\", Utils.decodePostalAddress( "\\5c", "!" ) ); + } + + + @Test + public void testPostalAddressRfcExamples() + { + assertEquals( "1234 Main St.\nAnytown, CA 12345\nUSA", + Utils.decodePostalAddress( "1234 Main St.$Anytown, CA 12345$USA", "\n" ) ); + assertEquals( "$1,000,000 Sweepstakes\nPO Box 1000000\nAnytown, CA 12345\nUSA", + Utils.decodePostalAddress( "\\241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA", "\n" ) ); + } +} diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ImportExportTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ImportExportTest.java index fcf5291d2..0ff46aa15 100644 --- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ImportExportTest.java +++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ImportExportTest.java @@ -625,7 +625,9 @@ public class ImportExportTest extends AbstractTestBase store.setDefault( BrowserCoreConstants.PREFERENCE_FORMAT_CSV_ENCODING, "UTF-8" ); URL url = Platform.getInstanceLocation().getURL(); - final String file = url.getFile() + "ImportExportTest" + server.getType().name() + ".csv"; + final String file = url.getFile() + + "ImportExportShouldPrefixFormulaWithApostropheTest" + + server.getType().name() + ".csv"; browserViewBot.selectEntry( path( GERMAN_UMLAUT_DN ) ); @@ -647,6 +649,44 @@ public class ImportExportTest extends AbstractTestBase /** + * Export to CSV and checks that RFC 4517 Postal Address syntax is decoded. + */ + @ParameterizedTest + @LdapServersSource + public void testExportCsvShouldDecodePostalAddress( TestLdapServer server ) throws Exception + { + connectionsViewBot.createTestConnection( server ); + // set CSV encoding explicit to UTF-8, otherwise platform default encoding would be used + Preferences store = BrowserCorePlugin.getDefault().getPluginPreferences(); + store.setDefault( BrowserCoreConstants.PREFERENCE_FORMAT_CSV_ENCODING, "UTF-8" ); + + URL url = Platform.getInstanceLocation().getURL(); + final String file = url.getFile() + + "ImportExportShouldDecodePostalAddressTest" + + server.getType().name() + ".csv"; + + browserViewBot.selectEntry( path( USER1_DN ) ); + + // export CSV + ExportWizardBot wizardBot = browserViewBot.openExportCsvWizard(); + assertTrue( wizardBot.isVisible() ); + wizardBot.setReturningAttributes( "postalAddress" ); + wizardBot.clickNextButton(); + wizardBot.typeFile( file ); + wizardBot.clickFinishButton(); + wizardBot.waitTillExportFinished( file, 100 ); + + List<String> lines = FileUtils.readLines( new File( file ), StandardCharsets.UTF_8 ); + // verify that the first line is header + assertEquals( "dn,postalAddress", lines.get( 0 ) ); + // verify that the postal address is broken into several lines + assertEquals( "\"uid=user.1,ou=users,dc=example,dc=org\",\"Aaccf Amar", lines.get( 1 ) ); + assertEquals( "27919 Broadway Street", lines.get( 2 ) ); + assertEquals( "Tallahassee, DE 67698\"", lines.get( 3 ) ); + } + + + /** * Test for DIRSTUDIO-1160. * * Attributes silently dropped and not imported when import LDIF and provider is Apache Directory LDAP API. |