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 5439a6ef3c org.apache.juneau.common.reflect API improvements
5439a6ef3c is described below

commit 5439a6ef3c860740ddba5fd98685e884124ead07
Author: James Bognar <[email protected]>
AuthorDate: Tue Nov 25 11:10:02 2025 -0500

    org.apache.juneau.common.reflect API improvements
---
 TODO-StringUtils.md                                |   26 +-
 .../apache/juneau/common/utils/StringUtils.java    |  237 ++++
 .../apache/juneau/common/utils/IOUtils_Test.java   |   88 ++
 .../juneau/common/utils/ListBuilder_Test.java      |   55 -
 .../juneau/common/utils/MapBuilder_Test.java       |   69 -
 .../juneau/common/utils/SetBuilder_Test.java       |   55 -
 .../juneau/common/utils/StringUtils_Test.java      |   99 ++
 .../java/org/apache/juneau/utils/IOUtils_Test.java |  109 --
 .../org/apache/juneau/utils/StringUtils_Test.java  | 1325 --------------------
 9 files changed, 437 insertions(+), 1626 deletions(-)

diff --git a/TODO-StringUtils.md b/TODO-StringUtils.md
index 120e9737f8..8def9bcaee 100644
--- a/TODO-StringUtils.md
+++ b/TODO-StringUtils.md
@@ -3,7 +3,7 @@
 This document outlines recommended methods to add to the `StringUtils` class 
to make it a comprehensive string utility library suitable for external use.
 
 ## Current Status
-The `StringUtils` class currently has 187+ public static methods covering:
+The `StringUtils` class currently has 198+ public static methods covering:
 - Base64 encoding/decoding
 - Hex encoding/decoding  
 - URL encoding/decoding
@@ -82,23 +82,23 @@ The `StringUtils` class currently has 187+ public static 
methods covering:
 
 ### 6. String Formatting and Templates
 - ✅ `format(String template, Object... args)` - Implemented (could be enhanced)
-- [ ] `formatWithNamedArgs(String template, Map<String, Object> args)` - 
Format with named placeholders
-- [ ] `interpolate(String template, Map<String, Object> variables)` - Variable 
interpolation
-- [ ] `pluralize(String word, int count)` - Simple pluralization
-- [ ] `ordinal(int number)` - Convert number to ordinal (1st, 2nd, 3rd, etc.)
+- ✅ `formatWithNamedArgs(String template, Map<String, Object> args)` - Format 
with named placeholders - Implemented
+- ✅ `interpolate(String template, Map<String, Object> variables)` - Variable 
interpolation - Implemented
+- ✅ `pluralize(String word, int count)` - Simple pluralization - Implemented
+- ✅ `ordinal(int number)` - Convert number to ordinal (1st, 2nd, 3rd, etc.) - 
Implemented
 
 ### 7. String Cleaning and Sanitization
 - ✅ `clean(String str)` - Implemented
 - ✅ `normalizeWhitespace(String str)` - Implemented
 - ✅ `removeControlChars(String str)` - Implemented
 - ✅ `removeNonPrintable(String str)` - Implemented
-- [ ] `sanitize(String str)` - Basic HTML/XML sanitization
-- [ ] `escapeHtml(String str)` - Escape HTML entities
-- [ ] `unescapeHtml(String str)` - Unescape HTML entities
-- [ ] `escapeXml(String str)` - Escape XML entities
-- [ ] `unescapeXml(String str)` - Unescape XML entities
-- [ ] `escapeSql(String str)` - Escape SQL strings (basic)
-- [ ] `escapeRegex(String str)` - Escape regex special characters
+- ✅ `sanitize(String str)` - Basic HTML/XML sanitization - Implemented
+- ✅ `escapeHtml(String str)` - Escape HTML entities - Implemented
+- ✅ `unescapeHtml(String str)` - Unescape HTML entities - Implemented
+- ✅ `escapeXml(String str)` - Escape XML entities - Implemented
+- ✅ `unescapeXml(String str)` - Unescape XML entities - Implemented
+- ✅ `escapeSql(String str)` - Escape SQL strings (basic) - Implemented
+- ✅ `escapeRegex(String str)` - Escape regex special characters - Implemented
 
 ### 8. String Comparison and Sorting
 - [ ] `equalsIgnoreCase(String str1, String str2)` - Case-insensitive equals
@@ -197,7 +197,7 @@ The `StringUtils` class currently has 187+ public static 
methods covering:
 
 ### Medium Priority (Useful Utilities)
 1. ✅ String searching and matching methods - Completed
-2. String formatting and templates
+2. ✅ String formatting and templates - Completed
 3. String comparison and sorting methods
 4. String generation and random methods
 5. String parsing and extraction methods
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 2c64c05442..14b149a41f 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
@@ -2285,6 +2285,243 @@ public class StringUtils {
                        .replace("|", "\\|");
        }
 
+       /**
+        * Compares two strings for equality, ignoring case.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      equalsIgnoreCase(<js>"Hello"</js>, <js>"hello"</js>);     
<jc>// true</jc>
+        *      equalsIgnoreCase(<js>"Hello"</js>, <js>"WORLD"</js>);     
<jc>// false</jc>
+        *      equalsIgnoreCase(<jk>null</jk>, <jk>null</jk>);           
<jc>// true</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @return <jk>true</jk> if the strings are equal ignoring case, 
<jk>false</jk> otherwise.
+        */
+       public static boolean equalsIgnoreCase(String str1, String str2) {
+               if (str1 == str2)
+                       return true;
+               if (str1 == null || str2 == null)
+                       return false;
+               return str1.equalsIgnoreCase(str2);
+       }
+
+       /**
+        * Compares two strings lexicographically, ignoring case.
+        *
+        * <p>
+        * Returns a negative integer, zero, or a positive integer as the first 
string is less than, equal to, or greater than the second string, ignoring case.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      compareIgnoreCase(<js>"apple"</js>, <js>"BANANA"</js>);   
<jc>// negative (apple &lt; banana)</jc>
+        *      compareIgnoreCase(<js>"Hello"</js>, <js>"hello"</js>);    
<jc>// 0 (equal)</jc>
+        *      compareIgnoreCase(<js>"Zebra"</js>, <js>"apple"</js>);    
<jc>// positive (zebra &gt; apple)</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @return A negative integer, zero, or a positive integer as the first 
string is less than, equal to, or greater than the second.
+        */
+       public static int compareIgnoreCase(String str1, String str2) {
+               if (str1 == str2)
+                       return 0;
+               if (str1 == null)
+                       return -1;
+               if (str2 == null)
+                       return 1;
+               return str1.compareToIgnoreCase(str2);
+       }
+
+       /**
+        * Performs natural string comparison that handles numbers correctly.
+        *
+        * <p>
+        * Compares strings in a way that numbers are compared numerically 
rather than lexicographically.
+        * For example, "file2.txt" comes before "file10.txt" in natural order.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      naturalCompare(<js>"file2.txt"</js>, <js>"file10.txt"</js>);   
<jc>// negative (2 &lt; 10)</jc>
+        *      naturalCompare(<js>"file10.txt"</js>, <js>"file2.txt"</js>);   
<jc>// positive (10 &gt; 2)</jc>
+        *      naturalCompare(<js>"file1.txt"</js>, <js>"file1.txt"</js>);    
<jc>// 0 (equal)</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @return A negative integer, zero, or a positive integer as the first 
string is less than, equal to, or greater than the second.
+        */
+       public static int naturalCompare(String str1, String str2) {
+               if (str1 == str2)
+                       return 0;
+               if (str1 == null)
+                       return -1;
+               if (str2 == null)
+                       return 1;
+               
+               var len1 = str1.length();
+               var len2 = str2.length();
+               var i1 = 0;
+               var i2 = 0;
+               
+               while (i1 < len1 && i2 < len2) {
+                       var c1 = str1.charAt(i1);
+                       var c2 = str2.charAt(i2);
+                       
+                       // If both are digits, compare numerically
+                       if (Character.isDigit(c1) && Character.isDigit(c2)) {
+                               // Skip leading zeros
+                               while (i1 < len1 && str1.charAt(i1) == '0')
+                                       i1++;
+                               while (i2 < len2 && str2.charAt(i2) == '0')
+                                       i2++;
+                               
+                               // Find end of number sequences
+                               var end1 = i1;
+                               var end2 = i2;
+                               while (end1 < len1 && 
Character.isDigit(str1.charAt(end1)))
+                                       end1++;
+                               while (end2 < len2 && 
Character.isDigit(str2.charAt(end2)))
+                                       end2++;
+                               
+                               // Compare lengths first (longer number is 
larger)
+                               var lenNum1 = end1 - i1;
+                               var lenNum2 = end2 - i2;
+                               if (lenNum1 != lenNum2)
+                                       return lenNum1 - lenNum2;
+                               
+                               // Same length, compare digit by digit
+                               for (var j = 0; j < lenNum1; j++) {
+                                       var d1 = str1.charAt(i1 + j);
+                                       var d2 = str2.charAt(i2 + j);
+                                       if (d1 != d2)
+                                               return d1 - d2;
+                               }
+                               
+                               i1 = end1;
+                               i2 = end2;
+                       } else {
+                               // Compare characters (case-insensitive)
+                               var cmp = Character.toLowerCase(c1) - 
Character.toLowerCase(c2);
+                               if (cmp != 0)
+                                       return cmp;
+                               i1++;
+                               i2++;
+                       }
+               }
+               
+               return len1 - len2;
+       }
+
+       /**
+        * Calculates the Levenshtein distance (edit distance) between two 
strings.
+        *
+        * <p>
+        * The Levenshtein distance is the minimum number of single-character 
edits (insertions, deletions, or substitutions) required to change one string 
into another.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      levenshteinDistance(<js>"kitten"</js>, <js>"sitting"</js>);     
<jc>// 3</jc>
+        *      levenshteinDistance(<js>"hello"</js>, <js>"hello"</js>);        
<jc>// 0</jc>
+        *      levenshteinDistance(<js>"abc"</js>, <js>""</js>);               
<jc>// 3</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @return The Levenshtein distance between the two strings.
+        */
+       public static int levenshteinDistance(String str1, String str2) {
+               if (str1 == null)
+                       str1 = "";
+               if (str2 == null)
+                       str2 = "";
+               
+               var len1 = str1.length();
+               var len2 = str2.length();
+               
+               // Use dynamic programming with optimized space (only need 
previous row)
+               var prev = new int[len2 + 1];
+               var curr = new int[len2 + 1];
+               
+               // Initialize first row
+               for (var j = 0; j <= len2; j++)
+                       prev[j] = j;
+               
+               for (var i = 1; i <= len1; i++) {
+                       curr[0] = i;
+                       for (var j = 1; j <= len2; j++) {
+                               if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
+                                       curr[j] = prev[j - 1];
+                               } else {
+                                       curr[j] = 1 + 
Math.min(Math.min(prev[j], curr[j - 1]), prev[j - 1]);
+                               }
+                       }
+                       // Swap arrays
+                       var temp = prev;
+                       prev = curr;
+                       curr = temp;
+               }
+               
+               return prev[len2];
+       }
+
+       /**
+        * Calculates the similarity percentage between two strings using 
Levenshtein distance.
+        *
+        * <p>
+        * Returns a value between 0.0 (completely different) and 1.0 
(identical).
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      similarity(<js>"hello"</js>, <js>"hello"</js>);           
<jc>// 1.0 (100%)</jc>
+        *      similarity(<js>"kitten"</js>, <js>"sitting"</js>);        
<jc>// ~0.57 (57%)</jc>
+        *      similarity(<js>"abc"</js>, <js>"xyz"</js>);               
<jc>// 0.0 (0%)</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @return A similarity value between 0.0 and 1.0, where 1.0 means 
identical.
+        */
+       public static double similarity(String str1, String str2) {
+               if (str1 == null)
+                       str1 = "";
+               if (str2 == null)
+                       str2 = "";
+               
+               if (str1.equals(str2))
+                       return 1.0;
+               
+               var maxLen = Math.max(str1.length(), str2.length());
+               if (maxLen == 0)
+                       return 1.0;
+               
+               var distance = levenshteinDistance(str1, str2);
+               return 1.0 - ((double)distance / maxLen);
+       }
+
+       /**
+        * Checks if two strings are similar based on a similarity threshold.
+        *
+        * <p>
+        * Uses the {@link #similarity(String, String)} method to calculate 
similarity and compares it to the threshold.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      isSimilar(<js>"hello"</js>, <js>"hello"</js>, <js>0.8</js>);    
    <jc>// true</jc>
+        *      isSimilar(<js>"kitten"</js>, <js>"sitting"</js>, <js>0.8</js>); 
    <jc>// false</jc>
+        *      isSimilar(<js>"kitten"</js>, <js>"sitting"</js>, <js>0.5</js>); 
    <jc>// true</jc>
+        * </p>
+        *
+        * @param str1 The first string.
+        * @param str2 The second string.
+        * @param threshold The similarity threshold (0.0 to 1.0).
+        * @return <jk>true</jk> if the similarity is greater than or equal to 
the threshold, <jk>false</jk> otherwise.
+        */
+       public static boolean isSimilar(String str1, String str2, double 
threshold) {
+               return similarity(str1, str2) >= threshold;
+       }
+
        /**
         * Join the specified tokens into a delimited string.
         *
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/IOUtils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/IOUtils_Test.java
index 8d56b80c41..62d8937ef4 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/IOUtils_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/IOUtils_Test.java
@@ -18,6 +18,7 @@
 package org.apache.juneau.common.utils;
 
 import static org.apache.juneau.common.utils.IOUtils.*;
+import static org.apache.juneau.common.utils.IOUtils.UTF8;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.io.*;
@@ -32,9 +33,96 @@ import org.junit.jupiter.api.*;
  */
 class IOUtils_Test extends TestBase {
 
+       
//====================================================================================================
+       // read(Path)
+       
//====================================================================================================
        @Test void a01_readPath() throws IOException {
                var p = new Properties();
                p.load(new 
StringReader(read(Paths.get("src/test/resources/files/Test3.properties"))));
                assertEquals("files/Test3.properties", p.get("file"));
        }
+
+       
//====================================================================================================
+       // pipe(Reader, Writer)
+       
//====================================================================================================
+       @Test void b01_pipe() throws Exception {
+               var out = new TestWriter();
+               var in = new TestReader("foobar");
+
+               pipe(in, out);
+               assertTrue(in.closed);
+               assertFalse(out.closed);
+               assertEquals("foobar", out.toString());
+       }
+
+       
//====================================================================================================
+       // loadSystemResourceAsString(String, String...)
+       
//====================================================================================================
+       @Test void c01_loadSystemResourceAsString() throws Exception {
+               assertNotNull(loadSystemResourceAsString("test1.txt", "."));
+               assertNull(loadSystemResourceAsString("test2.txt", "."));
+               assertNull(loadSystemResourceAsString("test3.txt", "sub"));
+               assertNull(loadSystemResourceAsString("test3.txt", "sub2"));
+               assertNotNull(loadSystemResourceAsString("test3.txt", "."));
+               assertNotNull(loadSystemResourceAsString("test4.txt", ".", 
"sub"));
+               assertNotNull(loadSystemResourceAsString("test4.txt", "sub"));
+       }
+
+       
//====================================================================================================
+       // Test helper classes
+       
//====================================================================================================
+       public static class TestReader extends StringReader {
+               boolean closed;
+
+               public TestReader(String s) {
+                       super(s);
+               }
+
+               @Override /* Reader */
+               public void close() {
+                       closed = true;
+               }
+       }
+
+       public static class TestWriter extends StringWriter {
+               boolean closed;
+
+               public TestWriter() { /* no-op */ }
+
+               @Override /* Writer */
+               public void close() {
+                       closed = true;
+               }
+       }
+
+       public static class TestInputStream extends ByteArrayInputStream {
+               boolean closed;
+
+               public TestInputStream(String s) {
+                       super(s.getBytes());
+               }
+
+               @Override /* InputStream */
+               public void close() throws IOException {
+                       super.close();
+                       closed = true;
+               }
+       }
+
+       public static class TestOutputStream extends ByteArrayOutputStream {
+               boolean closed;
+
+               public TestOutputStream() { /* no-op */ }
+
+               @Override /* OutputStream */
+               public void close() throws IOException {
+                       super.close();
+                       closed = true;
+               }
+
+               @Override /* Overridden from Object */
+               public synchronized String toString() {
+                       return new String(this.toByteArray(), UTF8);
+               }
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/ListBuilder_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/ListBuilder_Test.java
deleted file mode 100644
index 604ab8bf42..0000000000
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/ListBuilder_Test.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.utils;
-
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.*;
-
-import org.junit.jupiter.api.*;
-
-public class ListBuilder_Test {
-
-    @Test
-    void build_basic_add_and_addAll() {
-        List<String> l = listb(String.class)
-            .add("a")
-            .add("b", "c")
-            .addAll(l("d", "e"))
-            .build();
-        assertEquals(l("a","b","c","d","e"), l);
-    }
-
-    @Test
-    void build_sparse_and_unmodifiable() {
-        assertNull(listb(String.class).sparse().build());
-        List<String> l = listb(String.class).add("a").unmodifiable().build();
-        assertThrows(UnsupportedOperationException.class, () -> l.add("b"));
-    }
-
-    @Test
-    void sorted_natural_and_custom() {
-        List<Integer> l1 = listb(Integer.class).add(3,1,2).sorted().build();
-        assertEquals(l(1,2,3), l1);
-
-        List<Integer> l2 = 
listb(Integer.class).add(3,1,2).sorted(Comparator.reverseOrder()).build();
-        assertEquals(l(3,2,1), l2);
-    }
-}
-
-
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/MapBuilder_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/MapBuilder_Test.java
deleted file mode 100644
index 569f718fe3..0000000000
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/MapBuilder_Test.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.utils;
-
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.*;
-
-import org.junit.jupiter.api.*;
-
-public class MapBuilder_Test {
-
-    @Test
-    void build_basic_add_and_addAll() {
-        Map<String,Integer> m = mapb(String.class, Integer.class)
-            .add("a",1)
-            .add("b",2)
-            .addAll(map("c",3))
-            .build();
-        LinkedHashMap<String,Integer> expected = map();
-        expected.put("a",1);
-        expected.put("b",2);
-        expected.put("c",3);
-        assertEquals(expected, m);
-    }
-
-    @Test
-    void build_sparse_and_unmodifiable() {
-        assertNull(mapb(String.class, Integer.class).sparse().build());
-        Map<String,Integer> m = mapb(String.class, 
Integer.class).add("a",1).unmodifiable().build();
-        assertThrows(UnsupportedOperationException.class, () -> m.put("b",2));
-    }
-
-    @Test
-    void sorted_natural_and_custom() {
-        Map<String,Integer> m1 = mapb(String.class, Integer.class)
-            .add("b",2).add("c",3).add("a",1)
-            .sorted()
-            .build();
-        assertEquals(new TreeMap<>(map("a",1,"b",2,"c",3)), m1);
-
-        Map<String,Integer> m2 = mapb(String.class, Integer.class)
-            .add("b",2).add("c",3).add("a",1)
-            .sorted(Comparator.reverseOrder())
-            .build();
-        TreeMap<String,Integer> expected = new 
TreeMap<>(Comparator.reverseOrder());
-        expected.put("c",3);
-        expected.put("b",2);
-        expected.put("a",1);
-        assertEquals(expected, m2);
-    }
-}
-
-
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/SetBuilder_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/SetBuilder_Test.java
deleted file mode 100644
index c83e79dd9c..0000000000
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/SetBuilder_Test.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.utils;
-
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.*;
-
-import org.junit.jupiter.api.*;
-
-public class SetBuilder_Test {
-
-    @Test
-    void build_basic_add_and_addAll() {
-        Set<String> s = setb(String.class)
-            .add("a")
-            .add("b", "c")
-            .addAll(new LinkedHashSet<>(l("d", "e")))
-            .build();
-        assertEquals(new LinkedHashSet<>(l("a","b","c","d","e")), s);
-    }
-
-    @Test
-    void build_sparse_and_unmodifiable() {
-        assertNull(setb(String.class).sparse().build());
-        Set<String> s = setb(String.class).add("a").unmodifiable().build();
-        assertThrows(UnsupportedOperationException.class, () -> s.add("b"));
-    }
-
-    @Test
-    void sorted_natural_and_custom() {
-        Set<Integer> s1 = setb(Integer.class).add(3,1,2).sorted().build();
-        assertEquals(new TreeSet<>(l(1,2,3)), s1);
-
-        Set<Integer> s2 = 
setb(Integer.class).add(3,1,2).sorted(Comparator.reverseOrder()).build();
-        assertEquals(new TreeSet<>(l(3,2,1)), s2);
-    }
-}
-
-
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 cf822513b2..0338cca701 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
@@ -1522,6 +1522,105 @@ class StringUtils_Test extends TestBase {
                assertFalse(pattern.matcher("filextxt").matches());
        }
 
+       @Test void a73_equalsIgnoreCase() {
+               assertTrue(equalsIgnoreCase("Hello", "hello"));
+               assertTrue(equalsIgnoreCase("HELLO", "hello"));
+               assertTrue(equalsIgnoreCase("Hello", "HELLO"));
+               assertTrue(equalsIgnoreCase("Hello", "Hello"));
+               assertFalse(equalsIgnoreCase("Hello", "World"));
+               assertTrue(equalsIgnoreCase(null, null));
+               assertFalse(equalsIgnoreCase(null, "test"));
+               assertFalse(equalsIgnoreCase("test", null));
+               assertTrue(equalsIgnoreCase("", ""));
+       }
+
+       @Test void a74_compareIgnoreCase() {
+               assertTrue(compareIgnoreCase("apple", "BANANA") < 0);
+               assertTrue(compareIgnoreCase("BANANA", "apple") > 0);
+               assertEquals(0, compareIgnoreCase("Hello", "hello"));
+               assertEquals(0, compareIgnoreCase("HELLO", "hello"));
+               assertTrue(compareIgnoreCase("Zebra", "apple") > 0);
+               assertTrue(compareIgnoreCase("apple", "Zebra") < 0);
+               assertEquals(0, compareIgnoreCase(null, null));
+               assertTrue(compareIgnoreCase(null, "test") < 0);
+               assertTrue(compareIgnoreCase("test", null) > 0);
+       }
+
+       @Test void a75_naturalCompare() {
+               // Numbers should be compared numerically
+               assertTrue(naturalCompare("file2.txt", "file10.txt") < 0);
+               assertTrue(naturalCompare("file10.txt", "file2.txt") > 0);
+               assertEquals(0, naturalCompare("file1.txt", "file1.txt"));
+               assertTrue(naturalCompare("file1.txt", "file2.txt") < 0);
+               assertTrue(naturalCompare("file2.txt", "file1.txt") > 0);
+               
+               // Leading zeros
+               assertTrue(naturalCompare("file02.txt", "file10.txt") < 0);
+               assertTrue(naturalCompare("file002.txt", "file10.txt") < 0);
+               
+               // Mixed alphanumeric
+               assertTrue(naturalCompare("a2b", "a10b") < 0);
+               assertTrue(naturalCompare("a10b", "a2b") > 0);
+               
+               // Same numbers, different text
+               assertTrue(naturalCompare("file1a.txt", "file1b.txt") < 0);
+               
+               // Null handling
+               assertEquals(0, naturalCompare(null, null));
+               assertTrue(naturalCompare(null, "test") < 0);
+               assertTrue(naturalCompare("test", null) > 0);
+               
+               // Case-insensitive comparison
+               assertTrue(naturalCompare("Apple", "banana") < 0);
+               assertTrue(naturalCompare("banana", "Apple") > 0);
+       }
+
+       @Test void a76_levenshteinDistance() {
+               assertEquals(0, levenshteinDistance("hello", "hello"));
+               assertEquals(3, levenshteinDistance("kitten", "sitting")); // 
kitten -> sitten (s), sitten -> sittin (i), sittin -> sitting (g)
+               assertEquals(3, levenshteinDistance("abc", ""));
+               assertEquals(3, levenshteinDistance("", "abc"));
+               assertEquals(1, levenshteinDistance("hello", "hallo")); // e -> 
a (1 substitution)
+               assertEquals(1, levenshteinDistance("hello", "helo")); // 
Remove one 'l' (1 deletion)
+               assertEquals(1, levenshteinDistance("hello", "hell")); // 
Remove 'o' (1 deletion)
+               assertEquals(1, levenshteinDistance("hello", "hellox")); // Add 
'x' (1 insertion)
+               // Null handling
+               assertEquals(0, levenshteinDistance(null, null));
+               assertEquals(5, levenshteinDistance("hello", null));
+               assertEquals(5, levenshteinDistance(null, "hello"));
+       }
+
+       @Test void a77_similarity() {
+               assertEquals(1.0, similarity("hello", "hello"), 0.0001);
+               assertEquals(0.0, similarity("abc", "xyz"), 0.0001);
+               // kitten -> sitting: distance = 3, maxLen = 7, similarity = 1 
- 3/7 = 4/7 ≈ 0.571
+               assertEquals(4.0/7.0, similarity("kitten", "sitting"), 0.01);
+               assertEquals(1.0, similarity("", ""), 0.0001);
+               assertEquals(0.0, similarity("abc", ""), 0.0001);
+               assertEquals(0.0, similarity("", "abc"), 0.0001);
+               // Null handling
+               assertEquals(1.0, similarity(null, null), 0.0001);
+               assertEquals(0.0, similarity("hello", null), 0.0001);
+               assertEquals(0.0, similarity(null, "hello"), 0.0001);
+               // Similar strings
+               // "hello" vs "hallo": distance = 1, maxLen = 5, similarity = 1 
- 1/5 = 0.8
+               assertEquals(0.8, similarity("hello", "hallo"), 0.0001);
+       }
+
+       @Test void a78_isSimilar() {
+               assertTrue(isSimilar("hello", "hello", 0.8));
+               assertTrue(isSimilar("hello", "hello", 1.0));
+               assertFalse(isSimilar("kitten", "sitting", 0.8));
+               assertTrue(isSimilar("kitten", "sitting", 0.5));
+               assertFalse(isSimilar("abc", "xyz", 0.5));
+               assertTrue(isSimilar("hello", "hallo", 0.8));
+               assertFalse(isSimilar("hello", "world", 0.8));
+               // Null handling
+               assertTrue(isSimilar(null, null, 0.8));
+               assertFalse(isSimilar("hello", null, 0.8));
+               assertFalse(isSimilar(null, "hello", 0.8));
+       }
+
        
//====================================================================================================
        // String manipulation methods
        
//====================================================================================================
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/utils/IOUtils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/utils/IOUtils_Test.java
deleted file mode 100755
index 222ec11003..0000000000
--- a/juneau-utest/src/test/java/org/apache/juneau/utils/IOUtils_Test.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.utils;
-
-import static org.apache.juneau.common.utils.IOUtils.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.io.*;
-
-import org.apache.juneau.*;
-import org.junit.jupiter.api.*;
-
-class IOUtils_Test extends TestBase {
-
-       
//====================================================================================================
-       // read(File)
-       // read(InputStream, Charset)
-       // read(InputStream)
-       // read(Reader, int, int)
-       
//====================================================================================================
-       @Test void a01_read() throws Exception {
-               var out = new TestWriter();
-               var in = new TestReader("foobar");
-
-               pipe(in, out);
-               assertTrue(in.closed);
-               assertFalse(out.closed);
-               assertEquals("foobar", out.toString());
-       }
-
-       @Test void a02_loadSystemResourceAsString() throws Exception {
-               assertNotNull(loadSystemResourceAsString("test1.txt", "."));
-               assertNull(loadSystemResourceAsString("test2.txt", "."));
-               assertNull(loadSystemResourceAsString("test3.txt", "sub"));
-               assertNull(loadSystemResourceAsString("test3.txt", "sub2"));
-               assertNotNull(loadSystemResourceAsString("test3.txt", "."));
-               assertNotNull(loadSystemResourceAsString("test4.txt", ".", 
"sub"));
-               assertNotNull(loadSystemResourceAsString("test4.txt", "sub"));
-       }
-
-       public static class TestReader extends StringReader {
-               boolean closed;
-
-               public TestReader(String s) {
-                       super(s);
-               }
-
-               @Override /* Reader */
-               public void close() {
-                       closed = true;
-               }
-       }
-
-       public static class TestWriter extends StringWriter {
-               boolean closed;
-
-               public TestWriter() { /* no-op */ }
-
-               @Override /* Writer */
-               public void close() {
-                       closed = true;
-               }
-       }
-
-       public static class TestInputStream extends ByteArrayInputStream {
-               boolean closed;
-
-               public TestInputStream(String s) {
-                       super(s.getBytes());
-               }
-
-               @Override /* InputStream */
-               public void close() throws IOException {
-                       super.close();
-                       closed = true;
-               }
-       }
-
-       public static class TestOutputStream extends ByteArrayOutputStream {
-               boolean closed;
-
-               public TestOutputStream() { /* no-op */ }
-
-               @Override /* OutputStream */
-               public void close() throws IOException {
-                       super.close();
-                       closed = true;
-               }
-
-               @Override /* Overridden from Object */
-               public synchronized String toString() {
-                       return new String(this.toByteArray(), UTF8);
-               }
-       }
-}
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/utils/StringUtils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/utils/StringUtils_Test.java
deleted file mode 100755
index a0af01483a..0000000000
--- a/juneau-utest/src/test/java/org/apache/juneau/utils/StringUtils_Test.java
+++ /dev/null
@@ -1,1325 +0,0 @@
-/*
- * 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.utils;
-
-import static org.apache.juneau.TestUtils.*;
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.apache.juneau.common.utils.StringUtils.*;
-import static org.apache.juneau.common.utils.StringUtils.reverse;
-import static org.apache.juneau.junit.bct.BctAssertions.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.common.utils.*;
-import org.junit.jupiter.api.*;
-
-class StringUtils_Test extends TestBase {
-
-       
//====================================================================================================
-       // isNumeric(String,Class)
-       // parseNumber(String,Class)
-       
//====================================================================================================
-       @Test void a01_testParser() {
-
-               // Integers
-               assertTrue(isNumeric("123"));
-               assertEquals(123, parseNumber("123", null));
-               assertEquals(123, parseNumber("123", Integer.class));
-               assertEquals((short)123, parseNumber("123", Short.class));
-               assertEquals((long)123, parseNumber("123", Long.class));
-
-               assertTrue(isNumeric("0123"));
-               assertEquals(0123, parseNumber("0123", null));
-
-               assertTrue(isNumeric("-0123"));
-               assertEquals(-0123, parseNumber("-0123", null));
-
-               // Hexadecimal
-               assertTrue(isNumeric("0x123"));
-               assertEquals(0x123, parseNumber("0x123", null));
-
-               assertTrue(isNumeric("-0x123"));
-               assertEquals(-0x123, parseNumber("-0x123", null));
-
-               assertTrue(isNumeric("0X123"));
-               assertEquals(0X123, parseNumber("0X123", null));
-
-               assertTrue(isNumeric("-0X123"));
-               assertEquals(-0X123, parseNumber("-0X123", null));
-
-               assertTrue(isNumeric("#123"));
-               assertEquals(0x123, parseNumber("#123", null));
-
-               assertTrue(isNumeric("-#123"));
-               assertEquals(-0x123, parseNumber("-#123", null));
-
-               assertFalse(isNumeric("x123"));
-               assertFalse(isNumeric("0x123x"));
-
-               // Decimal
-               assertTrue(isNumeric("0.123"));
-               assertEquals(0.123f, parseNumber("0.123", null));
-
-               assertTrue(isNumeric("-0.123"));
-               assertEquals(-0.123f, parseNumber("-0.123", null));
-
-               assertTrue(isNumeric(".123"));
-               assertEquals(.123f, parseNumber(".123", null));
-
-               assertTrue(isNumeric("-.123"));
-               assertEquals(-.123f, parseNumber("-.123", null));
-
-               assertFalse(isNumeric("0.123.4"));
-
-               assertTrue(isNumeric("0.84370821629078d"));
-               assertEquals(0.84370821629078d, 
parseNumber("0.84370821629078d", null));
-
-               assertTrue(isNumeric("84370821629078.8437d"));
-               assertEquals(84370821629078.8437d, 
parseNumber("84370821629078.8437d", null));
-
-               assertTrue(isNumeric("0.16666666666666666d"));
-               assertEquals(0.16666666666666666d, 
parseNumber("0.16666666666666666d", null));
-
-               assertTrue(isNumeric("0.16666666f"));
-               assertEquals(0.16666666f, parseNumber("0.16666666f", null));
-
-               assertTrue(isNumeric("0.16666666d"));
-               assertEquals(0.16666666f, parseNumber("0.16666666d", null));
-
-               
assertTrue(isNumeric("3.140000000000000124344978758017532527446746826171875d"));
-               assertEquals(3.14f, 
parseNumber("3.140000000000000124344978758017532527446746826171875d", null));
-
-               assertTrue(isNumeric("12345.678f"));
-               assertEquals(1.2345678e4f, parseNumber("12345.678f", null));
-
-               // Scientific notation
-               assertTrue(isNumeric("1e1"));
-               assertEquals(1e1f, parseNumber("1e1", null));
-
-               assertTrue(isNumeric("1e+1"));
-               assertEquals(1e+1f, parseNumber("1e+1", null));
-
-               assertTrue(isNumeric("1e-1"));
-               assertEquals(1e-1f, parseNumber("1e-1", null));
-
-               assertTrue(isNumeric("1.1e1"));
-               assertEquals(1.1e1f, parseNumber("1.1e1", null));
-
-               assertTrue(isNumeric("1.1e+1"));
-               assertEquals(1.1e+1f, parseNumber("1.1e+1", null));
-
-               assertTrue(isNumeric("1.1e-1"));
-               assertEquals(1.1e-1f, parseNumber("1.1e-1", null));
-
-               assertTrue(isNumeric(".1e1"));
-               assertEquals(.1e1f, parseNumber(".1e1", null));
-
-               assertTrue(isNumeric(".1e+1"));
-               assertEquals(.1e+1f, parseNumber(".1e+1", null));
-
-               assertTrue(isNumeric(".1e-1"));
-               assertEquals(.1e-1f, parseNumber(".1e-1", null));
-
-               // Hexadecimal + scientific
-               assertTrue(isNumeric("0x123e1"));
-               assertEquals(0x123e1, parseNumber("0x123e1", null));
-
-               assertThrows(NumberFormatException.class, ()->parseNumber("x", 
Number.class));
-               assertThrows(NumberFormatException.class, ()->parseNumber("x", 
null));
-               assertThrowsWithMessage(NumberFormatException.class, 
"Unsupported Number type", ()->parseNumber("x", BadNumber.class));
-       }
-
-       @SuppressWarnings("serial")
-       private abstract static class BadNumber extends Number {}
-
-       
//====================================================================================================
-       // test - Basic tests
-       
//====================================================================================================
-       @Test void a02_numberRanges() {
-               // An integer range is -2,147,483,648 to 2,147,483,647
-
-               assertFalse(isNumeric(null));
-               assertFalse(isNumeric(""));
-               assertFalse(isNumeric("x"));
-
-               var s = "-2147483648";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Integer);
-               assertEquals(-2147483648, parseNumber(s, null));
-
-               s = "2147483647";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Integer);
-               assertEquals(2147483647, parseNumber(s, null));
-
-               s = "-2147483649";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Long);
-               assertEquals(-2147483649L, parseNumber(s, null));
-
-               s = "2147483648";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Long);
-               assertEquals(2147483648L, parseNumber(s, null));
-
-               // An long range is -9,223,372,036,854,775,808 to 
+9,223,372,036,854,775,807
-
-               s = "-9223372036854775808";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Long);
-               assertEquals(-9223372036854775808L, parseNumber(s, null));
-
-               s = "9223372036854775807";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Long);
-               assertEquals(9223372036854775807L, parseNumber(s, null));
-
-               // Anything that falls outside this range should be a double.
-
-               s = "-9223372036854775809";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals(-9223372036854775808L, parseNumber(s, 
null).longValue());
-               assertEquals(-9.223372036854776E18, parseNumber(s, null));
-
-               s = "9223372036854775808";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals(9223372036854775807L, parseNumber(s, 
null).longValue());
-               assertEquals(9.223372036854776E18, parseNumber(s, null));
-
-               // Check case where string is longer than 20 characters since 
it's a different code path.
-
-               s = "-123456789012345678901";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals(-9223372036854775808L, parseNumber(s, 
null).longValue());
-               assertEquals(-1.2345678901234568E20, parseNumber(s, null));
-
-               s = "123456789012345678901";
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals(9223372036854775807L, parseNumber(s, 
null).longValue());
-               assertEquals(1.2345678901234568E20, parseNumber(s, null));
-
-               // Autodetected floating point numbers smaller than 
Float.MAX_VALUE
-               s = String.valueOf(Float.MAX_VALUE / 2);
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Float);
-               assertEquals(1.7014117E38f, parseNumber(s, null));
-
-               s = String.valueOf((-Float.MAX_VALUE) / 2);
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Float);
-               assertEquals(-1.7014117E38f, parseNumber(s, null));
-
-               // Autodetected floating point numbers larger than 
Float.MAX_VALUE
-               s = String.valueOf((double)Float.MAX_VALUE * 2);
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals("6.805646932770577E38", parseNumber(s, 
null).toString());
-
-               s = String.valueOf((double)Float.MAX_VALUE * -2);
-               assertTrue(isNumeric(s));
-               assertTrue(parseNumber(s, null) instanceof Double);
-               assertEquals("-6.805646932770577E38", parseNumber(s, 
null).toString());
-
-               s = String.valueOf("214748364x");
-               assertFalse(isNumeric(s));
-               assertThrows(NumberFormatException.class, 
()->parseNumber("214748364x", Number.class));
-
-               s = String.valueOf("2147483640x");
-               assertFalse(isNumeric(s));
-               assertThrows(NumberFormatException.class, 
()->parseNumber("2147483640x", Long.class));
-       }
-
-       
//====================================================================================================
-       // testReplaceVars
-       
//====================================================================================================
-       @Test void a03_replaceVars() throws Exception {
-               var m = 
JsonMap.ofJson("{a:'A',b:1,c:true,d:'{e}',e:'E{f}E',f:'F',g:'{a}',h:'a',i:null}");
-
-               assertEquals("xxx", replaceVars("xxx", m));
-               assertEquals("A", replaceVars("{a}", m));
-               assertEquals("AA", replaceVars("{a}{a}", m));
-               assertEquals("xAx", replaceVars("x{a}x", m));
-               assertEquals("xAxAx", replaceVars("x{a}x{a}x", m));
-               assertEquals("1", replaceVars("{b}", m));
-               assertEquals("11", replaceVars("{b}{b}", m));
-               assertEquals("x1x", replaceVars("x{b}x", m));
-               assertEquals("x1x1x", replaceVars("x{b}x{b}x", m));
-               assertEquals("true", replaceVars("{c}", m));
-               assertEquals("truetrue", replaceVars("{c}{c}", m));
-               assertEquals("xtruextruex", replaceVars("x{c}x{c}x", m));
-               assertEquals("EFE", replaceVars("{d}", m));
-               assertEquals("EFEEFE", replaceVars("{d}{d}", m));
-               assertEquals("xEFEx", replaceVars("x{d}x", m));
-               assertEquals("xEFExEFEx", replaceVars("x{d}x{d}x", m));
-               assertEquals("A", replaceVars("{g}", m));
-               assertEquals("AA", replaceVars("{g}{g}", m));
-               assertEquals("xAx", replaceVars("x{g}x", m));
-               assertEquals("xAxAx", replaceVars("x{g}x{g}x", m));
-               assertEquals("{x}", replaceVars("{x}", m));
-               assertEquals("{x}{x}", replaceVars("{x}{x}", m));
-               assertEquals("x{x}x{x}x", replaceVars("x{x}x{x}x", m));
-               assertEquals("{A}", replaceVars("{{g}}", m));
-               assertEquals("A", replaceVars("{{h}}", m));
-               assertEquals("{}", replaceVars("{{i}}", m));
-               assertEquals("{}", replaceVars("{}", m));
-       }
-
-       
//====================================================================================================
-       // isFloat(String)
-       
//====================================================================================================
-       @Test void a04_isFloat() {
-               var valid = a(
-                       "+1.0",
-                       "-1.0",
-                       ".0",
-                       "NaN",
-                       "Infinity",
-                       "1e1",
-                       "-1e-1",
-                       "+1e+1",
-                       "-1.1e-1",
-                       "+1.1e+1",
-                       "1.1f",
-                       "1.1F",
-                       "1.1d",
-                       "1.1D",
-                       "0x1.fffffffffffffp1023",
-                       "0x1.FFFFFFFFFFFFFP1023"
-               );
-               for (var s : valid)
-                       assertTrue(isFloat(s));
-
-               var invalid = a(
-                       null,
-                       "",
-                       "a",
-                       "+",
-                       "-",
-                       ".",
-                       "a",
-                       "+a",
-                       "11a"
-               );
-               for (var s : invalid)
-                       assertFalse(isFloat(s));
-       }
-
-       
//====================================================================================================
-       // isDecimal(String)
-       
//====================================================================================================
-       @Test void a05_isDecimal() {
-               var valid = a(
-                       "+1",
-                       "-1",
-                       "0x123",
-                       "0X123",
-                       "0xdef",
-                       "0XDEF",
-                       "#def",
-                       "#DEF",
-                       "0123"
-               );
-               for (var s : valid)
-                       assertTrue(isDecimal(s));
-
-               var invalid = a(
-                       null,
-                       "",
-                       "a",
-                       "+",
-                       "-",
-                       ".",
-                       "0xdeg",
-                       "0XDEG",
-                       "#deg",
-                       "#DEG",
-                       "0128",
-                       "012A"
-               );
-               for (var s : invalid)
-                       assertFalse(isDecimal(s));
-       }
-
-       
//====================================================================================================
-       // join(Object[],String)
-       // join(int[],String)
-       // join(Collection,String)
-       // join(Object[],char)
-       // join(int[],char)
-       // join(Collection,char)
-       
//====================================================================================================
-       @Test void a01_join() {
-               assertNull(StringUtils.join((Object[])null, ","));
-               assertEquals("1", StringUtils.join(a(1), ","));
-               assertEquals("1,2", StringUtils.join(a(1,2), ","));
-
-               assertNull(StringUtils.join((Collection<?>)null, ","));
-               assertEquals("1", StringUtils.join(l(a(1)), ","));
-               assertEquals("1,2", StringUtils.join(l(a(1,2)), ","));
-
-               assertNull(StringUtils.join((Object[])null, ','));
-               assertEquals("x,y,z", StringUtils.join(a("x,y","z"), ','));
-
-               assertNull(StringUtils.join((int[])null, ','));
-               assertEquals("1", StringUtils.join(ints(1), ','));
-               assertEquals("1,2", StringUtils.join(ints(1,2), ','));
-
-               assertNull(StringUtils.join((Collection<?>)null, ','));
-               assertEquals("1", StringUtils.join(l(a(1)), ','));
-               assertEquals("1,2", StringUtils.join(l(a(1,2)), ','));
-
-               assertNull(StringUtils.joine((List<?>)null, ','));
-               assertEquals("x\\,y,z", StringUtils.joine(l(a("x,y","z")), 
','));
-       }
-
-       
//====================================================================================================
-       // split(String,char)
-       
//====================================================================================================
-       @Test void a07_split() {
-               assertNull(StringUtils.splita((String)null));
-               assertEmpty(StringUtils.splita(""));
-               assertList(StringUtils.splita("1"), "1");
-               assertList(StringUtils.splita("1,2"), "1", "2");
-               assertList(StringUtils.splita("1\\,2"), "1,2");
-               assertList(StringUtils.splita("1\\\\,2"), "1\\", "2");
-               assertList(StringUtils.splita("1\\\\\\,2"), "1\\,2");
-               assertList(StringUtils.splita("1,2\\"), "1", "2\\");
-               assertList(StringUtils.splita("1,2\\\\"), "1", "2\\");
-               assertList(StringUtils.splita("1,2\\,"), "1", "2,");
-               assertList(StringUtils.splita("1,2\\\\,"), "1", "2\\", "");
-       }
-
-       @Test void a08_split2() {
-               assertEmpty(split2test(null));
-               assertString("[]", split2test(""));
-               assertString("[1]", split2test("1"));
-               assertString("[1,2]", split2test("1,2"));
-               assertList(split2test("1\\,2"), "1,2");
-               assertList(split2test("1\\\\,2"), "1\\", "2");
-               assertList(split2test("1\\\\\\,2"), "1\\,2");
-               assertList(split2test("1,2\\"), "1", "2\\");
-               assertList(split2test("1,2\\\\"), "1", "2\\");
-               assertList(split2test("1,2\\,"), "1", "2,");
-               assertList(split2test("1,2\\\\,"), "1", "2\\", "");
-       }
-
-       private static List<String> split2test(String s) {
-               var l = new ArrayList<String>();
-               StringUtils.split(s, l::add);
-               return l;
-       }
-
-       
//====================================================================================================
-       // split(String,char,int)
-       
//====================================================================================================
-       @Test void a09_splitWithLimit() {
-               assertString("[boo,and,foo]", StringUtils.splita("boo:and:foo", 
':', 10));
-               assertString("[boo,and:foo]", StringUtils.splita("boo:and:foo", 
':', 2));
-               assertString("[boo:and:foo]", StringUtils.splita("boo:and:foo", 
':', 1));
-               assertString("[boo:and:foo]", StringUtils.splita("boo:and:foo", 
':', 0));
-               assertString("[boo:and:foo]", StringUtils.splita("boo:and:foo", 
':', -1));
-               assertString("[boo,and,foo]", StringUtils.splita("boo : and : 
foo", ':', 10));
-               assertString("[boo,and : foo]", StringUtils.splita("boo : and : 
foo", ':', 2));
-       }
-
-       
//====================================================================================================
-       // nullIfEmpty(String)
-       
//====================================================================================================
-       @Test void a10_nullIfEmpty() {
-               assertNull(StringUtils.nullIfEmpty(null));
-               assertNull(StringUtils.nullIfEmpty(""));
-               assertNotNull(StringUtils.nullIfEmpty("x"));
-       }
-
-       
//====================================================================================================
-       // unescapeChars(String,char[],char)
-       
//====================================================================================================
-       @Test void a11_unescapeChars() {
-               var escape = AsciiSet.of("\\,|");
-
-               assertNull(unEscapeChars(null, escape));
-               assertEquals("xxx", unEscapeChars("xxx", escape));
-
-               assertEquals("xxx", unEscapeChars("xxx", escape));
-               assertEquals("x,xx", unEscapeChars("x\\,xx", escape));
-               assertEquals("x\\xx", unEscapeChars("x\\xx", escape));
-               assertEquals("x\\,xx", unEscapeChars("x\\\\,xx", escape));
-               assertEquals("x\\,xx", unEscapeChars("x\\\\\\,xx", escape));
-               assertEquals("\\", unEscapeChars("\\", escape));
-               assertEquals(",", unEscapeChars("\\,", escape));
-               assertEquals("|", unEscapeChars("\\|", escape));
-
-               escape = AsciiSet.of(",|");
-               assertEquals("x\\\\xx", unEscapeChars("x\\\\xx", escape));
-       }
-
-       
//====================================================================================================
-       // decodeHex(String)
-       
//====================================================================================================
-       @Test void a12_decodeHex() {
-               assertNull(decodeHex(null));
-               assertEquals("19azAZ", decodeHex("19azAZ"));
-               assertEquals("[0][1][ffff]", decodeHex("\u0000\u0001\uFFFF"));
-       }
-
-       
//====================================================================================================
-       // startsWith(String,char)
-       
//====================================================================================================
-       @Test void a13_startsWith() {
-               assertFalse(startsWith(null, 'a'));
-               assertFalse(startsWith("", 'a'));
-               assertTrue(startsWith("a", 'a'));
-               assertTrue(startsWith("ab", 'a'));
-       }
-
-       
//====================================================================================================
-       // endsWith(String,char)
-       
//====================================================================================================
-       @Test void a14_endsWith() {
-               assertFalse(endsWith(null, 'a'));
-               assertFalse(endsWith("", 'a'));
-               assertTrue(endsWith("a", 'a'));
-               assertTrue(endsWith("ba", 'a'));
-       }
-
-       
//====================================================================================================
-       // base64EncodeToString(String)
-       // base64DecodeToString(String)
-       
//====================================================================================================
-       @Test void a15_base64EncodeToString() {
-               assertNull(base64DecodeToString(base64EncodeToString(null)));
-               assertEquals("", 
base64DecodeToString(base64EncodeToString("")));
-               assertEquals("foobar", 
base64DecodeToString(base64EncodeToString("foobar")));
-               assertEquals("\u0000\uffff", 
base64DecodeToString(base64EncodeToString("\u0000\uffff")));
-               assertThrowsWithMessage(IllegalArgumentException.class, 
"Invalid BASE64 string length.  Must be multiple of 4.", ()->base64Decode("a"));
-               assertThrows(IllegalArgumentException.class, 
()->base64Decode("aaa"));
-       }
-
-       
//====================================================================================================
-       // generateUUID(String)
-       
//====================================================================================================
-       @Test void a16_generateUUID() {
-               for (var i = 0; i < 10; i++) {
-                       var s = random(i);
-                       assertEquals(i, s.length());
-                       for (var c : s.toCharArray())
-                               assertTrue(Character.isLowerCase(c) || 
Character.isDigit(c));
-               }
-       }
-
-       
//====================================================================================================
-       // trim(String)
-       
//====================================================================================================
-       @Test void a17_trim() {
-               assertNull(trim(null));
-               assertEquals("", trim(""));
-               assertEquals("", trim("  "));
-       }
-
-       
//====================================================================================================
-       // parseISO8601Date(String)
-       
//====================================================================================================
-       @Test void a18_parseISO8601Date() throws Exception {
-               assertNull(parseIsoDate(null));
-               assertNull(parseIsoDate(""));
-
-               setTimeZone("GMT");
-               try {
-                       assertString("2000-01-01T00:00:00Z", 
parseIsoDate("2000"));
-                       assertString("2000-02-01T00:00:00Z", 
parseIsoDate("2000-02"));
-                       assertString("2000-02-03T00:00:00Z", 
parseIsoDate("2000-02-03"));
-                       assertString("2000-02-03T04:00:00Z", 
parseIsoDate("2000-02-03T04"));
-                       assertString("2000-02-03T04:05:00Z", 
parseIsoDate("2000-02-03T04:05"));
-                       assertString("2000-02-03T04:05:06Z", 
parseIsoDate("2000-02-03T04:05:06"));
-                       assertString("2000-02-03T04:00:00Z", 
parseIsoDate("2000-02-03 04"));
-                       assertString("2000-02-03T04:05:00Z", 
parseIsoDate("2000-02-03 04:05"));
-                       assertString("2000-02-03T04:05:06Z", 
parseIsoDate("2000-02-03 04:05:06"));
-                       assertString("2000-02-03T04:05:06Z", 
parseIsoDate("2000-02-03 04:05:06,789"));// ISO8601 doesn't support 
milliseconds, so it gets trimmed.
-               } finally {
-                       unsetTimeZone();
-               }
-       }
-
-       
//====================================================================================================
-       // parseMap(String,char,char,boolean)
-       
//====================================================================================================
-       @Test void a19_splitMap() {
-               assertString("{a=1}", StringUtils.splitMap("a=1", true));
-               assertString("{a=1,b=2}", StringUtils.splitMap("a=1,b=2", 
true));
-               assertString("{a=1,b=2}", StringUtils.splitMap(" a = 1 , b = 2 
", true));
-               assertString("{ a = 1 , b = 2 }", StringUtils.splitMap(" a = 1 
, b = 2 ", false));
-               assertString("{a=}", StringUtils.splitMap("a", true));
-               assertString("{a=,b=}", StringUtils.splitMap("a,b", true));
-               assertString("{a=1,b=}", StringUtils.splitMap("a=1,b", true));
-               assertString("{a=,b=1}", StringUtils.splitMap("a,b=1", true));
-               assertString("{a==1}", StringUtils.splitMap("a\\==1", true));
-               assertString("{a\\=1}", StringUtils.splitMap("a\\\\=1", true));
-       }
-
-       
//====================================================================================================
-       // isAbsoluteUri(String)
-       
//====================================================================================================
-       @Test void a10_isAbsoluteUri() {
-               assertFalse(isAbsoluteUri(null));
-               assertFalse(isAbsoluteUri(""));
-               assertTrue(isAbsoluteUri("http://foo";));
-               assertTrue(isAbsoluteUri("x://x"));
-               assertFalse(isAbsoluteUri("xX://x"));
-               assertFalse(isAbsoluteUri("x ://x"));
-               assertFalse(isAbsoluteUri("x: //x"));
-               assertFalse(isAbsoluteUri("x:/ /x"));
-               assertFalse(isAbsoluteUri("x:x//x"));
-               assertFalse(isAbsoluteUri("x:/x/x"));
-       }
-
-       
//====================================================================================================
-       // getAuthorityUri(String)
-       
//====================================================================================================
-       @Test void a21_getAuthorityUri() {
-               assertEquals("http://foo";, getAuthorityUri("http://foo";));
-               assertEquals("http://foo:123";, 
getAuthorityUri("http://foo:123";));
-               assertEquals("http://foo:123";, 
getAuthorityUri("http://foo:123/";));
-               assertEquals("http://foo:123";, 
getAuthorityUri("http://foo:123/bar";));
-       }
-
-       
//====================================================================================================
-       // splitQuoted(String)
-       
//====================================================================================================
-       @Test void a22_splitQuoted() {
-               assertNull(StringUtils.splitQuoted(null));
-               assertEmpty(StringUtils.splitQuoted(""));
-               assertEmpty(StringUtils.splitQuoted(" \t "));
-               assertList(StringUtils.splitQuoted("foo"), "foo");
-               assertList(StringUtils.splitQuoted("foo  bar baz"), "foo", 
"bar", "baz");
-               assertList(StringUtils.splitQuoted("'foo'"), "foo");
-               assertList(StringUtils.splitQuoted(" ' foo ' "), " foo ");
-               assertList(StringUtils.splitQuoted("'foo' 'bar'"), "foo", 
"bar");
-               assertList(StringUtils.splitQuoted("\"foo\""), "foo");
-               assertList(StringUtils.splitQuoted(" \" foo \" "), " foo ");
-               assertList(StringUtils.splitQuoted("\"foo\" \"bar\""), "foo", 
"bar");
-               assertList(StringUtils.splitQuoted("'foo\\'bar'"), "foo'bar");
-               assertList(StringUtils.splitQuoted("'foo\\\"bar'"), "foo\"bar");
-               assertList(StringUtils.splitQuoted("'\\'foo\\'bar\\''"), 
"'foo'bar'");
-               assertList(StringUtils.splitQuoted("'\\\"foo\\\"bar\\\"'"), 
"\"foo\"bar\"");
-               assertList(StringUtils.splitQuoted("'\\'foo\\''"), "'foo'");
-               assertList(StringUtils.splitQuoted("\"\\\"foo\\\"\""), 
"\"foo\"");
-               assertList(StringUtils.splitQuoted("'\"foo\"'"), "\"foo\"");
-               assertList(StringUtils.splitQuoted("\"'foo'\""), "'foo'");
-       }
-
-       
//====================================================================================================
-       // firstNonWhitespaceChar(String)
-       
//====================================================================================================
-       @Test void a23_firstNonWhitespaceChar() {
-               assertEquals('f', firstNonWhitespaceChar("foo"));
-               assertEquals('f', firstNonWhitespaceChar(" foo"));
-               assertEquals('f', firstNonWhitespaceChar("\tfoo"));
-               assertEquals(0, firstNonWhitespaceChar(""));
-               assertEquals(0, firstNonWhitespaceChar(" "));
-               assertEquals(0, firstNonWhitespaceChar("\t"));
-               assertEquals(0, firstNonWhitespaceChar(null));
-       }
-
-       
//====================================================================================================
-       // lastNonWhitespaceChar(String)
-       
//====================================================================================================
-       @Test void a24_lastNonWhitespaceChar() {
-               assertEquals('r', lastNonWhitespaceChar("bar"));
-               assertEquals('r', lastNonWhitespaceChar(" bar "));
-               assertEquals('r', lastNonWhitespaceChar("\tbar\t"));
-               assertEquals(0, lastNonWhitespaceChar(""));
-               assertEquals(0, lastNonWhitespaceChar(" "));
-               assertEquals(0, lastNonWhitespaceChar("\t"));
-               assertEquals(0, lastNonWhitespaceChar(null));
-       }
-
-       
//====================================================================================================
-       // testIsJsonObject(Object)
-       
//====================================================================================================
-       @Test void a25_isJsonObject() {
-               assertTrue(isJsonObject("{foo:'bar'}", true));
-               assertTrue(isJsonObject(" { foo:'bar' } ", true));
-               assertFalse(isJsonObject(" { foo:'bar'  ", true));
-               assertFalse(isJsonObject("  foo:'bar' } ", true));
-               assertTrue(isJsonObject("/*foo*/ { foo:'bar' } /*foo*/", true));
-       }
-
-       
//====================================================================================================
-       // isJsonArray(Object)
-       
//====================================================================================================
-       @Test void a26_isJsonArray() {
-               assertTrue(isJsonArray("[123,'bar']", true));
-               assertTrue(isJsonArray(" [ 123,'bar' ] ", true));
-               assertFalse(isJsonArray(" [ 123,'bar'  ", true));
-               assertFalse(isJsonArray("  123,'bar' ] ", true));
-               assertTrue(isJsonArray("/*foo*/ [ 123,'bar' ] /*foo*/", true));
-       }
-
-       
//====================================================================================================
-       // addLineNumbers(String)
-       
//====================================================================================================
-       @Test void a27_addLineNumbers() {
-               assertNull(getNumberedLines(null));
-               assertEquals("1: \n", getNumberedLines(""));
-               assertEquals("1: foo\n", getNumberedLines("foo"));
-               assertEquals("1: foo\n2: bar\n", getNumberedLines("foo\nbar"));
-       }
-
-       
//====================================================================================================
-       // compare(String,String)
-       
//====================================================================================================
-       @Test void a28_compare() {
-               assertTrue(compare("a","b") < 0);
-               assertTrue(compare("b","a") > 0);
-               assertTrue(compare(null,"b") < 0);
-               assertTrue(compare("b",null) > 0);
-               assertEquals(0, compare(null,null));
-       }
-
-       
//====================================================================================================
-       // matchPattern(String)
-       
//====================================================================================================
-       @Test void a29_getMatchPattern() {
-               
assertTrue(StringUtils.getMatchPattern("a").matcher("a").matches());
-               
assertTrue(StringUtils.getMatchPattern("*a*").matcher("aaa").matches());
-               
assertFalse(StringUtils.getMatchPattern("*b*").matcher("aaa").matches());
-       }
-
-       
//====================================================================================================
-       // getDuration(String)
-       
//====================================================================================================
-       @Test void a30_getDuration() {
-               assertEquals(-1, getDuration(null));
-               assertEquals(-1, getDuration(""));
-               assertEquals(-1, getDuration(" "));
-               assertEquals(1, getDuration("1"));
-               assertEquals(10, getDuration("10"));
-               assertEquals(10, getDuration("10"));
-
-               long
-                       s = 1000,
-                       m = s * 60,
-                       h = m * 60,
-                       d = h * 24,
-                       w = d * 7;
-
-               assertEquals(10*s, getDuration("10s"));
-               assertEquals(10*s, getDuration("10 s"));
-               assertEquals(10*s, getDuration("  10  s  "));
-               assertEquals(10*s, getDuration("10sec"));
-               assertEquals(10*s, getDuration("10 sec"));
-               assertEquals(10*s, getDuration("  10  sec  "));
-               assertEquals(10*s, getDuration("10seconds"));
-               assertEquals(10*s, getDuration("10 seconds"));
-               assertEquals(10*s, getDuration("  10  seconds  "));
-               assertEquals(10*s, getDuration("10S"));
-               assertEquals(10*s, getDuration("10 S"));
-               assertEquals(10*s, getDuration("  10  S  "));
-
-               assertEquals(10*m, getDuration("10m"));
-               assertEquals(10*m, getDuration("10 m"));
-               assertEquals(10*m, getDuration("  10  m  "));
-               assertEquals(10*m, getDuration("10min"));
-               assertEquals(10*m, getDuration("10 min"));
-               assertEquals(10*m, getDuration("  10  min  "));
-               assertEquals(10*m, getDuration("10minutes"));
-               assertEquals(10*m, getDuration("10 minutes"));
-               assertEquals(10*m, getDuration("  10  minutes  "));
-               assertEquals(10*m, getDuration("10M"));
-               assertEquals(10*m, getDuration("10 M"));
-               assertEquals(10*m, getDuration("  10  M  "));
-
-               assertEquals(10*h, getDuration("10h"));
-               assertEquals(10*h, getDuration("10 h"));
-               assertEquals(10*h, getDuration("  10  h  "));
-               assertEquals(10*h, getDuration("10hour"));
-               assertEquals(10*h, getDuration("10 hour"));
-               assertEquals(10*h, getDuration("  10  hour  "));
-               assertEquals(10*h, getDuration("10hours"));
-               assertEquals(10*h, getDuration("10 hours"));
-               assertEquals(10*h, getDuration("  10  hours  "));
-               assertEquals(10*h, getDuration("10H"));
-               assertEquals(10*h, getDuration("10 H"));
-               assertEquals(10*h, getDuration("  10  H  "));
-
-               assertEquals(10*d, getDuration("10d"));
-               assertEquals(10*d, getDuration("10 d"));
-               assertEquals(10*d, getDuration("  10  d  "));
-               assertEquals(10*d, getDuration("10day"));
-               assertEquals(10*d, getDuration("10 day"));
-               assertEquals(10*d, getDuration("  10  day  "));
-               assertEquals(10*d, getDuration("10days"));
-               assertEquals(10*d, getDuration("10 days"));
-               assertEquals(10*d, getDuration("  10  days  "));
-               assertEquals(10*d, getDuration("10D"));
-               assertEquals(10*d, getDuration("10 D"));
-               assertEquals(10*d, getDuration("  10  D  "));
-
-               assertEquals(10*w, getDuration("10w"));
-               assertEquals(10*w, getDuration("10 w"));
-               assertEquals(10*w, getDuration("  10  w  "));
-               assertEquals(10*w, getDuration("10week"));
-               assertEquals(10*w, getDuration("10 week"));
-               assertEquals(10*w, getDuration("  10  week  "));
-               assertEquals(10*w, getDuration("10weeks"));
-               assertEquals(10*w, getDuration("10 weeks"));
-               assertEquals(10*w, getDuration("  10  weeks  "));
-               assertEquals(10*w, getDuration("10W"));
-               assertEquals(10*w, getDuration("10 W"));
-               assertEquals(10*w, getDuration("  10  W  "));
-       }
-
-       
//====================================================================================================
-       // getDuration(String)
-       
//====================================================================================================
-       @Test void a31_stripInvalidHttpHeaderChars() {
-               assertEquals("xxx", stripInvalidHttpHeaderChars("xxx"));
-               assertEquals("\t []^x", 
stripInvalidHttpHeaderChars("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u0020\\[]^x"));
-       }
-
-       
//====================================================================================================
-       // abbreviate(String,int)
-       
//====================================================================================================
-       @Test void a32_abbrevate() {
-               assertNull(abbreviate(null, 0));
-               assertEquals("foo", abbreviate("foo", 3));
-               assertEquals("...", abbreviate("fooo", 3));
-               assertEquals("f...", abbreviate("foooo", 4));
-               assertEquals("foo", abbreviate("foo", 2));
-       }
-
-       
//====================================================================================================
-       // splitMethodArgs(String)
-       
//====================================================================================================
-       @Test void a33_splitMethodArgs() {
-               assertList(StringUtils.splitMethodArgs("java.lang.String"), 
"java.lang.String");
-               
assertList(StringUtils.splitMethodArgs("java.lang.String,java.lang.Integer"), 
"java.lang.String", "java.lang.Integer");
-               assertList(StringUtils.splitMethodArgs("x,y"), "x","y");
-               assertList(StringUtils.splitMethodArgs("x,y<a,b>,z"), "x", 
"y<a,b>", "z");
-               assertList(StringUtils.splitMethodArgs("x,y<a<b,c>,d<e,f>>,z"), 
"x", "y<a<b,c>,d<e,f>>", "z");
-       }
-
-       
//====================================================================================================
-       // fixUrl(String)
-       
//====================================================================================================
-       @Test void a34_fixUrl() {
-               assertEquals(null, fixUrl(null));
-               assertEquals("", fixUrl(""));
-               assertEquals("xxx", fixUrl("xxx"));
-               assertEquals("+x+x+", fixUrl(" x x "));
-               assertEquals("++x++x++", fixUrl("  x  x  "));
-               assertEquals("foo%7Bbar%7Dbaz", fixUrl("foo{bar}baz"));
-               assertEquals("%7Dfoo%7Bbar%7Dbaz%7B", fixUrl("}foo{bar}baz{"));
-       }
-
-       
//====================================================================================================
-       // diffPosition(String,String)
-       
//====================================================================================================
-       @Test void a35_diffPosition() {
-               assertEquals(-1, diffPosition("a", "a"));
-               assertEquals(-1, diffPosition(null, null));
-               assertEquals(0, diffPosition("a", "b"));
-               assertEquals(1, diffPosition("aa", "ab"));
-               assertEquals(1, diffPosition("aaa", "ab"));
-               assertEquals(1, diffPosition("aa", "abb"));
-               assertEquals(0, diffPosition("a", null));
-               assertEquals(0, diffPosition(null, "b"));
-       }
-
-       
//====================================================================================================
-       // diffPositionIc(String,String)
-       
//====================================================================================================
-       @Test void a36_diffPositionIc() {
-               assertEquals(-1, diffPositionIc("a", "a"));
-               assertEquals(-1, diffPositionIc("a", "A"));
-               assertEquals(-1, diffPositionIc(null, null));
-               assertEquals(0, diffPositionIc("a", "b"));
-               assertEquals(1, diffPositionIc("aa", "Ab"));
-               assertEquals(1, diffPositionIc("aaa", "Ab"));
-               assertEquals(1, diffPositionIc("aa", "Abb"));
-               assertEquals(0, diffPositionIc("a", null));
-               assertEquals(0, diffPositionIc(null, "b"));
-       }
-
-       
//====================================================================================================
-       // splitNested(String)
-       
//====================================================================================================
-       @Test void a37_splitNested() {
-               assertNull(StringUtils.splitNested(null));
-               assertEmpty(StringUtils.splitNested(""));
-               assertList(StringUtils.splitNested("a"), "a");
-               assertList(StringUtils.splitNested("a,b,c"), "a", "b", "c");
-               assertList(StringUtils.splitNested("a{b,c},d"), "a{b,c}", "d");
-               assertList(StringUtils.splitNested("a,b{c,d}"), "a", "b{c,d}");
-               assertList(StringUtils.splitNested("a,b{c,d{e,f}}"), "a", 
"b{c,d{e,f}}");
-               assertList(StringUtils.splitNested("a { b , c } , d "), "a { b 
, c }", "d");
-               assertList(StringUtils.splitNested("a\\,b"), "a,b");
-               assertList(StringUtils.splitNested("a\\\\,b"), "a\\", "b");
-       }
-
-       
//====================================================================================================
-       // splitNestedInner(String)
-       
//====================================================================================================
-       @Test void a38_splitNestedInner() {
-               assertThrowsWithMessage(IllegalArgumentException.class, "String 
was null.", ()->StringUtils.splitNestedInner(null));
-               assertThrowsWithMessage(IllegalArgumentException.class, "String 
was empty.", ()->StringUtils.splitNestedInner(""));
-               assertList(StringUtils.splitNestedInner("a{b}"), "b");
-               assertList(StringUtils.splitNestedInner(" a { b } "), "b");
-               assertList(StringUtils.splitNestedInner("a{b,c}"), "b", "c");
-               assertList(StringUtils.splitNestedInner("a{b{c,d},e{f,g}}"), 
"b{c,d}", "e{f,g}");
-       }
-
-       
//====================================================================================================
-       // toHex2(int)
-       
//====================================================================================================
-       @Test void a38_toHex2() {
-               // Test zero
-               assertString("00", toHex2(0));
-
-               // Test small positive numbers
-               assertString("01", toHex2(1));
-               assertString("0F", toHex2(15));
-               assertString("10", toHex2(16));
-               assertString("FF", toHex2(255));
-
-               // Test maximum valid value
-               assertString("FF", toHex2(255));
-
-               // Test values outside valid range - should throw exception
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(256));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(511));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(Integer.MAX_VALUE));
-
-               // Test negative numbers - should throw exception
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(-1));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(-100));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex2 
can only be used on numbers between 0 and 255", ()->toHex2(Integer.MIN_VALUE));
-
-               // Test edge cases
-               assertString("0A", toHex2(10));
-               assertString("0B", toHex2(11));
-               assertString("0C", toHex2(12));
-               assertString("0D", toHex2(13));
-               assertString("0E", toHex2(14));
-       }
-
-       
//====================================================================================================
-       // toHex4(int)
-       
//====================================================================================================
-       @Test void a39_toHex4() {
-               // Test zero
-               assertString("0000", toHex4(0));
-
-               // Test small positive numbers
-               assertString("0001", toHex4(1));
-               assertString("000F", toHex4(15));
-               assertString("0010", toHex4(16));
-               assertString("00FF", toHex4(255));
-
-               // Test larger numbers
-               assertString("0100", toHex4(256));
-               assertString("1000", toHex4(4096));
-               assertString("FFFF", toHex4(65535));
-
-               // Test maximum 16-bit value
-               assertString("FFFF", toHex4(65535));
-
-               // Test larger values (these get truncated to 4 hex characters)
-               assertString("0000", toHex4(65536));
-               assertString("FFFF", toHex4(1048575));
-
-               // Test maximum int value (this gets truncated to 4 hex 
characters)
-               assertString("FFFF", toHex4(Integer.MAX_VALUE));
-
-               // Test negative numbers - should throw exception
-               assertThrowsWithMessage(NumberFormatException.class, "toHex4 
can only be used on non-negative numbers", ()->toHex4(-1));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex4 
can only be used on non-negative numbers", ()->toHex4(-100));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex4 
can only be used on non-negative numbers", ()->toHex4(Integer.MIN_VALUE));
-
-               // Test edge cases
-               assertString("000A", toHex4(10));
-               assertString("000B", toHex4(11));
-               assertString("000C", toHex4(12));
-               assertString("000D", toHex4(13));
-               assertString("000E", toHex4(14));
-       }
-
-       
//====================================================================================================
-       // toHex8(long)
-       
//====================================================================================================
-       @Test void a40_toHex8() {
-               // Test zero
-               assertString("00000000", toHex8(0));
-
-               // Test small positive numbers
-               assertString("00000001", toHex8(1));
-               assertString("0000000F", toHex8(15));
-               assertString("00000010", toHex8(16));
-               assertString("000000FF", toHex8(255));
-
-               // Test larger numbers
-               assertString("00000100", toHex8(256));
-               assertString("00001000", toHex8(4096));
-               assertString("00010000", toHex8(65536));
-               assertString("00100000", toHex8(1048576));
-               assertString("01000000", toHex8(16777216));
-               assertString("10000000", toHex8(268435456));
-
-               // Test maximum 32-bit value
-               assertString("FFFFFFFF", toHex8(4294967295L));
-
-               // Test larger values (these get truncated to 8 hex characters)
-               assertString("00000000", toHex8(4294967296L));
-               assertString("FFFFFFFF", toHex8(68719476735L));
-
-               // Test maximum long value (this gets truncated to 8 hex 
characters)
-               assertString("FFFFFFFF", toHex8(Long.MAX_VALUE));
-
-               // Test negative numbers - should throw exception
-               assertThrowsWithMessage(NumberFormatException.class, "toHex8 
can only be used on non-negative numbers", ()->toHex8(-1));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex8 
can only be used on non-negative numbers", ()->toHex8(-100));
-               assertThrowsWithMessage(NumberFormatException.class, "toHex8 
can only be used on non-negative numbers", ()->toHex8(Long.MIN_VALUE));
-
-               // Test edge cases
-               assertString("0000000A", toHex8(10));
-               assertString("0000000B", toHex8(11));
-               assertString("0000000C", toHex8(12));
-               assertString("0000000D", toHex8(13));
-               assertString("0000000E", toHex8(14));
-       }
-
-       
//====================================================================================================
-       // String validation methods
-       
//====================================================================================================
-
-       @Test void a41_isBlank() {
-               assertTrue(StringUtils.isBlank(null));
-               assertTrue(StringUtils.isBlank(""));
-               assertTrue(StringUtils.isBlank("   "));
-               assertTrue(StringUtils.isBlank("\t\n"));
-               assertFalse(StringUtils.isBlank("hello"));
-               assertFalse(StringUtils.isBlank(" hello "));
-               assertFalse(StringUtils.isBlank("a"));
-       }
-
-       @Test void a42_isNotBlank() {
-               assertFalse(isNotBlank(null));
-               assertFalse(isNotBlank(""));
-               assertFalse(isNotBlank("   "));
-               assertFalse(isNotBlank("\t\n"));
-               assertTrue(isNotBlank("hello"));
-               assertTrue(isNotBlank(" hello "));
-               assertTrue(isNotBlank("a"));
-       }
-
-       @Test void a43_isEmpty() {
-               assertTrue(Utils.isEmpty((String)null));
-               assertTrue(Utils.isEmpty(""));
-               assertFalse(Utils.isEmpty("   "));
-               assertFalse(Utils.isEmpty("hello"));
-               assertFalse(Utils.isEmpty("a"));
-       }
-
-       @Test void a44_hasText() {
-               assertFalse(hasText(null));
-               assertFalse(hasText(""));
-               assertFalse(hasText("   "));
-               assertFalse(hasText("\t\n"));
-               assertTrue(hasText("hello"));
-               assertTrue(hasText(" hello "));
-               assertTrue(hasText("a"));
-       }
-
-       @Test void a45_isAlpha() {
-               assertFalse(isAlpha(null));
-               assertFalse(isAlpha(""));
-               assertTrue(isAlpha("abc"));
-               assertTrue(isAlpha("ABC"));
-               assertTrue(isAlpha("AbCdEf"));
-               assertFalse(isAlpha("abc123"));
-               assertFalse(isAlpha("abc def"));
-               assertFalse(isAlpha("abc-def"));
-               assertFalse(isAlpha("123"));
-       }
-
-       @Test void a46_isAlphaNumeric() {
-               assertFalse(isAlphaNumeric(null));
-               assertFalse(isAlphaNumeric(""));
-               assertTrue(isAlphaNumeric("abc"));
-               assertTrue(isAlphaNumeric("123"));
-               assertTrue(isAlphaNumeric("abc123"));
-               assertTrue(isAlphaNumeric("ABC123"));
-               assertFalse(isAlphaNumeric("abc def"));
-               assertFalse(isAlphaNumeric("abc-123"));
-               assertFalse(isAlphaNumeric("abc_123"));
-       }
-
-       @Test void a47_isDigit() {
-               assertFalse(isDigit(null));
-               assertFalse(isDigit(""));
-               assertTrue(isDigit("123"));
-               assertTrue(isDigit("0"));
-               assertTrue(isDigit("999"));
-               assertFalse(isDigit("abc"));
-               assertFalse(isDigit("abc123"));
-               assertFalse(isDigit("12.3"));
-               assertFalse(isDigit("12-3"));
-       }
-
-       @Test void a48_isWhitespace() {
-               assertFalse(isWhitespace(null));
-               assertTrue(isWhitespace(""));
-               assertTrue(isWhitespace("   "));
-               assertTrue(isWhitespace("\t\n"));
-               assertTrue(isWhitespace("\r\n\t "));
-               assertFalse(isWhitespace(" a "));
-               assertFalse(isWhitespace("hello"));
-       }
-
-       
//====================================================================================================
-       // String manipulation methods
-       
//====================================================================================================
-
-       @Test void a49_capitalize() {
-               assertNull(capitalize(null));
-               assertEquals("", capitalize(""));
-               assertEquals("Hello", capitalize("hello"));
-               assertEquals("Hello", capitalize("Hello"));
-               assertEquals("HELLO", capitalize("HELLO"));
-               assertEquals("A", capitalize("a"));
-               assertEquals("123", capitalize("123"));
-       }
-
-       @Test void a50_uncapitalize() {
-               assertNull(uncapitalize(null));
-               assertEquals("", uncapitalize(""));
-               assertEquals("hello", uncapitalize("hello"));
-               assertEquals("hello", uncapitalize("Hello"));
-               assertEquals("hELLO", uncapitalize("HELLO"));
-               assertEquals("a", uncapitalize("A"));
-               assertEquals("123", uncapitalize("123"));
-       }
-
-       @Test void a51_reverse() {
-               assertNull(StringUtils.reverse(null));
-               assertEquals("", reverse(""));
-               assertEquals("olleh", reverse("hello"));
-               assertEquals("321", reverse("123"));
-               assertEquals("cba", reverse("abc"));
-       }
-
-       @Test void a52_remove() {
-               assertNull(remove(null, "x"));
-               assertEquals("hello", remove("hello", null));
-               assertEquals("hello", remove("hello", ""));
-               assertEquals("hell wrld", remove("hello world", "o"));
-               assertEquals("hello world", remove("hello world", "xyz"));
-               assertEquals("", remove("xxx", "x"));
-       }
-
-       @Test void a53_removeStart() {
-               assertNull(removeStart(null, "x"));
-               assertEquals("hello", removeStart("hello", null));
-               assertEquals("hello", removeStart("hello", ""));
-               assertEquals(" world", removeStart("hello world", "hello"));
-               assertEquals("hello world", removeStart("hello world", "xyz"));
-               assertEquals("", removeStart("hello", "hello"));
-       }
-
-       @Test void a54_removeEnd() {
-               assertNull(removeEnd(null, "x"));
-               assertEquals("hello", removeEnd("hello", null));
-               assertEquals("hello", removeEnd("hello", ""));
-               assertEquals("hello ", removeEnd("hello world", "world"));
-               assertEquals("hello world", removeEnd("hello world", "xyz"));
-               assertEquals("", removeEnd("hello", "hello"));
-       }
-
-       @Test void a55_substringBefore() {
-               assertNull(substringBefore(null, "."));
-               assertEquals("hello.world", substringBefore("hello.world", 
null));
-               assertEquals("hello", substringBefore("hello.world", "."));
-               assertEquals("hello.world", substringBefore("hello.world", 
"xyz"));
-               assertEquals("", substringBefore(".world", "."));
-       }
-
-       @Test void a56_substringAfter() {
-               assertNull(substringAfter(null, "."));
-               assertEquals("", substringAfter("hello.world", null));
-               assertEquals("world", substringAfter("hello.world", "."));
-               assertEquals("", substringAfter("hello.world", "xyz"));
-               assertEquals("world", substringAfter("hello.world", "."));
-       }
-
-       @Test void a57_substringBetween() {
-               assertNull(substringBetween(null, "<", ">"));
-               assertNull(substringBetween("<hello>", null, ">"));
-               assertNull(substringBetween("<hello>", "<", null));
-               assertEquals("hello", substringBetween("<hello>", "<", ">"));
-               assertNull(substringBetween("<hello>", "[", "]"));
-               assertNull(substringBetween("hello", "<", ">"));
-               assertEquals("", substringBetween("<>", "<", ">"));
-       }
-
-       @Test void a58_left() {
-               assertNull(left(null, 3));
-               assertEquals("", left("", 3));
-               assertEquals("hel", left("hello", 3));
-               assertEquals("hello", left("hello", 10));
-               assertEquals("", left("hello", 0));
-               assertEquals("", left("hello", -1));
-       }
-
-       @Test void a59_right() {
-               assertNull(right(null, 3));
-               assertEquals("", right("", 3));
-               assertEquals("llo", right("hello", 3));
-               assertEquals("hello", right("hello", 10));
-               assertEquals("", right("hello", 0));
-               assertEquals("", right("hello", -1));
-       }
-
-       @Test void a60_mid() {
-               assertNull(mid(null, 1, 3));
-               assertEquals("", mid("", 1, 3));
-               assertEquals("ell", mid("hello", 1, 3));
-               assertEquals("ello", mid("hello", 1, 10));
-               assertEquals("", mid("hello", 10, 3));
-               assertEquals("", mid("hello", -1, 3));
-               assertEquals("", mid("hello", 1, -1));
-       }
-
-       @Test void a61_padLeft() {
-               assertEquals("     ", padLeft(null, 5, ' '));
-               assertEquals("     ", padLeft("", 5, ' '));
-               assertEquals("   hello", padLeft("hello", 8, ' '));
-               assertEquals("hello", padLeft("hello", 3, ' '));
-               assertEquals("00123", padLeft("123", 5, '0'));
-       }
-
-       @Test void a62_padRight() {
-               assertEquals("     ", padRight(null, 5, ' '));
-               assertEquals("     ", padRight("", 5, ' '));
-               assertEquals("hello   ", padRight("hello", 8, ' '));
-               assertEquals("hello", padRight("hello", 3, ' '));
-               assertEquals("12300", padRight("123", 5, '0'));
-       }
-
-       @Test void a63_padCenter() {
-               assertEquals("     ", padCenter(null, 5, ' '));
-               assertEquals("     ", padCenter("", 5, ' '));
-               assertEquals("  hi  ", padCenter("hi", 6, ' '));
-               assertEquals("   hi  ", padCenter("hi", 7, ' '));
-               assertEquals("hello", padCenter("hello", 3, ' '));
-               assertEquals(" hello ", padCenter("hello", 7, ' '));
-       }
-
-       
//====================================================================================================
-       // String joining and splitting methods
-       
//====================================================================================================
-
-       @Test void a64_joinObjectArray() {
-               assertNull(StringUtils.join((Object[])null, ","));
-               assertEquals("", StringUtils.join(a(), ","));
-               assertEquals("a,b,c", StringUtils.join(a("a", "b", "c"), ","));
-               assertEquals("1-2-3", StringUtils.join(a(1, 2, 3), "-"));
-               assertEquals("abc", StringUtils.join(a("a", "b", "c"), ""));
-               assertEquals("a,null,c", StringUtils.join(a("a", null, "c"), 
","));
-               assertEquals("a;b;c", StringUtils.join(a("a", "b", "c"), ";"));
-       }
-
-       @Test void a65_joinIntArray() {
-               assertEquals("", StringUtils.join((int[])null, ","));
-               assertEquals("", StringUtils.join(ints(), ","));
-               assertEquals("1,2,3", StringUtils.join(ints(1, 2, 3), ","));
-               assertEquals("1-2-3", StringUtils.join(ints(1, 2, 3), "-"));
-               assertEquals("123", StringUtils.join(ints(1, 2, 3), ""));
-       }
-
-       @Test void a66_joinCollection() {
-               assertNull(StringUtils.join((Collection<?>)null, ","));
-               assertEquals("", StringUtils.join(Collections.emptyList(), 
","));
-               assertEquals("a,b,c", StringUtils.join(l("a", "b", "c"), ","));
-               assertEquals("1-2-3", StringUtils.join(l(1, 2, 3), "-"));
-               assertEquals("a,null,c", StringUtils.join(l("a", null, "c"), 
","));
-       }
-
-       @Test void a67_joinObjectArrayChar() {
-               assertEquals("a,b,c", StringUtils.join(ao("a", "b", "c"), ','));
-               assertEquals("1-2-3", StringUtils.join(ao(1, 2, 3), '-'));
-       }
-
-       @Test void a68_joinIntArrayChar() {
-               assertEquals("1,2,3", StringUtils.join(ints(1, 2, 3), ','));
-               assertEquals("1-2-3", StringUtils.join(ints(1, 2, 3), '-'));
-       }
-
-       @Test void a69_joinCollectionChar() {
-               assertEquals("a,b,c", StringUtils.join(l("a", "b", "c"), ','));
-               assertEquals("1-2-3", StringUtils.join(l(1, 2, 3), '-'));
-       }
-
-       
//====================================================================================================
-       // String cleaning and sanitization methods
-       
//====================================================================================================
-
-       @Test void a72_clean() {
-               assertNull(clean(null));
-               assertEquals("", clean(""));
-               assertEquals("hello world", clean("hello\u0000\u0001world"));
-               assertEquals("hello world", clean("hello  \t\n  world"));
-               assertEquals("test", clean("test"));
-       }
-
-       @Test void a73_normalizeWhitespace() {
-               assertNull(normalizeWhitespace(null));
-               assertEquals("", normalizeWhitespace(""));
-               assertEquals("hello world", normalizeWhitespace("hello  \t\n  
world"));
-               assertEquals("hello world", normalizeWhitespace("  hello  world 
 "));
-               assertEquals("a b c", normalizeWhitespace("a  b  c"));
-       }
-
-       @Test void a74_removeControlChars() {
-               assertNull(removeControlChars(null));
-               assertEquals("", removeControlChars(""));
-               assertEquals("hello  world", 
removeControlChars("hello\u0000\u0001world"));
-               assertEquals("hello\nworld", 
removeControlChars("hello\nworld"));
-               assertEquals("test", removeControlChars("test"));
-       }
-
-       @Test void a75_removeNonPrintable() {
-               assertNull(removeNonPrintable(null));
-               assertEquals("", removeNonPrintable(""));
-               assertEquals("helloworld", 
removeNonPrintable("hello\u0000world"));
-               assertEquals("test", removeNonPrintable("test"));
-       }
-
-       @Test void a76_swapCase() {
-               assertNull(swapCase(null));
-               assertEquals("", swapCase(""));
-               assertEquals("hELLO wORLD", swapCase("Hello World"));
-               assertEquals("abc123XYZ", swapCase("ABC123xyz"));
-               assertEquals("123", swapCase("123"));
-       }
-}
\ No newline at end of file

Reply via email to