Author: oheger Date: Sun Jun 6 16:12:08 2010 New Revision: 951884 URL: http://svn.apache.org/viewvc?rev=951884&view=rev Log: [CONFIGURATION-418] Improved handling of escaped list delimiters. Ported fix to configuration2 branch.
Modified: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java commons/proper/configuration/branches/configuration2_experimental/src/test/resources/test.properties Modified: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java?rev=951884&r1=951883&r2=951884&view=diff ============================================================================== --- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java (original) +++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java Sun Jun 6 16:12:08 2010 @@ -212,6 +212,9 @@ public class PropertiesConfiguration ext /** Constant for the escaping character.*/ private static final String ESCAPE = "\\"; + /** Constant for the escaped escaping character.*/ + private static final String DOUBLE_ESC = ESCAPE + ESCAPE; + /** Constant for the radix of hex numbers.*/ private static final int HEX_RADIX = 16; @@ -594,6 +597,24 @@ public class PropertiesConfiguration ext } /** + * Returns the number of trailing backslashes. This is sometimes needed for + * the correct handling of escape characters. + * + * @param line the string to investigate + * @return the number of trailing backslashes + */ + private static int countTrailingBS(String line) + { + int bsCount = 0; + for (int idx = line.length() - 1; idx >= 0 && line.charAt(idx) == '\\'; idx--) + { + bsCount++; + } + + return bsCount; + } + + /** * This class is used to read properties lines. These lines do * not terminate with new-line chars but rather when there is no * backslash sign a the end of the line. This is used to @@ -837,13 +858,7 @@ public class PropertiesConfiguration ext */ private static boolean checkCombineLines(String line) { - int bsCount = 0; - for (int idx = line.length() - 1; idx >= 0 && line.charAt(idx) == '\\'; idx--) - { - bsCount++; - } - - return bsCount % 2 != 0; + return countTrailingBS(line) % 2 != 0; } /** @@ -1018,7 +1033,7 @@ public class PropertiesConfiguration ext { String v; - if (value instanceof List) + if (value instanceof List<?>) { List<?> values = (List<?>) value; if (forceSingleLine) @@ -1033,7 +1048,7 @@ public class PropertiesConfiguration ext } else { - v = escapeValue(value); + v = escapeValue(value, false); } write(escapeKey(key)); @@ -1089,12 +1104,13 @@ public class PropertiesConfiguration ext * will be escaped. * * @param value the property value + * @param inList a flag whether the value is part of a list * @return the escaped property value * @since 1.3 */ - private String escapeValue(Object value) + private String escapeValue(Object value, boolean inList) { - String escapedValue = StringEscapeUtils.escapeJava(String.valueOf(value)); + String escapedValue = handleBackslashs(value, inList); if (delimiter != 0) { escapedValue = StringUtils.replace(escapedValue, String.valueOf(delimiter), ESCAPE + delimiter); @@ -1103,6 +1119,45 @@ public class PropertiesConfiguration ext } /** + * Performs the escaping of backslashes in the specified properties + * value. Because a double backslash is used to escape the escape + * character of a list delimiter, double backslashes also have to be + * escaped if the property is part of a (single line) list. Then, in all cases each backslash has to be doubled in order to produce a + * valid properties file. + * + * @param value the value to be escaped + * @param inList a flag whether the value is part of a list + * @return the value with escaped backslashes as string + */ + private String handleBackslashs(Object value, boolean inList) + { + String strValue = String.valueOf(value); + + if (inList && strValue.indexOf(DOUBLE_ESC) >= 0) + { + char esc = ESCAPE.charAt(0); + StringBuffer buf = new StringBuffer(strValue.length() + 8); + for (int i = 0; i < strValue.length(); i++) + { + if (strValue.charAt(i) == esc && i < strValue.length() - 1 + && strValue.charAt(i + 1) == esc) + { + buf.append(DOUBLE_ESC).append(DOUBLE_ESC); + i++; + } + else + { + buf.append(strValue.charAt(i)); + } + } + + strValue = buf.toString(); + } + + return StringEscapeUtils.escapeJava(strValue); + } + + /** * Transforms a list of values into a single line value. * * @param values the list with the values @@ -1114,19 +1169,19 @@ public class PropertiesConfiguration ext if (!values.isEmpty()) { Iterator<?> it = values.iterator(); - String lastValue = escapeValue(it.next()); + String lastValue = escapeValue(it.next(), true); StringBuilder buf = new StringBuilder(lastValue); while (it.hasNext()) { // if the last value ended with an escape character, it has // to be escaped itself; otherwise the list delimiter will // be escaped - if (lastValue.endsWith(ESCAPE)) + if (lastValue.endsWith(ESCAPE) && (countTrailingBS(lastValue) / 2) % 2 != 0) { buf.append(ESCAPE).append(ESCAPE); } buf.append(delimiter); - lastValue = escapeValue(it.next()); + lastValue = escapeValue(it.next(), true); buf.append(lastValue); } return buf.toString(); Modified: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java?rev=951884&r1=951883&r2=951884&view=diff ============================================================================== --- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java (original) +++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java Sun Jun 6 16:12:08 2010 @@ -991,6 +991,33 @@ public class TestPropertiesConfiguration } /** + * Tests whether backslashes are correctly handled if lists are parsed. This + * test is related to CONFIGURATION-418. + */ + public void testBackslashEscapingInLists() throws ConfigurationException + { + checkBackslashList("share2"); + checkBackslashList("share1"); + } + + /** + * Helper method for testing the content of a list with elements that + * contain backslashes. + * + * @param key the key + */ + private void checkBackslashList(String key) + { + Object prop = conf.getProperty("test." + key); + assertTrue("Not a list", prop instanceof List<?>); + List<?> list = (List<?>) prop; + assertEquals("Wrong number of list elements", 2, list.size()); + final String prefix = "\\\\" + key; + assertEquals("Wrong element 1", prefix + "a", list.get(0)); + assertEquals("Wrong element 2", prefix + "b", list.get(1)); + } + + /** * Creates a configuration that can be used for testing copy operations. * * @return the configuration to be copied Modified: commons/proper/configuration/branches/configuration2_experimental/src/test/resources/test.properties URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/resources/test.properties?rev=951884&r1=951883&r2=951884&view=diff ============================================================================== --- commons/proper/configuration/branches/configuration2_experimental/src/test/resources/test.properties (original) +++ commons/proper/configuration/branches/configuration2_experimental/src/test/resources/test.properties Sun Jun 6 16:12:08 2010 @@ -102,3 +102,9 @@ test.separator.formfeedfoo test.separator.whitespace foo test.separator.no.space=foo +# Tests for backslash escaping in lists +test.share1 = \\\\\\\\share1a, \\\\\\\\share1b +test.share2 = \\\\share2a +test.share2 = \\\\share2b +test.share3 = \\\\\\\\share3a\\\\\\\\,\\\\\\\\share3b\\ +