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 f9dacc6744 Unit tests
f9dacc6744 is described below

commit f9dacc6744de4c48aa27b3df0b0fbfcc1c1947b7
Author: James Bognar <[email protected]>
AuthorDate: Mon Dec 1 18:11:34 2025 -0800

    Unit tests
---
 TODO-completed.md                                  |   4 +
 TODO.md                                            |   2 +-
 .../apache/juneau/common/reflect/ClassInfo.java    |  11 +-
 .../juneau/common/reflect/ClassInfoTyped.java      |  48 +++++++
 .../juneau/common/reflect/ExecutableInfo.java      |   2 +-
 .../apache/juneau/common/utils/StringUtils.java    |  12 +-
 .../juneau/common/utils/StringUtils_Test.java      | 149 ++++++++++-----------
 .../apache/juneau/common/utils/Version_Test.java   |   1 +
 8 files changed, 141 insertions(+), 88 deletions(-)

diff --git a/TODO-completed.md b/TODO-completed.md
index 5f004164bb..d4b489bb0d 100644
--- a/TODO-completed.md
+++ b/TODO-completed.md
@@ -13,6 +13,10 @@ This file contains TODO items that have been completed and 
moved from TODO.md.
   - **Status**: COMPLETED
   - **Details**: Removed the legacy `ParamInfoTest`, replacing it with 
`ParameterInfoTest`, updated all test references (including variable 
names/comments) to use `ParameterInfo`, and ensured the active codebase no 
longer mentions `ParamInfo` outside of archived static Javadocs from prior 
releases.
   
+- **TODO-89** ✅ Add `ClassInfoTyped`.
+  - **Status**: COMPLETED
+  - **Details**: Introduced `ClassInfoTyped<T>` as a generic subclass of 
`ClassInfo` and updated `ClassInfo.of(Class)` to return this typed variant 
(while still caching instances). This keeps the API identical but allows 
callers to benefit from compile-time typing without changing existing code.
+  
 - **TODO-88** ✅ Eliminate need for AssertionArgs in BctAssertions by allowing 
DEFAULT_CONVERTER to be overridden and resettable.
   - **Status**: COMPLETED
   - **Details**: Introduced a thread-local resettable converter supplier 
(defaulting to `BasicBeanConverter.DEFAULT`), added 
`setConverter()`/`resetConverter()` APIs, removed the `AssertionArgs` class, 
updated all assertion signatures to take a leading `Supplier<String>` for 
custom messages, refreshed docs/javadocs, and migrated the BCT tests to the new 
API.
diff --git a/TODO.md b/TODO.md
index b5d3c79845..0c9bc4f0af 100644
--- a/TODO.md
+++ b/TODO.md
@@ -23,7 +23,7 @@ This file tracks pending tasks for the Apache Juneau project. 
For completed item
 
 - [x] TODO-19 ClassInfo improvements to getMethod (e.g. getMethodExact vs 
getMethod).
 - [ ] TODO-21 Thrown NotFound causes - javax.servlet.ServletException: Invalid 
method response: 200
-- [ ] TODO-89 Add ClassInfoTyped
+- [x] TODO-89 Add ClassInfoTyped
 
 ## HTTP Response/Exception Improvements
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
index d6da9743a8..9c35a6b5ab 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
@@ -119,11 +119,12 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
        /**
         * Returns a class info wrapper around the specified class type.
         *
+        * @param <T> The class type.
         * @param inner The class type.
         * @return The constructed class info.
         */
-       public static ClassInfo of(Class<?> inner) {
-               return CACHE.get(inner, () -> new ClassInfo(inner, inner));
+       public static <T> ClassInfoTyped<T> of(Class<T> inner) {
+               return (ClassInfoTyped<T>)CACHE.get(inner, () -> new 
ClassInfoTyped<>(inner));
        }
 
        /**
@@ -136,7 +137,9 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
        public static ClassInfo of(Class<?> inner, Type innerType) {
                if (inner == innerType)
                        return of(inner);
-               return new ClassInfo(inner, innerType);
+               if (inner != null)
+                       return new ClassInfoTyped<>(inner, innerType);
+               return new ClassInfo(null, innerType);
        }
 
        /**
@@ -229,7 +232,7 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
                this.fullName = memoize(() -> getNameFormatted(FULL, true, '$', 
BRACKETS));
                this.shortName = memoize(() -> getNameFormatted(SHORT, true, 
'$', BRACKETS));
                this.readableName = memoize(() -> getNameFormatted(SIMPLE, 
false, '$', WORD));
-               this.declaredInterfaces = memoize(() -> opt(inner).map(x -> 
stream(x.getInterfaces()).map(ClassInfo::of).toList()).orElse(liste()));
+               this.declaredInterfaces = memoize(() -> opt(inner).map(x -> 
stream(x.getInterfaces()).map(ClassInfo::of).map(ClassInfo.class::cast).toList()).orElse(liste()));
                this.interfaces = memoize(() -> getParents().stream().flatMap(x 
-> x.getDeclaredInterfaces().stream()).flatMap(ci2 -> concat(Stream.of(ci2), 
ci2.getInterfaces().stream())).distinct().toList());
                this.allParents = memoize(() -> concat(getParents().stream(), 
getInterfaces().stream()).toList());
                this.parentsAndInterfaces = 
memoize(this::findParentsAndInterfaces);
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
new file mode 100644
index 0000000000..fe5994b648
--- /dev/null
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.common.reflect;
+
+import java.lang.reflect.Type;
+
+/**
+ * Generic, typed variant of {@link ClassInfo}.
+ *
+ * @param <T> The raw class type this instance represents.
+ */
+public class ClassInfoTyped<T> extends ClassInfo {
+
+       /**
+        * Constructor.
+        *
+        * @param inner The class type.
+        */
+       protected ClassInfoTyped(Class<T> inner) {
+               super(inner, inner);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param inner The class type.
+        * @param innerType The generic type (if parameterized type).
+        */
+       protected ClassInfoTyped(Class<T> inner, Type innerType) {
+               super(inner, innerType);
+       }
+
+}
+
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
index ec803a9b80..f49d970b05 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
@@ -109,7 +109,7 @@ public abstract class ExecutableInfo extends AccessibleInfo 
{
                this.inner = inner;
                this.isConstructor = inner instanceof Constructor;
                this.parameters = memoize(this::findParameters);
-               this.exceptions = memoize(() -> 
stream(inner.getExceptionTypes()).map(ClassInfo::of).toList());
+               this.exceptions = memoize(() -> 
stream(inner.getExceptionTypes()).map(ClassInfo::of).map(ClassInfo.class::cast).toList());
                this.declaredAnnotations = memoize(() -> 
stream(inner.getDeclaredAnnotations()).flatMap(a -> 
AnnotationUtils.streamRepeated(a)).map(a -> ai((Annotatable)this, a)).toList());
                this.shortName = memoize(() -> f("{0}({1})", getSimpleName(), 
getParameters().stream().map(p -> 
p.getParameterType().getNameSimple()).collect(joining(","))));
                this.fullName = memoize(this::findFullName);
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
index d782a80ad3..09060bd783 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
@@ -3708,7 +3708,7 @@ public class StringUtils {
                        sdf.setLenient(false); // Strict parsing
                        sdf.parse(dateStr);
                        return true;
-               } catch (ParseException | IllegalArgumentException e) {
+               } catch (@SuppressWarnings("unused") ParseException | 
IllegalArgumentException e) {
                        // IllegalArgumentException thrown for invalid format 
patterns
                        return false;
                }
@@ -3817,7 +3817,7 @@ public class StringUtils {
                                return isValidIPv6Address(ip);
                        }
                        return false;
-               } catch (NumberFormatException e) {
+               } catch (@SuppressWarnings("unused") NumberFormatException e) {
                        return false;
                }
        }
@@ -3862,7 +3862,7 @@ public class StringUtils {
                                        var num = Integer.parseInt(part);
                                        if (num < 0 || num > 255)
                                                return false;
-                               } catch (NumberFormatException e) {
+                               } catch (@SuppressWarnings("unused") 
NumberFormatException e) {
                                        return false;
                                }
                        }
@@ -3985,7 +3985,7 @@ public class StringUtils {
                try {
                        Pattern.compile(regex);
                        return true;
-               } catch (PatternSyntaxException e) {
+               } catch (@SuppressWarnings("unused") PatternSyntaxException e) {
                        return false;
                }
        }
@@ -4015,7 +4015,7 @@ public class StringUtils {
                        sdf.setLenient(false); // Strict parsing
                        sdf.parse(timeStr);
                        return true;
-               } catch (ParseException | IllegalArgumentException e) {
+               } catch (@SuppressWarnings("unused") ParseException | 
IllegalArgumentException e) {
                        // IllegalArgumentException thrown for invalid format 
patterns
                        return false;
                }
@@ -5540,7 +5540,7 @@ public class StringUtils {
                var base = Long.decode(baseStr);  // NOSONAR - NPE not possible 
here.
                try {
                        return Math.multiplyExact(base, m);
-               } catch (ArithmeticException e) {
+               } catch (@SuppressWarnings("unused") ArithmeticException e) {
                        throw new NumberFormatException("Value " + s + " 
exceeds Long.MAX_VALUE");
                }
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/StringUtils_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/StringUtils_Test.java
index 41aef636f8..a9670f385c 100755
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/StringUtils_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/StringUtils_Test.java
@@ -23,7 +23,6 @@ import static org.apache.juneau.common.utils.StringUtils.*;
 import static org.apache.juneau.common.utils.StringUtils.compare;
 import static org.apache.juneau.common.utils.StringUtils.contains;
 import static org.apache.juneau.common.utils.StringUtils.reverse;
-import static org.apache.juneau.common.utils.Utils.eqic;
 import static org.apache.juneau.junit.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -33,7 +32,6 @@ import java.util.concurrent.atomic.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.collections.*;
 import org.junit.jupiter.api.*;
 
 class StringUtils_Test extends TestBase {
@@ -384,10 +382,10 @@ class StringUtils_Test extends TestBase {
                assertEquals("!!!", camelCase("!!!"));
                assertEquals("@#$", camelCase("@#$"));
                assertEquals(".,;:", camelCase(".,;:"));
-               
+
                // Test line 8424 - splitWords with null or empty string
                // This is already covered by the null/empty tests above
-               
+
                // Test line 8458 - splitWords Case 2: uppercase after 
uppercase when next is lowercase
                // This handles cases where we have 2+ consecutive uppercase, 
then an uppercase
                // followed by lowercase (which starts a new word)
@@ -397,7 +395,7 @@ class StringUtils_Test extends TestBase {
                // The consecutiveUpperCount is checked AFTER appending, so the 
logic is complex
                var result1 = camelCase("ABCDe");
                assertEquals("aBCDe", result1); // A + BCDe (actual behavior)
-               
+
                // Test lines 8475-8478 - splitWords Case 3: lowercase after 2+ 
consecutive uppercase
                // Split all but the last uppercase (e.g., "XMLH" → "XML" + "H")
                // "XMLHt" - X, M, L are consecutive uppercase (count=3), then 
H (uppercase), then t (lowercase)
@@ -405,7 +403,7 @@ class StringUtils_Test extends TestBase {
                // The actual behavior is "xMLHt" because the split logic works 
differently
                // The consecutiveUpperCount is checked AFTER appending, so the 
logic is complex
                assertEquals("xMLHt", camelCase("XMLHt")); // X + MLHt (actual 
behavior)
-               
+
                // Test with "XMLHttp" - actual behavior is "xMLHttp"
                // The split logic for "XMLHttp" results in "X" + "MLHttp" = 
"xMLHttp"
                assertEquals("xMLHttp", camelCase("XMLHttp")); // X + MLHttp 
(actual behavior)
@@ -974,9 +972,9 @@ class StringUtils_Test extends TestBase {
                assertFalse(equalsIgnoreCase((Object)"Hello", (Object)"World"));
 
                // Non-string objects (toString() is called)
-               assertTrue(equalsIgnoreCase((Object)123, (Object)"123"));
-               assertTrue(equalsIgnoreCase((Object)"123", (Object)123));
-               assertFalse(equalsIgnoreCase((Object)123, (Object)"456"));
+               assertTrue(equalsIgnoreCase(123, "123"));
+               assertTrue(equalsIgnoreCase("123", 123));
+               assertFalse(equalsIgnoreCase(123, "456"));
 
                // Custom object with toString()
                var obj1 = new Object() {
@@ -988,7 +986,7 @@ class StringUtils_Test extends TestBase {
                        public String toString() { return "test"; }
                };
                assertTrue(equalsIgnoreCase(obj1, obj2));
-               assertTrue(equalsIgnoreCase(obj1, (Object)"TEST"));
+               assertTrue(equalsIgnoreCase(obj1, "TEST"));
        }
 
        
//====================================================================================================
@@ -1707,35 +1705,35 @@ class StringUtils_Test extends TestBase {
                assertEquals(10 * w, getDuration("10week"));
                assertEquals(10 * w, getDuration("10 weeks"));
                assertEquals(10 * w, getDuration("10W"));
-               
+
                // Test lines 8344, 8348, 8351, 8354, 8364, 8368, 8374 - 
parseUnit method
                // Line 8344 - seconds (startsWith("sec") || 
startsWith("second"))
                assertEquals(5 * s, getDuration("5sec"));
                assertEquals(5 * s, getDuration("5second"));
                assertEquals(5 * s, getDuration("5seconds"));
-               
+
                // Line 8348 - minutes (startsWith("m") && !startsWith("mo") && 
!startsWith("mill") && !startsWith("ms"))
                // Line 8351 - minutes (startsWith("min") || 
startsWith("minute"))
                assertEquals(5 * m, getDuration("5m"));
                assertEquals(5 * m, getDuration("5min"));
                assertEquals(5 * m, getDuration("5minute"));
                assertEquals(5 * m, getDuration("5minutes"));
-               
+
                // Line 8354 - hours (startsWith("h") || startsWith("hour"))
                assertEquals(5 * h, getDuration("5h"));
                assertEquals(5 * h, getDuration("5hour"));
                assertEquals(5 * h, getDuration("5hours"));
-               
+
                // Line 8364 - weeks (startsWith("w") || startsWith("week"))
                assertEquals(2 * w, getDuration("2w"));
                assertEquals(2 * w, getDuration("2week"));
                assertEquals(2 * w, getDuration("2weeks"));
-               
+
                // Line 8368 - months (startsWith("mo") || startsWith("month"))
                assertEquals(3 * mo, getDuration("3mo"));
                assertEquals(3 * mo, getDuration("3month"));
                assertEquals(3 * mo, getDuration("3months"));
-               
+
                // Line 8374 - years (startsWith("y") || startsWith("year"))
                assertEquals(2 * y, getDuration("2y"));
                assertEquals(2 * y, getDuration("2year"));
@@ -4490,7 +4488,7 @@ class StringUtils_Test extends TestBase {
                // Test Byte type - triggers line 5436
                assertEquals((byte)123, parseNumber("123", Byte.class));
                assertEquals((byte)123, parseNumber("123", Byte.TYPE));
-               
+
                // Test line 8275 - multiplierInt (empty string returns 'z' 
which is not G/M/K/g/m/k)
                // Test line 8299 - multiplierLong (empty string returns 1)
                // These are tested indirectly through parseNumber with 
multiplier suffixes
@@ -4738,7 +4736,7 @@ class StringUtils_Test extends TestBase {
                assertEquals(0.0, readabilityScore("!!!"), 0.0001); // No words 
extracted
                assertEquals(0.0, readabilityScore("..."), 0.0001); // No words 
extracted
                assertEquals(0.0, readabilityScore("   "), 0.0001); // Only 
whitespace
-               
+
                // Test line 8191 - estimateSyllables with null or empty word 
(returns 1)
                // This is tested indirectly through readabilityScore with 
single-letter words
                var scoreSingle = readabilityScore("a");
@@ -4819,9 +4817,8 @@ class StringUtils_Test extends TestBase {
 
                // Test File - triggers line 5856
                var file = new File("test.txt");
-               var fileStr = readable(file);
-               // May throw exception or return content, depends on file 
existence
-               // Just verify it doesn't crash
+               assertDoesNotThrow(() -> readable(file));
+               // May throw exception or return content depending on file 
existence; just verify it doesn't crash.
 
                // Test byte[] - triggers line 5858
                var bytes = new byte[]{0x48, 0x65, 0x6C, 0x6C, 0x6F}; // 
"Hello" in hex
@@ -4998,7 +4995,7 @@ class StringUtils_Test extends TestBase {
                assertEquals("hello", removeAll("hello", "x", "y", "z"));
                assertEquals("", removeAll("abc", "a", "b", "c"));
                assertEquals("hello", removeAll("hello", null, "x"));
-               
+
                // Test with empty string - triggers line 6102
                assertEquals("", removeAll("", "x", "y"));
                assertEquals("", removeAll("", new String[0])); // empty remove 
array
@@ -5149,7 +5146,7 @@ class StringUtils_Test extends TestBase {
                // Similar strings
                // "hello" vs "hallo": distance = 1, maxLen = 5, similarity = 1 
- 1/5 = 0.8
                assertEquals(0.8, similarity("hello", "hallo"), 0.01);
-               
+
                // Test line 6367 - maxLen == 0 (both empty after null handling)
                // Note: This line appears unreachable since empty strings are 
equal and return at line 6363
                // But testing anyway to confirm behavior
@@ -5177,7 +5174,7 @@ class StringUtils_Test extends TestBase {
                assertEquals("test", snakeCase("test"));
                assertEquals("test", snakeCase("TEST"));
                assertEquals("hello_123_world", snakeCase("hello 123 world"));
-               
+
                // Test with empty words list - triggers line 6406
                // splitWords returns empty list for strings with only 
separators (spaces, tabs, underscores, hyphens)
                assertEquals("", snakeCase("   ")); // Only spaces
@@ -5241,73 +5238,73 @@ class StringUtils_Test extends TestBase {
                assertNotNull(code3);
                assertEquals(4, code3.length());
                assertTrue(code3.startsWith("A"));
-               
+
                // Test lines 8244-8258 - getSoundexCode for all character types
                // Test all soundex code mappings
                var code4 = soundex("BFPV"); // Code 1
                assertNotNull(code4);
                assertTrue(code4.contains("1"));
-               
+
                var code5 = soundex("CGJKQSXZ"); // Code 2
                assertNotNull(code5);
                assertTrue(code5.contains("2"));
-               
+
                var code6 = soundex("DT"); // Code 3
                assertNotNull(code6);
                assertTrue(code6.contains("3"));
-               
+
                var code7 = soundex("L"); // Code 4
                assertNotNull(code7);
                // Single character "L" will be "L000" (padded), code 4 is only 
for subsequent L's
                assertEquals("L000", code7);
-               
+
                // Test with a string that has L after the first character to 
get code 4
                var code7b = soundex("AL"); // A + L(4) = A400
                assertNotNull(code7b);
                assertTrue(code7b.contains("4"));
-               
+
                var code8 = soundex("MN"); // Code 5
                assertNotNull(code8);
                assertTrue(code8.contains("5"));
-               
+
                var code9 = soundex("R"); // Code 6
                assertNotNull(code9);
                // Single character "R" will be "R000" (padded), code 6 is only 
for subsequent R's
                assertEquals("R000", code9);
-               
+
                // Test with a string that has R after the first character to 
get code 6
                var code9b = soundex("AR"); // A + R(6) = A600
                assertNotNull(code9b);
                assertTrue(code9b.contains("6"));
-               
+
                var code10 = soundex("AEIOUHWY"); // Code 0 (vowels/H/W/Y)
                assertNotNull(code10);
                // Vowels/H/W/Y don't add codes but don't break sequences
-               
+
                // Test line 8258 - non-letter characters return '0'
                var code11 = soundex("A123");
                assertNotNull(code11);
                assertTrue(code11.startsWith("A"));
-               
+
                // Test line 6499 - loop continues (i < upper.length() && 
result.length() < 4)
                // Test line 6507 - code != lastCode (different codes)
                // Test line 6515 - result.length() < 4 (need to pad with zeros)
                // String that produces less than 4 codes (needs padding)
                var code12 = soundex("A"); // Only one character, needs 3 zeros
                assertEquals("A000", code12);
-               
+
                // String with H/W/Y/vowels that don't produce codes but don't 
break sequences
                var code13 = soundex("AH"); // A + H (H is 0, doesn't add code 
but doesn't break)
                assertEquals("A000", code13); // Still needs padding
-               
+
                // String that produces exactly 3 codes (needs 1 zero)
                var code14 = soundex("ABC"); // A + B(1) + C(2) = A12, needs 
one zero
                assertEquals("A120", code14);
-               
+
                // String with different codes (code != lastCode) - triggers 
line 6507
                var code15 = soundex("ABCD"); // A + B(1) + C(2) + D(3) = A123 
(all different)
                assertEquals("A123", code15);
-               
+
                // String with same consecutive codes (code == lastCode, should 
skip)
                var code16 = soundex("ABBC"); // A + B(1) + B(1, same) + C(2) = 
A12 (B skipped)
                assertEquals("A120", code16);
@@ -5358,22 +5355,22 @@ class StringUtils_Test extends TestBase {
                var list3 = new ArrayList<String>();
                split("1,2", list3::add);
                assertEquals(List.of("1", "2"), list3);
-               
+
                // Test line 6572 - s.indexOf(c) == -1 (no split character 
found)
                var list4 = new ArrayList<String>();
                split("no-commas-here", ',', list4::add);
                assertEquals(List.of("no-commas-here"), list4);
-               
+
                // Test line 6581 - s.charAt(i) == '\\' (escape character)
                // Test line 6588 - s.charAt(i) != '\\' (reset escapeCount)
                var list5 = new ArrayList<String>();
                split("a\\,b,c", ',', list5::add);
                assertEquals(List.of("a,b", "c"), list5); // Escaped comma 
doesn't split
-               
+
                var list6 = new ArrayList<String>();
                split("a\\\\,b", ',', list6::add);
                assertEquals(List.of("a\\", "b"), list6); // Double backslash, 
second one escapes comma
-               
+
                var list7 = new ArrayList<String>();
                split("a\\b,c", ',', list7::add);
                assertEquals(List.of("a\\b", "c"), list7); // Backslash not 
before comma, escapeCount resets
@@ -5458,17 +5455,17 @@ class StringUtils_Test extends TestBase {
                assertString("{a=,b=1}", splitMap("a,b=1", true));
                assertString("{a==1}", splitMap("a\\==1", true));
                assertString("{a\\=1}", splitMap("a\\\\=1", true));
-               
+
                // Test line 6737 - null input returns null
                assertNull(splitMap(null, true));
-               
+
                // Test line 6739 - empty string returns empty map
                assertTrue(splitMap("", true).isEmpty());
-               
+
                // Test line 6767 - trim when key has no value (comma found in 
state S1)
                assertString("{key=}", splitMap(" key ", true)); // " key " 
should be trimmed, no value
                assertString("{ key =}", splitMap(" key ", false)); // No trim, 
no value
-               
+
                // Test line 6774 - state S2 handling (equals found, looking 
for delimiter)
                assertString("{a=1,b=2}", splitMap("a=1,b=2", true)); // Comma 
in state S2
                assertString("{a=1}", splitMap("a=1", true)); // End of string 
in state S2
@@ -5498,7 +5495,7 @@ class StringUtils_Test extends TestBase {
                assertEquals(3, args3.length);
                assertEquals("x", args3[0]);
                assertEquals("y<a<b,c>,d<e,f>>", args3[1]);
-               
+
                // Test line 6807 - no comma found, return array with single 
element
                var args4 = splitMethodArgs("singleArg");
                assertEquals(1, args4.length);
@@ -5537,7 +5534,7 @@ class StringUtils_Test extends TestBase {
                // Null/empty input
                assertNull(splitNested(null));
                assertTrue(splitNested("").isEmpty());
-               
+
                // Test lines 6866-6873 - escape handling
                // Line 6867: c == '\\' when inEscape is true (double backslash)
                // When inEscape is true and we see '\', we set inEscape = 
false (double backslash = literal backslash)
@@ -5545,7 +5542,7 @@ class StringUtils_Test extends TestBase {
                assertEquals(2, result4.size());
                assertEquals("a\\", result4.get(0)); // Double backslash 
becomes single literal backslash
                assertEquals("b", result4.get(1));
-               
+
                // Line 6871: c == '\\' when inEscape is false (start escape)
                // When inEscape is false and we see '\', we set inEscape = true
                // For "a\\,b,c": a, \ (inEscape=true), , (escaped, skipped, 
inEscape stays true), b (inEscape still true), , (escaped, skipped), c
@@ -5554,7 +5551,7 @@ class StringUtils_Test extends TestBase {
                var result5 = splitNested("a\\,b,c");
                assertEquals(1, result5.size()); // Escaped comma doesn't 
split, entire string is one token
                assertEquals("a,b,c", result5.get(0));
-               
+
                // Test escape sequence with nested braces - escaped brace 
doesn't affect depth
                // When inEscape is true, the '{' is skipped (escaped), so 
depth doesn't increase
                // For "a\\{b},c": a, \ (inEscape=true), { (escaped, skipped, 
depth stays 0), b, } (normal, depth becomes -1), , (depth=-1, doesn't split), c
@@ -5589,14 +5586,14 @@ class StringUtils_Test extends TestBase {
                // Null/empty input - throws exception
                assertThrows(IllegalArgumentException.class, () -> 
splitNestedInner(null));
                assertThrows(IllegalArgumentException.class, () -> 
splitNestedInner(""));
-               
+
                // Test line 6945 - Start character '{' not found
                assertThrows(IllegalArgumentException.class, () -> 
splitNestedInner("no braces here"));
-               
+
                // Test line 6947 - End character '}' not found
                assertThrows(IllegalArgumentException.class, () -> 
splitNestedInner("a{b"));
                assertThrows(IllegalArgumentException.class, () -> 
splitNestedInner("a{b{c}"));
-               
+
                // Test lines 6917-6926 - escape handling
                // Line 6920: c == '\\' when inEscape is true (double backslash)
                // When inEscape is true and we see '\', we set inEscape = 
false (double backslash = literal backslash)
@@ -5604,7 +5601,7 @@ class StringUtils_Test extends TestBase {
                assertEquals(2, result4.size());
                assertEquals("b\\", result4.get(0)); // Double backslash 
becomes single literal backslash
                assertEquals("c", result4.get(1));
-               
+
                // Line 6924: c == '\\' when inEscape is false (start escape)
                // When inEscape is false and we see '\', we set inEscape = true
                // Note: For splitNestedInner, we need valid braces, so escaped 
comma inside braces is fine
@@ -5615,7 +5612,7 @@ class StringUtils_Test extends TestBase {
                var result5 = splitNestedInner("a{b\\,c\\}");
                assertEquals(1, result5.size());
                assertEquals("b,c\\", result5.get(0)); // Escaped comma and 
closing brace (brace becomes backslash)
-               
+
                // Test escape sequence with opening brace - escaped opening 
brace doesn't affect depth
                // When inEscape is true, the '{' is skipped (escaped), so 
depth doesn't increase
                // For "a{b{c\\{d},e}}": The escaped brace causes issues with 
finding the matching closing brace
@@ -5651,18 +5648,18 @@ class StringUtils_Test extends TestBase {
                assertList(splitQuoted("\"\\\"foo\\\"\""), "\"foo\"");
                assertList(splitQuoted("'\"foo\"'"), "\"foo\"");
                assertList(splitQuoted("\"'foo'\""), "'foo'");
-               
+
                // Test lines 7014, 7017 - keepQuotes=true
                // Line 7014: Single quote with keepQuotes
                var result1 = splitQuoted("'foo'", true);
                assertEquals(1, result1.length);
                assertEquals("'foo'", result1[0]); // Quotes are kept
-               
+
                // Line 7017: Double quote with keepQuotes
                var result2 = splitQuoted("\"bar\"", true);
                assertEquals(1, result2.length);
                assertEquals("\"bar\"", result2[0]); // Quotes are kept
-               
+
                // Test lines 7024, 7025, 7028 - escape handling in quotes
                // Line 7024: Escape character in quoted string
                // Line 7025: needsUnescape when keepQuotes=false
@@ -5670,17 +5667,17 @@ class StringUtils_Test extends TestBase {
                var result3 = splitQuoted("'foo\\'bar'", false);
                assertEquals(1, result3.length);
                assertEquals("foo'bar", result3[0]); // Escaped quote is 
unescaped
-               
+
                var result4 = splitQuoted("'foo\\'bar'", true);
                assertEquals(1, result4.length);
                assertEquals("'foo\\'bar'", result4[0]); // Quotes kept, escape 
preserved
-               
+
                // Test line 7039 - state S4 (non-whitespace token ending with 
whitespace)
                var result5 = splitQuoted("foo bar");
                assertEquals(2, result5.length);
                assertEquals("foo", result5[0]);
                assertEquals("bar", result5[1]);
-               
+
                // Test line 7048 - unmatched quotes error
                assertThrows(IllegalArgumentException.class, () -> 
splitQuoted("'unmatched quote"));
                assertThrows(IllegalArgumentException.class, () -> 
splitQuoted("\"unmatched quote"));
@@ -5813,7 +5810,7 @@ class StringUtils_Test extends TestBase {
                assertEquals("", substringBetween("<>", "<", ">"));
                assertEquals("test", substringBetween("<test>", "<", ">"));
                assertEquals("foo", substringBetween("a<foo>b", "<", ">"));
-               
+
                // Test line 7214 - end marker not found after start marker
                assertNull(substringBetween("<hello", "<", ">"));
                assertNull(substringBetween("start<content", "<", ">"));
@@ -6049,7 +6046,7 @@ class StringUtils_Test extends TestBase {
                // Test with non-printable characters
                var bytes2 = new byte[] { 0, 1, 2, (byte)255 };
                var result2 = toReadableBytes(bytes2);
-               
+
                // Test line 7453 - bytes outside printable range (b2 < ' ' || 
b2 > 'z')
                var bytes3 = new byte[]{0x00, 0x1F, 0x20, 0x7A, 0x7B, 
(byte)0xFF}; // null, control char, space, 'z', '{', non-printable
                var result3 = toReadableBytes(bytes3);
@@ -6311,7 +6308,7 @@ class StringUtils_Test extends TestBase {
                assertEquals("x\\,xx", unEscapeChars("x\\\\\\,xx", escape));
                assertEquals("\\", unEscapeChars("\\", escape));
                assertEquals(",", unEscapeChars("\\,", escape));
-               
+
                // Test line 7743 - double backslash (c2 == '\\')
                assertEquals("x\\y", unEscapeChars("x\\\\y", escape)); // 
Double backslash becomes single
                assertEquals("x\\", unEscapeChars("x\\\\", escape)); // Double 
backslash at end
@@ -6408,16 +6405,16 @@ class StringUtils_Test extends TestBase {
                // No encoding needed - returns as-is
                assertEquals("Hello", urlEncode("Hello"));
                assertEquals("test123", urlEncode("test123"));
-               
+
                // Test line 7914 - characters <= 127 (ASCII)
                var result1 = urlEncode("[email protected]");
                assertTrue(result1.contains("%40")); // @ is encoded
-               
+
                // Test line 7917 - characters > 127 (UTF-8 encoding)
                var result2 = urlEncode("café");
                assertNotNull(result2);
                assertTrue(result2.contains("%")); // Contains encoded 
characters
-               
+
                var result3 = urlEncode("测试");
                assertNotNull(result3);
                assertTrue(result3.contains("%")); // Contains UTF-8 encoded 
characters
@@ -6461,12 +6458,12 @@ class StringUtils_Test extends TestBase {
                // Special characters are encoded
                var result3 = urlEncodePath("file@name");
                assertNotNull(result3);
-               
+
                // Test line 7945 - check if encoding is needed (needsEncode 
loop)
                // Test line 7948 - return early if no encoding needed
                var result4 = urlEncodePath("simplepath");
                assertEquals("simplepath", result4); // No encoding needed, 
returns as-is
-               
+
                // Test lines 7967-7971 - UTF-8 surrogate pairs (high surrogate 
0xD800-0xDBFF, low surrogate 0xDC00-0xDFFF)
                // Create a string with surrogate pairs (emoji or other high 
Unicode characters)
                // Note: The emoji might be in the valid character set, so 
let's use a character that definitely needs encoding
@@ -6478,7 +6475,7 @@ class StringUtils_Test extends TestBase {
                // If the surrogate pair is not in the valid character set, it 
should be encoded
                // Otherwise, it might be returned as-is
                assertTrue(result5.length() > 0);
-               
+
                // Test lines 7985, 7990 - uppercase hex digits (caseDiff 
applied)
                var result6 = urlEncodePath("test@file");
                assertNotNull(result6);
@@ -6553,16 +6550,16 @@ class StringUtils_Test extends TestBase {
                assertThrows(IllegalArgumentException.class, () -> wrap("test", 
0, "\n"));
                assertThrows(IllegalArgumentException.class, () -> wrap("test", 
-1, "\n"));
                assertThrows(IllegalArgumentException.class, () -> wrap("test", 
10, null));
-               
+
                // Test lines 8097-8099 - empty line handling (lineIdx < 
lines.length - 1)
                var result1 = wrap("line1\n\nline2", 10, "\n");
                assertTrue(result1.contains("\n\n")); // Empty line preserved
-               
+
                // Test line 8108 - empty word skipping (word.isEmpty())
                var result2 = wrap("word1  word2", 10, "\n"); // Multiple 
spaces create empty words
                assertTrue(result2.contains("word1"));
                assertTrue(result2.contains("word2"));
-               
+
                // Test lines 8117-8131 - word breaking when wordLength > 
wrapLength && words.length > 1
                var result3 = wrap("short verylongword here", 5, "\n");
                assertFalse(result3.contains("verylongword")); // Word should 
be split into chunks
@@ -6570,7 +6567,7 @@ class StringUtils_Test extends TestBase {
                        if (! line.isEmpty())
                                assertTrue(line.length() <= 5);
                }
-               
+
                // Test lines 8149-8162 - word breaking in else branch (when 
current line doesn't fit)
                var result4 = wrap("short word verylongword here", 10, "\n");
                assertFalse(result4.contains("verylongword")); // Word should 
be split into chunks
@@ -6578,7 +6575,7 @@ class StringUtils_Test extends TestBase {
                        if (! line.isEmpty())
                                assertTrue(line.length() <= 10);
                }
-               
+
                // Test line 8172 - append remaining line (currentLine.length() 
> 0)
                var result5 = wrap("short word", 20, "\n");
                assertEquals("short word", result5); // Remaining line appended
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/Version_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/Version_Test.java
index 58e343fce3..38b6c0c573 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/Version_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/Version_Test.java
@@ -149,6 +149,7 @@ class Version_Test extends TestBase {
                }
        }
 
+       @SuppressWarnings("unlikely-arg-type")
        @Test
        void b05_equalsObject_differentType() {
                var v1 = of("1.2.3");

Reply via email to