diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java index bfcc4e4e7..5f8faae34 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java @@ -64,11 +64,12 @@ * * * - *

Parsing Delimited Lists

This implementation can convert a delimited list in {@code String} format into an array of the appropriate type. By default, - * it uses a comma as the delimiter but the following methods can be used to configure parsing: + *

Parsing Delimited Lists

This implementation can convert a delimited list in {@code String} format into an array of the appropriate type. The + * String is split on the delimiter and on whitespace; every other character is kept as part of an element, and elements may be quoted with single or double + * quotes to protect embedded whitespace or delimiters. By default, it uses a comma as the delimiter but the following method can be used to configure + * parsing: * * *

Multi Dimensional Arrays

It is possible to convert a {@code String} to multi-dimensional arrays by using {@link ArrayConverter} as the element @@ -89,11 +90,8 @@ * // Construct a "Matrix" Converter which converts arrays of integer arrays using * // the preceding ArrayConverter as the element Converter. * // Uses a semicolon (i.e. ";") as the delimiter to separate the different sets of numbers. - * // Also the delimiter used by the first ArrayConverter needs to be added to the - * // "allowed characters" for this one. * ArrayConverter matrixConverter = new ArrayConverter(int[][].class, arrayConverter); * matrixConverter.setDelimiter(';'); - * matrixConverter.setAllowedChars(new char[] { ',' }); * * // Do the Conversion * String matrixString = "11,12,13 ; 21,22,23 ; 31,32,33 ; 41,42,43"; @@ -310,10 +308,10 @@ protected Class getDefaultType() { * according to the following rules. *

* * * @param value String value to be parsed @@ -335,17 +333,18 @@ private List parseElements(String value) { final String typeName = toString(String.class); try { - // Set up a StreamTokenizer on the characters in this String + // Set up a StreamTokenizer on the characters in this String. Every character is part of a token except whitespace, the quote characters and the + // delimiter, so elements are only split on those. The default syntax table would also split on any other non-alphanumeric character and treat + // '/' as a comment start, silently dropping the rest of the input. final StreamTokenizer st = new StreamTokenizer(new StringReader(value)); - st.whitespaceChars(delimiter, delimiter); // Set the delimiters - st.ordinaryChars('0', '9'); // Needed to turn off numeric flag - st.wordChars('0', '9'); // Needed to make part of tokens - for (final char allowedChar : allowedChars) { - st.ordinaryChars(allowedChar, allowedChar); - st.wordChars(allowedChar, allowedChar); - } - - // Split comma-delimited tokens into a List + st.resetSyntax(); + st.wordChars(0, 255); // Everything is part of a token... + st.whitespaceChars(0, ' '); // ...except whitespace... + st.quoteChar('"'); // ...quoted elements, which may contain whitespace and the delimiter... + st.quoteChar('\''); + st.whitespaceChars(delimiter, delimiter); // ...and the delimiter, which separates elements. + + // Split the tokens into a List List list = null; while (true) { final int ttype = st.nextToken(); @@ -382,7 +381,10 @@ private List parseElements(String value) { * Sets the allowed characters to be used for parsing a delimited String. * * @param allowedChars Characters which are to be considered as part of the tokens when parsing a delimited String [default is '.' and '-'] + * @deprecated Since 1.12.0: No longer has any effect: every character apart from whitespace, the delimiter and the quote characters is kept + * as part of an element. */ + @Deprecated public void setAllowedChars(final char[] allowedChars) { this.allowedChars = Objects.requireNonNull(allowedChars, "allowedChars").clone(); } diff --git a/src/test/java/org/apache/commons/beanutils2/bugs/Jira359Test.java b/src/test/java/org/apache/commons/beanutils2/bugs/Jira359Test.java index fc54c7657..da275b38f 100644 --- a/src/test/java/org/apache/commons/beanutils2/bugs/Jira359Test.java +++ b/src/test/java/org/apache/commons/beanutils2/bugs/Jira359Test.java @@ -105,11 +105,9 @@ void testBeanUtilsSetProperty_DefaultConvertStringToArray_WithColonValue() throw final SimplePojoData simplePojo = new SimplePojoData(); BeanUtils.setProperty(simplePojo, "jcrMixinTypes", "mix:rereferencible,mix:simple"); showArray("Default WithColonValue", simplePojo.getJcrMixinTypes()); - assertEquals(4, simplePojo.getJcrMixinTypes().length, "array size"); - assertEquals("mix", simplePojo.getJcrMixinTypes()[0]); - assertEquals("rereferencible", simplePojo.getJcrMixinTypes()[1]); - assertEquals("mix", simplePojo.getJcrMixinTypes()[2]); - assertEquals("simple", simplePojo.getJcrMixinTypes()[3]); + assertEquals(2, simplePojo.getJcrMixinTypes().length, "array size"); + assertEquals("mix:rereferencible", simplePojo.getJcrMixinTypes()[0]); + assertEquals("mix:simple", simplePojo.getJcrMixinTypes()[1]); } /** diff --git a/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTest.java b/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTest.java index 6f7be51f4..890e4e333 100644 --- a/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTest.java +++ b/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTest.java @@ -163,21 +163,14 @@ void testErrors() { assertThrows(NullPointerException.class, () -> new ArrayConverter(int[].class, null)); } + /** + * A forward slash must be kept as part of an element instead of starting a comment and dropping the rest of the input. + */ + @Test void testForwardSlashSeparator() { final String value = "first/value,second/value"; final ArrayConverter converter = new ArrayConverter<>(String[].class, new StringConverter()); - // test forward slash not allowed (the default) - String[] result = converter.convert(String[].class, value); - assertNotNull(result, "result.null"); - assertEquals(4, result.length, "result.length"); - assertEquals("first", result[0], "result[0]"); - assertEquals("value", result[1], "result[1]"); - assertEquals("second", result[2], "result[2]"); - assertEquals("value", result[3], "result[3]"); - // configure the converter to allow forward slash - converter.setAllowedChars(new char[] { '.', '-', '/' }); - // test forward slash allowed - result = converter.convert(String[].class, value); + final String[] result = converter.convert(String[].class, value); assertNotNull(result, "result.null"); assertEquals(2, result.length, "result.length"); assertEquals("first/value", result[0], "result[0]"); @@ -248,11 +241,8 @@ void testTheMatrix() { // Construct a "Matrix" Converter which converts arrays of integer arrays using // the first (int[]) Converter as the element Converter. // Uses a semicolon (i.e. ";") as the delimiter to separate the different sets of numbers. - // Also the delimiter for the above array Converter needs to be added to this - // array Converter's "allowed characters" final ArrayConverter matrixConverter = new ArrayConverter(int[][].class, arrayConverter); matrixConverter.setDelimiter(';'); - matrixConverter.setAllowedChars(new char[] { ',' }); // Do the Conversion final Object result = matrixConverter.convert(int[][].class, matrixString); // Check it actually worked OK @@ -270,24 +260,13 @@ void testTheMatrix() { } /** - * Test for BEANUTILS-302 throwing a NPE when underscore used. + * Test for BEANUTILS-302 throwing a NPE when underscore used. The underscore is kept as part of the element. */ @Test void testUnderscore_BEANUTILS_302() { final String value = "first_value,second_value"; - final ArrayConverter converter = new ArrayConverter(String[].class, new StringConverter()); - // test underscore not allowed (the default) - String[] result = converter.convert(String[].class, value); - assertNotNull(result, "result.null"); - assertEquals(4, result.length, "result.length"); - assertEquals("first", result[0], "result[0]"); - assertEquals("value", result[1], "result[1]"); - assertEquals("second", result[2], "result[2]"); - assertEquals("value", result[3], "result[3]"); - // configure the converter to allow underscore - converter.setAllowedChars(new char[] { '.', '-', '_' }); - // test underscore allowed - result = converter.convert(String[].class, value); + final ArrayConverter converter = new ArrayConverter<>(String[].class, new StringConverter()); + final String[] result = converter.convert(String[].class, value); assertNotNull(result, "result.null"); assertEquals(2, result.length, "result.length"); assertEquals("first_value", result[0], "result[0]");