This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new a0cbc85a Split ArrayConverter list elements on whitespace, quotes and 
the delimiter only (#427)
a0cbc85a is described below

commit a0cbc85af9ed7a7dbb01e92f471c7144bab6933b
Author: Naveed Khan <[email protected]>
AuthorDate: Wed Jul 29 15:00:23 2026 +0000

    Split ArrayConverter list elements on whitespace, quotes and the delimiter 
only (#427)
    
    * disable comment char in ArrayConverter list parsing
    
    parseElements left StreamTokenizer's default comment character '/' active, 
so a list element containing a slash commented out the rest of the input and 
dropped the following elements. Treat '/' as an ordinary separator instead.
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
    
    * cover allowed-chars behavior for forward slash in test
    
    * add missing @Test annotation to testForwardSlashSeparator
    
    * split list elements on whitespace, quotes and the delimiter only
    
    Reset the StreamTokenizer syntax table in ArrayConverter.parseElements so
    every character is part of an element except whitespace, the delimiter and
    the quote characters. first_value,second_value and first/value,second/value
    now both parse to two elements instead of four, and the default comment
    char is gone as part of the reset. setAllowedChars no longer has any
    effect and is deprecated. A plain split on the delimiter was ruled out
    because ConvertUtilsTest pins whitespace separation and quote handling.
    
    * Revise deprecation message in ArrayConverter
    
    Updated deprecation notice for setAllowedChars method.
    
    ---------
    
    Co-authored-by: Gary Gregory <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../beanutils2/converters/ArrayConverter.java      | 40 ++++++++++++----------
 .../commons/beanutils2/bugs/Jira359Test.java       |  8 ++---
 .../beanutils2/converters/ArrayConverterTest.java  | 37 +++++---------------
 3 files changed, 32 insertions(+), 53 deletions(-)

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 bfcc4e4e..5f8faae3 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 @@ import org.apache.commons.beanutils2.Converter;
  * </li>
  * </ul>
  *
- * <h2>Parsing Delimited Lists</h2> 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:
+ * <h2>Parsing Delimited Lists</h2> 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:
  * <ul>
  * <li>{@code setDelimiter(char)} - allows the character used as the delimiter 
to be configured [default is a comma].</li>
- * <li>{@code setAllowedChars(char[])} - adds additional characters (to the 
default alphabetic/numeric) to those considered to be valid token 
characters.</li>
  * </ul>
  *
  * <h2>Multi Dimensional Arrays</h2> It is possible to convert a {@code 
String} to multi-dimensional arrays by using {@link ArrayConverter} as the 
element
@@ -89,11 +90,8 @@ import org.apache.commons.beanutils2.Converter;
  * // 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 @@ public class ArrayConverter<C> extends 
AbstractConverter<C> {
      * according to the following rules.
      * </p>
      * <ul>
-     * <li>The string is expected to be a comma-separated list of values.</li>
+     * <li>The string is split on the delimiter [default is a comma] and on 
whitespace; every other character is kept as part of an element.</li>
      * <li>The string may optionally have matching '{' and '}' delimiters 
around the list.</li>
-     * <li>Whitespace before and after each element is stripped.</li>
-     * <li>Elements in the list may be delimited by single or double quotes. 
Within a quoted elements, the normal Java escape sequences are valid.</li>
+     * <li>Elements in the list may be delimited by single or double quotes. A 
quoted element may contain whitespace and the delimiter, and within a quoted
+     * element the normal Java escape sequences are valid.</li>
      * </ul>
      *
      * @param value String value to be parsed
@@ -335,17 +333,18 @@ public class ArrayConverter<C> extends 
AbstractConverter<C> {
         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<String> list = null;
             while (true) {
                 final int ttype = st.nextToken();
@@ -382,7 +381,10 @@ public class ArrayConverter<C> extends 
AbstractConverter<C> {
      * 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 fc54c765..da275b38 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 @@ class Jira359Test {
         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 6f7be51f..890e4e33 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 @@ class ArrayConverterTest {
         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<String[]> 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 @@ class ArrayConverterTest {
         // 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 @@ class ArrayConverterTest {
     }
 
     /**
-     * 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<String[]> 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<String[]> 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]");

Reply via email to