This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 99592ef04 Test modernization
99592ef04 is described below
commit 99592ef0483dc68bec41542e7443e7745ebb7bcc
Author: James Bognar <[email protected]>
AuthorDate: Fri Sep 12 12:03:15 2025 -0400
Test modernization
---
.../java/org/apache/juneau/NestedTokenizer.java | 253 ---------------------
1 file changed, 253 deletions(-)
diff --git a/juneau-utest/src/test/java/org/apache/juneau/NestedTokenizer.java
b/juneau-utest/src/test/java/org/apache/juneau/NestedTokenizer.java
deleted file mode 100644
index 8cec94e8e..000000000
--- a/juneau-utest/src/test/java/org/apache/juneau/NestedTokenizer.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package org.apache.juneau;
-
-import static org.apache.juneau.NestedTokenizer.ParseState.*;
-import static java.util.Collections.*;
-import static java.util.stream.Collectors.*;
-
-import java.util.*;
-import java.util.function.*;
-
-/**
- * Splits a nested comma-delimited string into a list of Token objects using a
state machine parser.
- *
- * <p>This class parses complex nested structures with support for escaping
and arbitrary nesting depth.
- * The parser uses a finite state machine to handle different contexts during
parsing.</p>
- *
- * <h5 class='section'>Supported Syntax:</h5>
- * <ul>
- * <li><js>"foo"</js> - Single value token</li>
- * <li><js>"foo,bar"</js> - Multiple value tokens</li>
- * <li><js>"foo{a,b},bar"</js> - Token with nested values</li>
- * <li><js>"foo{a{a1,a2}},bar"</js> - Recursively nested values</li>
- * <li><js>"foo\\,bar"</js> - Escaped comma in value</li>
- * <li><js>"foo\\{bar\\}"</js> - Escaped braces in value</li>
- * </ul>
- *
- * <h5 class='section'>State Machine:</h5>
- * <p>The parser operates in several states:</p>
- * <ul>
- * <li><b>PARSING_VALUE:</b> Reading a token value</li>
- * <li><b>PARSING_NESTED:</b> Reading nested content within braces</li>
- * <li><b>IN_ESCAPE:</b> Processing escaped character</li>
- * </ul>
- *
- * <h5 class='section'>Usage Examples:</h5>
- * <p class='bjava'>
- * <jc>// Simple tokens</jc>
- * var tokens = NestedTokenizer.splitNested(<js>"foo,bar,baz"</js>);
- * <jc>// tokens = [Token{value="foo"}, Token{value="bar"},
Token{value="baz"}]</jc>
- *
- * <jc>// Nested tokens</jc>
- * var nested =
NestedTokenizer.splitNested(<js>"user{name,email},config{timeout,retries}"</js>);
- * <jc>// nested[0] = Token{value="user", nested=[Token{value="name"},
Token{value="email"}]}</jc>
- * <jc>// nested[1] = Token{value="config",
nested=[Token{value="timeout"}, Token{value="retries"}]}</jc>
- * </p>
- */
-public class NestedTokenizer {
-
- /**
- * Parser states for the finite state machine.
- */
- enum ParseState {
- /** Parsing a token value outside of nested braces */
- PARSING_VALUE,
- /** Parsing nested content within braces */
- PARSING_NESTED,
- /** Processing an escaped character */
- IN_ESCAPE
- }
-
- public static List<Token> tokenize(String in) {
- if (in == null) throw new IllegalArgumentException("Input was
null.");
- if (in.isBlank()) throw new IllegalArgumentException("Input was
empty.");
-
- var length = in.length();
- var pos = 0;
- var state = PARSING_VALUE;
- var currentValue = new StringBuilder();
- var nestedDepth = 0;
- var nestedStart = -1;
- var tokens = new ArrayList<Token>();
- var lastWasComma = false;
- var justCompletedNested = false;
-
- while (pos < length) {
- var c = in.charAt(pos);
-
- if (state == PARSING_VALUE) {
- if (c == '\\') {
- state = IN_ESCAPE;
- } else if (c == ',') {
- var value =
currentValue.toString().trim();
- // Add token unless it's empty and we
just completed a nested token
- if (!value.isEmpty() ||
tokens.isEmpty() || !justCompletedNested) {
- tokens.add(new Token(value));
- }
- currentValue.setLength(0);
- nestedStart = -1;
- lastWasComma = true;
- justCompletedNested = false;
- pos = skipWhitespace(in, pos);
- } else if (c == '{') {
- nestedStart = pos + 1;
- nestedDepth = 1;
- state = PARSING_NESTED;
- } else {
- currentValue.append(c);
- lastWasComma = false;
- justCompletedNested = false;
- }
- } else if (state == PARSING_NESTED) {
- if (c == '\\') {
- state = IN_ESCAPE;
- } else if (c == '{') {
- nestedDepth++;
- } else if (c == '}') {
- nestedDepth--;
- if (nestedDepth == 0) {
- var value =
currentValue.toString().trim();
- var nestedContent =
in.substring(nestedStart, pos);
- var token = new Token(value);
- if
(!nestedContent.trim().isEmpty()) {
-
token.setNested(tokenize(nestedContent));
- }
- tokens.add(token);
- currentValue.setLength(0);
- nestedStart = -1;
- lastWasComma = false; // Reset
since we've completed a token
- justCompletedNested = true; //
Flag that we just completed a nested token
- pos = skipWhitespace(in, pos);
- state = PARSING_VALUE;
- }
- }
- } else if (state == IN_ESCAPE) {
- // Add the escaped character to current value
only if we're parsing the main token value
- if (nestedDepth == 0) {
- currentValue.append(c);
- }
- state = (nestedDepth > 0) ? PARSING_NESTED :
PARSING_VALUE;
- }
-
- pos++;
- }
-
- // Add final token if we have content, or if input ended with
comma, or if no tokens yet
- var finalValue = currentValue.toString().trim();
- if (!finalValue.isEmpty() || lastWasComma || tokens.isEmpty()) {
- tokens.add(new Token(finalValue));
- }
-
- return tokens;
- }
-
- private static int skipWhitespace(String input, int position) {
- var length = input.length();
- while (position + 1 < length &&
Character.isWhitespace(input.charAt(position + 1))) {
- position++;
- }
- return position;
- }
-
- /**
- * Represents a parsed token with optional nested sub-tokens.
- *
- * <p>A Token contains a string value and may have nested tokens
representing
- * the content inside braces. Tokens support deep nesting for complex
hierarchical structures.</p>
- *
- * <h5 class='section'>Structure:</h5>
- * <ul>
- * <li><b>value:</b> The main token value (part before any
braces)</li>
- * <li><b>nested:</b> Optional list of nested tokens (content
within braces)</li>
- * </ul>
- *
- * <h5 class='section'>Examples:</h5>
- * <ul>
- * <li><js>"foo"</js> → <js>Token{value="foo",
nested=null}</js></li>
- * <li><js>"foo{a,b}"</js> → <js>Token{value="foo",
nested=[Token{value="a"}, Token{value="b"}]}</js></li>
- * </ul>
- */
- public static class Token {
-
- /** The main value of this token */
- private final String value;
-
- /** Nested tokens if this token has braced content, null
otherwise */
- private List<Token> nested;
-
- /**
- * Creates a new token with the specified value.
- *
- * @param value The token value
- */
- public Token(String value) {
- this.value = value != null ? value : "";
- }
-
- /**
- * Returns the main value of this token.
- *
- * @return The token value
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Returns true if this token has nested content.
- *
- * @return true if nested tokens exist
- */
- public boolean hasNested() {
- return nested != null && !nested.isEmpty();
- }
-
- /**
- * Returns an unmodifiable view of the nested tokens.
- *
- * @return unmodifiable list of nested tokens, or empty list if
none
- */
- public List<Token> getNested() {
- return nested != null ? unmodifiableList(nested) :
emptyList();
- }
-
- /**
- * Sets the nested tokens for this token (package-private for
tokenizer use).
- *
- * @param nested The list of nested tokens
- */
- void setNested(List<Token> nested) {
- this.nested = nested;
- }
-
- @Override
- public String toString() {
- return hasNested() ?
nested.stream().map(Object::toString).collect(joining(",",value + "{","}")) :
value;
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof Token o2) && eq(this, o2,
(x,y)->x.value.equals(y.value) && eq(x.nested, y.nested));
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(value, nested);
- }
- }
-
-
//---------------------------------------------------------------------------------------------
- // Helper methods.
-
//---------------------------------------------------------------------------------------------
-
- private static <T,U> boolean eq(T o1, U o2, BiPredicate<T,U> test) {
- if (o1 == null) { return o2 == null; }
- if (o2 == null) { return false; }
- if (o1 == o2) { return true; }
- return test.test(o1, o2);
- }
-
- @SuppressWarnings("unlikely-arg-type")
- private static <T,U> boolean eq(T o1, U o2) {
- return Objects.equals(o1, o2);
- }
-}