Split ArrayConverter list elements on whitespace, quotes and the delimiter only - #427
Conversation
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.
There was a problem hiding this comment.
Pull request overview
This pull request fixes ArrayConverter.parseElements treating / as a comment introducer during StreamTokenizer parsing, which could silently drop later elements when list items contain forward slashes.
Changes:
- Disable
/as a comment character inStreamTokenizersetup so it behaves like other non-allowed separator characters. - Add a regression test ensuring forward slashes split tokens rather than truncating input.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java | Configures StreamTokenizer to treat / as an ordinary character instead of a comment start during element parsing. |
| src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTest.java | Adds a unit test covering forward-slash behavior in delimited list parsing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@rootvector2 Please address #426 (comment) |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| final String[] result = converter.convert(String[].class, value); | ||
| assertNotNull(result, "result.null"); | ||
| assertEquals(4, result.length, "result.length"); | ||
| assertEquals("first", result[0], "result[0]"); |
There was a problem hiding this comment.
Are you sure?
The / now behaves the same as a ,? That can't be right. Shouldn't the asserts be:
assertEquals("first/value", result[0]);
assertEquals("second/value", result[1]);?
There was a problem hiding this comment.
Yes. Any non-allowed char splits and keeps both sides by default, same as _ in testUnderscore_BEANUTILS_302 (first_value,second_value parses to 4 elements). Before the patch / was the only char that instead commented out the rest of the input, so a/b,c parsed to just ["a"]. Your asserts hold once / is in the allowed chars, same as _. I extended the test to mirror the underscore test: 4 elements by default, ["first/value", "second/value"] after setAllowedChars(new char[] { '.', '-', '/' }).
garydgregory
left a comment
There was a problem hiding this comment.
@rootvector2 Please see my questions.
|
Done on #426, switched the test to |
|
Switched to draft PR until @rootvector2 replies. |
|
Hello @rootvector2 |
|
Still needed. master picked up the test in 3d7c330 but without |
|
Thank you for the update @rootvector2 , I triggered the builds. |
|
Hi @rootvector2 Before the PR, the input After the PR, we get FOUR elements because both the I think users would be quite surprised by this new data, as opposed to two elements separated by the one Please explain. Thank you! |
|
The choice here isn't 2 vs 4, it's 1 vs 4. Before the patch On If you'd rather make |
|
The surprising part is what happens today: nobody gets two elements. Before the patch Four matches the On If you'd rather plain input give |
|
Thank you for your patience reviewing this issue. Hm, I see what you're saying, but The intention is stated in the Javadoc:
and the setters: /**
* 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 '-']
*/
public void setAllowedChars(final char[] allowedChars) {
this.allowedChars = Objects.requireNonNull(allowedChars, "allowedChars").clone();
}
/**
* Sets the delimiter to be used for parsing a delimited String.
*
* @param delimiter The delimiter [default ',']
*/
public void setDelimiter(final char delimiter) {
this.delimiter = delimiter;
}So, by default there is only one delimiter:
The fact that the internals use a string tokenizer is an implementation detail, likely because this code was written before Java implemented a regex API. If using a regex API is better (split or Pattern), then let's do that. WDYT? |
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.
|
I started with the plain regex split, but So I kept the tokenizer and reset its syntax table instead: only whitespace, the delimiter and the quote chars split, every other character is kept as part of an element. That gives the results you describe: If you'd rather have strict delimiter-only splitting (no whitespace splitting, no quotes), that's the regex split plus retiring those |
Updated deprecation notice for setAllowedChars method.
|
@rootvector2 |
ArrayConverter.parseElementsset up aStreamTokenizerwith its default syntax table, which treats/as a comment start (a/b,cparsed to["a"], everything after the first slash silently dropped) and splits elements on any non-alphanumeric character outsideallowedChars(first_value,second_valueparsed to four elements). Per review, the syntax table is now reset so that only whitespace, the delimiter and the quote characters separate elements; every other character is kept as part of an element.first_value,second_valueandfirst/value,second/valueboth parse to two elements now. Whitespace separation (0 10parses to two elements) and quote handling ('de,f'keeps its embedded delimiter) are unchanged, as pinned byConvertUtilsTest.setAllowedCharsno longer has any effect and is deprecated;testUnderscore_BEANUTILS_302,testTheMatrixandJira359Testare updated accordingly.mvn; that'smvnon the command line by itself.