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

commit b133217da9506431f3f4145bf8194b867f9b7fe8
Author: James Bognar <[email protected]>
AuthorDate: Mon Dec 1 18:20:55 2025 -0800

    Unit tests
---
 TODO.md                                            |   3 +-
 .../apache/juneau/common/collections/Cache.java    |   5 +
 .../apache/juneau/common/collections/Cache2.java   |   2 +
 .../apache/juneau/common/collections/Cache3.java   |   2 +
 .../apache/juneau/common/collections/Cache4.java   |   2 +
 .../apache/juneau/common/collections/Cache5.java   |   2 +
 .../juneau/common/collections/MapBuilder.java      |   1 +
 .../juneau/common/collections/SetBuilder.java      |   1 +
 .../java/org/apache/juneau/common/io/LocalDir.java |   1 +
 .../juneau/common/reflect/AnnotationInfo.java      |   1 +
 .../juneau/common/reflect/AnnotationProvider.java  |   1 +
 .../apache/juneau/common/reflect/ClassInfo.java    |  52 +-
 .../juneau/common/reflect/ClassInfoTyped.java      |   1 -
 .../juneau/common/reflect/ParameterInfo.java       |   2 +
 .../apache/juneau/common/utils/StringFormat.java   |   4 +-
 .../apache/juneau/common/utils/StringUtils.java    | 711 ++++++++++-----------
 16 files changed, 406 insertions(+), 385 deletions(-)

diff --git a/TODO.md b/TODO.md
index 0c9bc4f0af..25c73573fb 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,6 +1,6 @@
 # TODO List
 
-**Last generated TODO number: TODO-89**
+**Last generated TODO number: TODO-90**
 
 This file tracks pending tasks for the Apache Juneau project. For completed 
items, see [TODO-completed.md](TODO-completed.md).
 
@@ -18,6 +18,7 @@ This file tracks pending tasks for the Apache Juneau project. 
For completed item
 - [ ] TODO-27 Determine if there are any other good candidates for 
Stringifiers and Listifiers.
 - [ ] TODO-29 Finish setting up SonarQube analysis in git workflow.
 - [ ] TODO-54 Search for places in code where Calendar should be replaced with 
ZonedDateTime.
+- [ ] TODO-90 Investigate replacing `StringUtils.parseIsoCalendar()` with 
java.time APIs and removing the helper if possible.
 
 ## Framework Improvements
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
index 2e9556a08b..79da0eb180 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
@@ -401,6 +401,7 @@ public class Cache<K,V> {
                        return cacheMode(WEAK);
                }
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache with explicit 
type parameters.
         *
@@ -424,6 +425,7 @@ public class Cache<K,V> {
        public static <K,V> Builder<K,V> create() {
                return new Builder<>();
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache.
         *
@@ -471,6 +473,7 @@ public class Cache<K,V> {
        private final Function<K,V> supplier;
 
        private final AtomicInteger cacheHits = new AtomicInteger();
+
        /**
         * Constructor.
         *
@@ -509,6 +512,7 @@ public class Cache<K,V> {
                        shutdownMessage(() -> builder.id + ":  hits=" + 
cacheHits.get() + ", misses: " + size());
                }
        }
+
        /**
         * Removes all entries from the cache.
         */
@@ -516,6 +520,7 @@ public class Cache<K,V> {
                getMap().clear();
                getWrapperCache().clear(); // Clean up wrapper cache
        }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified key.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
index 1c2d6a03b1..d56ccdb5ac 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
@@ -335,6 +335,7 @@ public class Cache2<K1,K2,V> {
                }
 
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache with explicit 
type parameters.
         *
@@ -359,6 +360,7 @@ public class Cache2<K1,K2,V> {
        public static <K1,K2,V> Builder<K1,K2,V> create() {
                return new Builder<>();
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
index 80ff1a288e..eab49a21d0 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
@@ -217,6 +217,7 @@ public class Cache3<K1,K2,K3,V> {
                        return cacheMode(WEAK);
                }
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache with explicit 
type parameters.
         *
@@ -233,6 +234,7 @@ public class Cache3<K1,K2,K3,V> {
        public static <K1,K2,K3,V> Builder<K1,K2,K3,V> create() {
                return new Builder<>();
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
index 3e594c4ea8..d0450dcb77 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
@@ -204,6 +204,7 @@ public class Cache4<K1,K2,K3,K4,V> {
                        return cacheMode(WEAK);
                }
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache with explicit 
type parameters.
         *
@@ -221,6 +222,7 @@ public class Cache4<K1,K2,K3,K4,V> {
        public static <K1,K2,K3,K4,V> Builder<K1,K2,K3,K4,V> create() {
                return new Builder<>();
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
index 7b5436644b..c499c21c12 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
@@ -207,6 +207,7 @@ public class Cache5<K1,K2,K3,K4,K5,V> {
                        return cacheMode(WEAK);
                }
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache with explicit 
type parameters.
         *
@@ -225,6 +226,7 @@ public class Cache5<K1,K2,K3,K4,K5,V> {
        public static <K1,K2,K3,K4,K5,V> Builder<K1,K2,K3,K4,K5,V> create() {
                return new Builder<>();
        }
+
        /**
         * Creates a new {@link Builder} for constructing a cache.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/MapBuilder.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/MapBuilder.java
index 1e86e36bae..113ff6bb3b 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/MapBuilder.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/MapBuilder.java
@@ -123,6 +123,7 @@ public class MapBuilder<K,V> {
        public static <K,V> MapBuilder<K,V> create(Class<K> keyType, Class<V> 
valueType) {
                return new MapBuilder<>(assertArgNotNull("keyType", keyType), 
assertArgNotNull("valueType", valueType));
        }
+
        private Map<K,V> map;
        private boolean unmodifiable = false, sparse = false;
        private Comparator<K> comparator;
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/SetBuilder.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/SetBuilder.java
index 6acaade43a..7829384847 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/SetBuilder.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/SetBuilder.java
@@ -119,6 +119,7 @@ public class SetBuilder<E> {
        public static <E> SetBuilder<E> create(Class<E> elementType) {
                return new SetBuilder<>(assertArgNotNull("elementType", 
elementType));
        }
+
        private Set<E> set;
        private boolean unmodifiable, sparse;
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/LocalDir.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/LocalDir.java
index 5cd98697cc..a2c6a2714f 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/LocalDir.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/LocalDir.java
@@ -105,6 +105,7 @@ public class LocalDir {
                        return true;
                });
        }
+
        private final Class<?> clazz;
        private final String clazzPath;
        private final Path path;
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
index ba833e4988..3ecd7d111b 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
@@ -96,6 +96,7 @@ public class AnnotationInfo<T extends Annotation> {
                        .orElse(0);
                // @formatter:on
        }
+
        private final Annotatable annotatable;
        final int rank;
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
index 9d20b5c61c..ad0c472c9c 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
@@ -451,6 +451,7 @@ public class AnnotationProvider {
        private static <A extends Annotation> AnnotationInfo<A> ai(Annotatable 
on, A value) {
                return AnnotationInfo.of(on, value);
        }
+
        private final Cache<Object,List<AnnotationInfo<Annotation>>> 
runtimeCache;
        private final Cache3<Class<?>,ElementInfo,AnnotationTraversal[],List> 
cache;
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
index 9c35a6b5ab..6c78b3d618 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
@@ -119,27 +119,27 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
        /**
         * Returns a class info wrapper around the specified class type.
         *
-        * @param <T> The class type.
         * @param inner The class type.
+        * @param innerType The generic type (if parameterized type).
         * @return The constructed class info.
         */
-       public static <T> ClassInfoTyped<T> of(Class<T> inner) {
-               return (ClassInfoTyped<T>)CACHE.get(inner, () -> new 
ClassInfoTyped<>(inner));
+       public static ClassInfo of(Class<?> inner, Type innerType) {
+               if (inner == innerType)
+                       return of(inner);
+               if (inner != null)
+                       return new ClassInfoTyped<>(inner, innerType);
+               return new ClassInfo(null, innerType);
        }
 
        /**
         * Returns a class info wrapper around the specified class type.
         *
+        * @param <T> The class type.
         * @param inner The class type.
-        * @param innerType The generic type (if parameterized type).
         * @return The constructed class info.
         */
-       public static ClassInfo of(Class<?> inner, Type innerType) {
-               if (inner == innerType)
-                       return of(inner);
-               if (inner != null)
-                       return new ClassInfoTyped<>(inner, innerType);
-               return new ClassInfo(null, innerType);
+       public static <T> ClassInfoTyped<T> of(Class<T> inner) {
+               return (ClassInfoTyped<T>)CACHE.get(inner, () -> new 
ClassInfoTyped<>(inner));
        }
 
        /**
@@ -463,21 +463,21 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The annotation if found, or <jk>null</jk> if not.
         */
 
-               /**
-                * Returns the component type of this class if it is an array 
type.
-                *
-                * <p>
-                * This is equivalent to {@link Class#getComponentType()} but 
returns a {@link ClassInfo} instead.
-                * Note that {@link #getComponentType()} also exists and 
returns the base component type for multi-dimensional arrays.
-                *
-                * @return The {@link ClassInfo} representing the component 
type, or <jk>null</jk> if this class does not represent an array type.
-                */
-               public ClassInfo componentType() {
-                       if (inner == null)
-                               return null;
-                       var ct = inner.componentType();
-                       return ct == null ? null : of(ct);
-               }
+       /**
+        * Returns the component type of this class if it is an array type.
+        *
+        * <p>
+        * This is equivalent to {@link Class#getComponentType()} but returns a 
{@link ClassInfo} instead.
+        * Note that {@link #getComponentType()} also exists and returns the 
base component type for multi-dimensional arrays.
+        *
+        * @return The {@link ClassInfo} representing the component type, or 
<jk>null</jk> if this class does not represent an array type.
+        */
+       public ClassInfo componentType() {
+               if (inner == null)
+                       return null;
+               var ct = inner.componentType();
+               return ct == null ? null : of(ct);
+       }
 
        /**
         * Returns the descriptor string of this class.
@@ -711,7 +711,7 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         *
         * @return The class loader for this class, or <jk>null</jk> if it 
doesn't have one.
         */
-public ClassLoader getClassLoader() { return inner == null ? null : 
inner.getClassLoader(); }
+       public ClassLoader getClassLoader() { return inner == null ? null : 
inner.getClassLoader(); }
 
        /**
         * Returns the base component type of this class.
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
index fe5994b648..eb340935a2 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfoTyped.java
@@ -45,4 +45,3 @@ public class ClassInfoTyped<T> extends ClassInfo {
        }
 
 }
-
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
index c416710cb4..d6c08a23e9 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
@@ -135,9 +135,11 @@ public class ParameterInfo extends ElementInfo implements 
Annotatable {
                }
                throw new IllegalArgumentException("Parameter not found in 
declaring executable: " + inner);
        }
+
        static void reset() {
                DISABLE_PARAM_NAME_DETECTION.reset();
        }
+
        private final ExecutableInfo executable;
        private final Parameter inner;
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
index 8de20c61d2..50b0805484 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
@@ -385,7 +385,8 @@ public final class StringFormat {
        private static final Cache2<Locale,String,MessageFormat> 
MESSAGE_FORMAT_CACHE = Cache2.of(Locale.class, String.class, 
MessageFormat.class).maxSize(100).threadLocal().cacheMode(CACHE_MODE)
                .supplier((locale, content) -> new MessageFormat(content, 
locale)).build();
 
-       private static final Cache<Locale,NumberFormat> NUMBER_FORMAT_CACHE = 
Cache.of(Locale.class, 
NumberFormat.class).maxSize(50).threadLocal().cacheMode(CACHE_MODE).supplier(NumberFormat::getInstance).build();
+       private static final Cache<Locale,NumberFormat> NUMBER_FORMAT_CACHE = 
Cache.of(Locale.class, 
NumberFormat.class).maxSize(50).threadLocal().cacheMode(CACHE_MODE).supplier(NumberFormat::getInstance)
+               .build();
 
        private static final Cache<Locale,DateFormat> DATE_FORMAT_CACHE = 
Cache.of(Locale.class, 
DateFormat.class).maxSize(50).threadLocal().cacheMode(CACHE_MODE)
                .supplier(locale -> 
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, 
locale)).build();
@@ -452,6 +453,7 @@ public final class StringFormat {
                        return;
                tokens.add(new LiteralToken(pattern.substring(start)));
        }
+
        private static void lit(List<Token> tokens, String pattern, int start, 
int end) {
                if (start == end)
                        return;
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 09060bd783..8288749f38 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
@@ -181,166 +181,6 @@ public class StringUtils {
                return in.substring(0, length - 3) + "...";
        }
 
-       private static List<Tuple2<Class<?>,Function<Object,String>>> 
loadReadifiers() {
-               var list = new 
ArrayList<Tuple2<Class<?>,Function<Object,String>>>();
-
-               // More specific types first - order matters!
-
-               // Map.Entry before Map
-               list.add(Tuple2.of(Map.Entry.class, o -> {
-                       var e = (Map.Entry<?,?>)o;
-                       return readable(e.getKey()) + '=' + 
readable(e.getValue());
-               }));
-
-               // Collection before Iterable
-               list.add(Tuple2.of(Collection.class, o -> {
-                       var c = (Collection<?>)o;
-                       return 
c.stream().map(StringUtils::readable).collect(joining(",", "[", "]"));
-               }));
-
-               // Map
-               list.add(Tuple2.of(Map.class, o -> {
-                       var m = (Map<?,?>)o;
-                       return 
m.entrySet().stream().map(StringUtils::readable).collect(joining(",", "{", 
"}"));
-               }));
-
-               // Iterable (but not Collection, which is handled above)
-               list.add(Tuple2.of(Iterable.class, o -> {
-                       var i = (Iterable<?>)o;
-                       return readable(toList(i));
-               }));
-
-               // Iterator
-               list.add(Tuple2.of(Iterator.class, o -> {
-                       var i = (Iterator<?>)o;
-                       return readable(toList(i));
-               }));
-
-               // Enumeration
-               list.add(Tuple2.of(Enumeration.class, o -> {
-                       var e = (Enumeration<?>)o;
-                       return readable(toList(e));
-               }));
-
-               // Optional
-               list.add(Tuple2.of(Optional.class, o -> {
-                       var opt = (Optional<?>)o;
-                       return readable(opt.orElse(null));
-               }));
-
-               // GregorianCalendar
-               list.add(Tuple2.of(GregorianCalendar.class, o -> {
-                       var cal = (GregorianCalendar)o;
-                       return 
cal.toZonedDateTime().format(DateTimeFormatter.ISO_INSTANT);
-               }));
-
-               // Date
-               list.add(Tuple2.of(Date.class, o -> {
-                       var date = (Date)o;
-                       return date.toInstant().toString();
-               }));
-
-               // InputStream
-               list.add(Tuple2.of(InputStream.class, o -> {
-                       var is = (InputStream)o;
-                       return toHex(is);
-               }));
-
-               // Reader
-               list.add(Tuple2.of(Reader.class, o -> {
-                       var r = (Reader)o;
-                       return safe(() -> read(r));
-               }));
-
-               // File
-               list.add(Tuple2.of(File.class, o -> {
-                       var f = (File)o;
-                       return safe(() -> read(f));
-               }));
-
-               // byte[]
-               list.add(Tuple2.of(byte[].class, o -> {
-                       var bytes = (byte[])o;
-                       return toHex(bytes);
-               }));
-
-               // Enum
-               list.add(Tuple2.of(Enum.class, o -> {
-                       var e = (Enum<?>)o;
-                       return e.name();
-               }));
-
-               // Class
-               list.add(Tuple2.of(Class.class, o -> {
-                       var c = (Class<?>)o;
-                       return cns(c);
-               }));
-
-               // Executable (Method or Constructor)
-               list.add(Tuple2.of(Executable.class, o -> {
-                       var exec = (Executable)o;
-                       var sb = new StringBuilder(64);
-                       sb.append(exec instanceof Constructor ? 
cns(exec.getDeclaringClass()) : exec.getName()).append('(');
-                       var pt = exec.getParameterTypes();
-                       for (var i = 0; i < pt.length; i++) {
-                               if (i > 0)
-                                       sb.append(',');
-                               sb.append(cns(pt[i]));
-                       }
-                       sb.append(')');
-                       return sb.toString();
-               }));
-
-               // ClassInfo
-               list.add(Tuple2.of(ClassInfo.class, o -> {
-                       var ci = (ClassInfo)o;
-                       return ci.toString();
-               }));
-
-               // ExecutableInfo
-               list.add(Tuple2.of(ExecutableInfo.class, o -> {
-                       var ei = (ExecutableInfo)o;
-                       return ei.toString();
-               }));
-
-               // FieldInfo
-               list.add(Tuple2.of(FieldInfo.class, o -> {
-                       var fi = (FieldInfo)o;
-                       return fi.toString();
-               }));
-
-               // ParameterInfo
-               list.add(Tuple2.of(ParameterInfo.class, o -> {
-                       var pi = (ParameterInfo)o;
-                       return pi.toString();
-               }));
-
-               // Field
-               list.add(Tuple2.of(Field.class, o -> {
-                       var f = (Field)o;
-                       return cns(f.getDeclaringClass()) + "." + f.getName();
-               }));
-
-               // Parameter
-               list.add(Tuple2.of(Parameter.class, o -> {
-                       var p = (Parameter)o;
-                       var exec = p.getDeclaringExecutable();
-                       var sb = new StringBuilder(64);
-                       sb.append(exec instanceof Constructor ? 
cns(exec.getDeclaringClass()) : exec.getName()).append('[');
-                       var params = exec.getParameters();
-                       for (var i = 0; i < params.length; i++) {
-                               if (params[i] == p) {
-                                       sb.append(i);
-                                       break;
-                               }
-                       }
-                       sb.append(']');
-                       return sb.toString();
-               }));
-
-               return Collections.unmodifiableList(list);
-       }
-
        /**
         * Appends a string to a StringBuilder, creating a new one if null.
         *
@@ -3823,172 +3663,66 @@ public class StringUtils {
        }
 
        /**
-        * Validates if a string is a valid IPv6 address format (without 
network operations).
+        * Validates if a string is a valid MAC address.
         *
         * <p>
-        * This method performs pure string-based validation and does not 
perform any DNS lookups
-        * or network operations, making it fast and suitable for validation 
purposes.
+        * Supports common MAC address formats:
+        * <ul>
+        *   <li>Colon-separated: <js>"00:1B:44:11:3A:B7"</js></li>
+        *   <li>Hyphen-separated: <js>"00-1B-44-11-3A-B7"</js></li>
+        *   <li>No separators: <js>"001B44113AB7"</js></li>
+        * </ul>
         *
-        * @param ip The IPv6 address string to validate.
-        * @return <jk>true</jk> if the string is a valid IPv6 address format, 
<jk>false</jk> otherwise.
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      isValidMacAddress(<js>"00:1B:44:11:3A:B7"</js>);  <jc>// 
true</jc>
+        *      isValidMacAddress(<js>"00-1B-44-11-3A-B7"</js>);  <jc>// 
true</jc>
+        *      isValidMacAddress(<js>"001B44113AB7"</js>);       <jc>// 
true</jc>
+        *      isValidMacAddress(<js>"00:1B:44:11:3A"</js>);     <jc>// false 
(too short)</jc>
+        * </p>
+        *
+        * @param mac The MAC address string to validate. Can be <jk>null</jk>.
+        * @return <jk>true</jk> if the string is a valid MAC address, 
<jk>false</jk> otherwise.
         */
-       private static boolean isValidIPv6Address(String ip) {
-               // IPv6 addresses can be:
-               // 1. Full format: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 (8 
groups of 4 hex digits)
-               // 2. Compressed format: 2001:db8::1 (uses :: to represent 
consecutive zeros)
-               // 3. IPv4-mapped: ::ffff:192.168.1.1 (last 32 bits as IPv4)
-               // 4. Loopback: ::1
-               // 5. Unspecified: ::
-
-               // Cannot start or end with a single colon (except ::)
-               if (ip.startsWith(":") && !ip.startsWith("::"))
+       public static boolean isValidMacAddress(String mac) {
+               if (isEmpty(mac))
                        return false;
-               if (ip.endsWith(":") && !ip.endsWith("::"))
+
+               // Remove separators and check if it's 12 hex digits
+               var cleaned = mac.replaceAll("[:-]", "").toUpperCase();
+               if (cleaned.length() != 12)
                        return false;
 
-               // Check for IPv4-mapped format (contains both : and .)
-               if (ip.contains(".")) {
-                       // Must be in format ::ffff:x.x.x.x or similar
-                       var lastColon = ip.lastIndexOf(":");
-                       if (lastColon < 0)
-                               return false;
-                       var ipv4Part = ip.substring(lastColon + 1);
-                       // Validate IPv4 part
-                       var ipv4Parts = ipv4Part.split("\\.");
-                       if (ipv4Parts.length != 4)
-                               return false;
-                       for (var part : ipv4Parts) {
-                               try {
-                                       var num = Integer.parseInt(part);
-                                       if (num < 0 || num > 255)
-                                               return false;
-                               } catch (@SuppressWarnings("unused") 
NumberFormatException e) {
-                                       return false;
-                               }
-                       }
-                       // Validate IPv6 part before the IPv4
-                       var ipv6Part = ip.substring(0, lastColon);
-                       if (ipv6Part.isEmpty() || ipv6Part.equals("::ffff") || 
ipv6Part.equals("::FFFF"))
-                               return true;
-                       // More complex validation would be needed for other 
IPv4-mapped formats
-                       // For now, accept common formats
+               // Check if all characters are valid hex digits
+               return cleaned.matches("^[0-9A-F]{12}$");
+       }
+
+       /**
+        * Validates if a string is a valid regular expression pattern.
+        *
+        * <p>
+        * Attempts to compile the regex pattern to verify it's syntactically 
correct.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      isValidRegex(<js>"[a-z]+"</js>);        <jc>// true</jc>
+        *      isValidRegex(<js>"[a-z"</js>);          <jc>// false (unclosed 
bracket)</jc>
+        *      isValidRegex(<js>"(test"</js>);         <jc>// false (unclosed 
parenthesis)</jc>
+        * </p>
+        *
+        * @param regex The regex pattern to validate. Can be <jk>null</jk>.
+        * @return <jk>true</jk> if the string is a valid regex pattern, 
<jk>false</jk> otherwise.
+        */
+       public static boolean isValidRegex(String regex) {
+               if (isEmpty(regex))
+                       return false;
+               try {
+                       Pattern.compile(regex);
+                       return true;
+               } catch (@SuppressWarnings("unused") PatternSyntaxException e) {
+                       return false;
                }
-
-               // Check for :: (compression) - only one allowed
-               var doubleColonCount = 0;
-               for (var i = 1; i < ip.length(); i++) {
-                       if (ip.charAt(i) == ':' && ip.charAt(i - 1) == ':') {
-                               doubleColonCount++;
-                               if (doubleColonCount > 1)
-                                       return false; // Only one :: allowed
-                       }
-               }
-
-               // Split by ::
-               var parts = ip.split("::", -1);
-               if (parts.length > 2)
-                       return false; // Only one :: allowed
-
-               if (parts.length == 2) {
-                       // Compressed format
-                       var leftParts = parts[0].isEmpty() ? new String[0] : 
parts[0].split(":");
-                       var rightParts = parts[1].isEmpty() ? new String[0] : 
parts[1].split(":");
-                       var totalParts = leftParts.length + rightParts.length;
-                       if (totalParts > 7)
-                               return false; // Too many groups (max 8, but :: 
counts as one or more)
-                       if (totalParts == 0 && !ip.equals("::"))
-                               return false; // Empty on both sides of :: is 
invalid (except :: itself)
-               } else {
-                       // Full format (no compression)
-                       var groups = ip.split(":");
-                       if (groups.length != 8)
-                               return false;
-               }
-
-               // Validate each hex group
-               var groups = ip.split("::");
-               for (var groupSection : groups) {
-                       if (groupSection.isEmpty())
-                               continue; // Skip empty section from ::
-                       var groupParts = groupSection.split(":");
-                       for (var group : groupParts) {
-                               if (group.isEmpty())
-                                       return false;
-                               if (group.length() > 4)
-                                       return false; // Each group is max 4 
hex digits
-                               // Validate hex digits
-                               for (var i = 0; i < group.length(); i++) {
-                                       var c = group.charAt(i);
-                                       if (!((c >= '0' && c <= '9') || (c >= 
'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
-                                               return false;
-                               }
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * Validates if a string is a valid MAC address.
-        *
-        * <p>
-        * Supports common MAC address formats:
-        * <ul>
-        *   <li>Colon-separated: <js>"00:1B:44:11:3A:B7"</js></li>
-        *   <li>Hyphen-separated: <js>"00-1B-44-11-3A-B7"</js></li>
-        *   <li>No separators: <js>"001B44113AB7"</js></li>
-        * </ul>
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bjava'>
-        *      isValidMacAddress(<js>"00:1B:44:11:3A:B7"</js>);  <jc>// 
true</jc>
-        *      isValidMacAddress(<js>"00-1B-44-11-3A-B7"</js>);  <jc>// 
true</jc>
-        *      isValidMacAddress(<js>"001B44113AB7"</js>);       <jc>// 
true</jc>
-        *      isValidMacAddress(<js>"00:1B:44:11:3A"</js>);     <jc>// false 
(too short)</jc>
-        * </p>
-        *
-        * @param mac The MAC address string to validate. Can be <jk>null</jk>.
-        * @return <jk>true</jk> if the string is a valid MAC address, 
<jk>false</jk> otherwise.
-        */
-       public static boolean isValidMacAddress(String mac) {
-               if (isEmpty(mac))
-                       return false;
-
-               // Remove separators and check if it's 12 hex digits
-               var cleaned = mac.replaceAll("[:-]", "").toUpperCase();
-               if (cleaned.length() != 12)
-                       return false;
-
-               // Check if all characters are valid hex digits
-               return cleaned.matches("^[0-9A-F]{12}$");
-       }
-
-       /**
-        * Validates if a string is a valid regular expression pattern.
-        *
-        * <p>
-        * Attempts to compile the regex pattern to verify it's syntactically 
correct.
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bjava'>
-        *      isValidRegex(<js>"[a-z]+"</js>);        <jc>// true</jc>
-        *      isValidRegex(<js>"[a-z"</js>);          <jc>// false (unclosed 
bracket)</jc>
-        *      isValidRegex(<js>"(test"</js>);         <jc>// false (unclosed 
parenthesis)</jc>
-        * </p>
-        *
-        * @param regex The regex pattern to validate. Can be <jk>null</jk>.
-        * @return <jk>true</jk> if the string is a valid regex pattern, 
<jk>false</jk> otherwise.
-        */
-       public static boolean isValidRegex(String regex) {
-               if (isEmpty(regex))
-                       return false;
-               try {
-                       Pattern.compile(regex);
-                       return true;
-               } catch (@SuppressWarnings("unused") PatternSyntaxException e) {
-                       return false;
-               }
-       }
+       }
 
        /**
         * Validates if a time string matches the specified time format.
@@ -4427,12 +4161,6 @@ public class StringUtils {
                return str.substring(0, len);
        }
 
-       // TODO: See if we can remove StringUtils.parseIsoCalendar.
-       // Currently used by:
-       //   - OpenApiParserSession.java for DATE/DATE_TIME format parsing
-       //   - StringUtils.parseIsoDate() (which wraps this method)
-       // Investigation needed: Can we replace this with java.time APIs or 
other standard date parsing?
-
        /**
         * Calculates the Levenshtein distance (edit distance) between two 
strings.
         *
@@ -4522,6 +4250,12 @@ public class StringUtils {
                return count;
        }
 
+       // TODO: See if we can remove StringUtils.parseIsoCalendar.
+       // Currently used by:
+       //   - OpenApiParserSession.java for DATE/DATE_TIME format parsing
+       //   - StringUtils.parseIsoDate() (which wraps this method)
+       // Investigation needed: Can we replace this with java.time APIs or 
other standard date parsing?
+
        /**
         * Null-safe convenience method for {@link String#toLowerCase()}.
         *
@@ -5177,10 +4911,6 @@ public class StringUtils {
                return ! containsAny(s, values);
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String validation methods
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Returns the specified string, or <jk>null</jk> if that string is 
<jk>null</jk> or empty.
         *
@@ -5203,6 +4933,10 @@ public class StringUtils {
                return s.substring(0, 1) + s.substring(1).replaceAll(".", "*"); 
 // NOSONAR
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // String validation methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Provides optimization suggestions for a string based on its 
characteristics.
         *
@@ -5403,10 +5137,6 @@ public class StringUtils {
                return Float.parseFloat(StringUtils.removeUnderscores(value));
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String manipulation methods
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Same as {@link Integer#parseInt(String)} but removes any underscore 
characters first.
         *
@@ -5446,10 +5176,17 @@ public class StringUtils {
                return Integer.decode(s.substring(0, s.length() - 1).trim()) * 
m;  // NOSONAR - NPE not possible here.
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // String manipulation methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Parses an ISO8601 string into a calendar.
         *
         * <p>
+        * TODO-90: Investigate whether this helper can be removed in favor of 
java.time parsing (see TODO.md).
+        *
+        * <p>
         * Supports any of the following formats:
         * <br><c>yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddThh, yyyy-MM-ddThh:mm, 
yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS</c>
         *
@@ -5903,10 +5640,6 @@ public class StringUtils {
                return sb.toString();
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String joining and splitting methods
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Generates a random numeric string of the specified length.
         *
@@ -5931,10 +5664,6 @@ public class StringUtils {
                return sb.toString();
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String cleaning and sanitization methods
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Generates a random string of the specified length using characters 
from the given character set.
         *
@@ -5964,6 +5693,10 @@ public class StringUtils {
                return sb.toString();
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // String joining and splitting methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Calculates a simple readability score for a string.
         *
@@ -6022,6 +5755,10 @@ public class StringUtils {
                return Math.max(0.0, Math.min(100.0, score));
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // String cleaning and sanitization methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Converts an arbitrary object to a readable string format suitable 
for debugging and testing.
         *
@@ -7558,10 +7295,6 @@ public class StringUtils {
                return sb.toString();
        }
 
-       
//------------------------------------------------------------------------------------------------------------------
-       // Additional utility methods
-       
//------------------------------------------------------------------------------------------------------------------
-
        /**
         * Same as {@link #toHex(byte[])} but puts spaces between the byte 
strings.
         *
@@ -7589,6 +7322,10 @@ public class StringUtils {
                return obj == null ? null : obj.toString();
        }
 
+       
//------------------------------------------------------------------------------------------------------------------
+       // Additional utility methods
+       
//------------------------------------------------------------------------------------------------------------------
+
        /**
         * Safely converts an object to a string, returning the default string 
if the object is <jk>null</jk>.
         *
@@ -7879,10 +7616,6 @@ public class StringUtils {
                return str.replace("&lt;", "<").replace("&gt;", 
">").replace("&quot;", "\"").replace("&#39;", "'").replace("&apos;", 
"'").replace("&amp;", "&");
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String Array and Collection Utilities
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Unescapes XML entities in a string.
         *
@@ -8025,10 +7758,6 @@ public class StringUtils {
                return s;
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // String Builder Utilities
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Similar to {@link URLEncoder#encode(String, String)} but doesn't 
encode <js>"/"</js> characters.
         *
@@ -8138,6 +7867,10 @@ public class StringUtils {
                return count;
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // String Builder Utilities
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Wraps text to a specified line length.
         *
@@ -8280,10 +8013,6 @@ public class StringUtils {
                return result.toString();
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Performance and Memory Utilities
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Helper method to estimate the number of syllables in a word.
         */
@@ -8338,6 +8067,10 @@ public class StringUtils {
                }
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Performance and Memory Utilities
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Helper method to get Soundex code for a character.
         */
@@ -8359,6 +8092,112 @@ public class StringUtils {
                return '0'; // Non-letter characters
        }
 
+       /**
+        * Validates if a string is a valid IPv6 address format (without 
network operations).
+        *
+        * <p>
+        * This method performs pure string-based validation and does not 
perform any DNS lookups
+        * or network operations, making it fast and suitable for validation 
purposes.
+        *
+        * @param ip The IPv6 address string to validate.
+        * @return <jk>true</jk> if the string is a valid IPv6 address format, 
<jk>false</jk> otherwise.
+        */
+       private static boolean isValidIPv6Address(String ip) {
+               // IPv6 addresses can be:
+               // 1. Full format: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 (8 
groups of 4 hex digits)
+               // 2. Compressed format: 2001:db8::1 (uses :: to represent 
consecutive zeros)
+               // 3. IPv4-mapped: ::ffff:192.168.1.1 (last 32 bits as IPv4)
+               // 4. Loopback: ::1
+               // 5. Unspecified: ::
+
+               // Cannot start or end with a single colon (except ::)
+               if (ip.startsWith(":") && !ip.startsWith("::"))
+                       return false;
+               if (ip.endsWith(":") && !ip.endsWith("::"))
+                       return false;
+
+               // Check for IPv4-mapped format (contains both : and .)
+               if (ip.contains(".")) {
+                       // Must be in format ::ffff:x.x.x.x or similar
+                       var lastColon = ip.lastIndexOf(":");
+                       if (lastColon < 0)
+                               return false;
+                       var ipv4Part = ip.substring(lastColon + 1);
+                       // Validate IPv4 part
+                       var ipv4Parts = ipv4Part.split("\\.");
+                       if (ipv4Parts.length != 4)
+                               return false;
+                       for (var part : ipv4Parts) {
+                               try {
+                                       var num = Integer.parseInt(part);
+                                       if (num < 0 || num > 255)
+                                               return false;
+                               } catch (@SuppressWarnings("unused") 
NumberFormatException e) {
+                                       return false;
+                               }
+                       }
+                       // Validate IPv6 part before the IPv4
+                       var ipv6Part = ip.substring(0, lastColon);
+                       if (ipv6Part.isEmpty() || ipv6Part.equals("::ffff") || 
ipv6Part.equals("::FFFF"))
+                               return true;
+                       // More complex validation would be needed for other 
IPv4-mapped formats
+                       // For now, accept common formats
+               }
+
+               // Check for :: (compression) - only one allowed
+               var doubleColonCount = 0;
+               for (var i = 1; i < ip.length(); i++) {
+                       if (ip.charAt(i) == ':' && ip.charAt(i - 1) == ':') {
+                               doubleColonCount++;
+                               if (doubleColonCount > 1)
+                                       return false; // Only one :: allowed
+                       }
+               }
+
+               // Split by ::
+               var parts = ip.split("::", -1);
+               if (parts.length > 2)
+                       return false; // Only one :: allowed
+
+               if (parts.length == 2) {
+                       // Compressed format
+                       var leftParts = parts[0].isEmpty() ? new String[0] : 
parts[0].split(":");
+                       var rightParts = parts[1].isEmpty() ? new String[0] : 
parts[1].split(":");
+                       var totalParts = leftParts.length + rightParts.length;
+                       if (totalParts > 7)
+                               return false; // Too many groups (max 8, but :: 
counts as one or more)
+                       if (totalParts == 0 && !ip.equals("::"))
+                               return false; // Empty on both sides of :: is 
invalid (except :: itself)
+               } else {
+                       // Full format (no compression)
+                       var groups = ip.split(":");
+                       if (groups.length != 8)
+                               return false;
+               }
+
+               // Validate each hex group
+               var groups = ip.split("::");
+               for (var groupSection : groups) {
+                       if (groupSection.isEmpty())
+                               continue; // Skip empty section from ::
+                       var groupParts = groupSection.split(":");
+                       for (var group : groupParts) {
+                               if (group.isEmpty())
+                                       return false;
+                               if (group.length() > 4)
+                                       return false; // Each group is max 4 
hex digits
+                               // Validate hex digits
+                               for (var i = 0; i < group.length(); i++) {
+                                       var c = group.charAt(i);
+                                       if (!((c >= '0' && c <= '9') || (c >= 
'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
+                                               return false;
+                               }
+                       }
+               }
+
+               return true;
+       }
+
        /**
         * Helper method to check if a character is a vowel.
         */
@@ -8366,6 +8205,166 @@ public class StringUtils {
                return c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U';
        }
 
+       private static List<Tuple2<Class<?>,Function<Object,String>>> 
loadReadifiers() {
+               var list = new 
ArrayList<Tuple2<Class<?>,Function<Object,String>>>();
+
+               // More specific types first - order matters!
+
+               // Map.Entry before Map
+               list.add(Tuple2.of(Map.Entry.class, o -> {
+                       var e = (Map.Entry<?,?>)o;
+                       return readable(e.getKey()) + '=' + 
readable(e.getValue());
+               }));
+
+               // Collection before Iterable
+               list.add(Tuple2.of(Collection.class, o -> {
+                       var c = (Collection<?>)o;
+                       return 
c.stream().map(StringUtils::readable).collect(joining(",", "[", "]"));
+               }));
+
+               // Map
+               list.add(Tuple2.of(Map.class, o -> {
+                       var m = (Map<?,?>)o;
+                       return 
m.entrySet().stream().map(StringUtils::readable).collect(joining(",", "{", 
"}"));
+               }));
+
+               // Iterable (but not Collection, which is handled above)
+               list.add(Tuple2.of(Iterable.class, o -> {
+                       var i = (Iterable<?>)o;
+                       return readable(toList(i));
+               }));
+
+               // Iterator
+               list.add(Tuple2.of(Iterator.class, o -> {
+                       var i = (Iterator<?>)o;
+                       return readable(toList(i));
+               }));
+
+               // Enumeration
+               list.add(Tuple2.of(Enumeration.class, o -> {
+                       var e = (Enumeration<?>)o;
+                       return readable(toList(e));
+               }));
+
+               // Optional
+               list.add(Tuple2.of(Optional.class, o -> {
+                       var opt = (Optional<?>)o;
+                       return readable(opt.orElse(null));
+               }));
+
+               // GregorianCalendar
+               list.add(Tuple2.of(GregorianCalendar.class, o -> {
+                       var cal = (GregorianCalendar)o;
+                       return 
cal.toZonedDateTime().format(DateTimeFormatter.ISO_INSTANT);
+               }));
+
+               // Date
+               list.add(Tuple2.of(Date.class, o -> {
+                       var date = (Date)o;
+                       return date.toInstant().toString();
+               }));
+
+               // InputStream
+               list.add(Tuple2.of(InputStream.class, o -> {
+                       var is = (InputStream)o;
+                       return toHex(is);
+               }));
+
+               // Reader
+               list.add(Tuple2.of(Reader.class, o -> {
+                       var r = (Reader)o;
+                       return safe(() -> read(r));
+               }));
+
+               // File
+               list.add(Tuple2.of(File.class, o -> {
+                       var f = (File)o;
+                       return safe(() -> read(f));
+               }));
+
+               // byte[]
+               list.add(Tuple2.of(byte[].class, o -> {
+                       var bytes = (byte[])o;
+                       return toHex(bytes);
+               }));
+
+               // Enum
+               list.add(Tuple2.of(Enum.class, o -> {
+                       var e = (Enum<?>)o;
+                       return e.name();
+               }));
+
+               // Class
+               list.add(Tuple2.of(Class.class, o -> {
+                       var c = (Class<?>)o;
+                       return cns(c);
+               }));
+
+               // Executable (Method or Constructor)
+               list.add(Tuple2.of(Executable.class, o -> {
+                       var exec = (Executable)o;
+                       var sb = new StringBuilder(64);
+                       sb.append(exec instanceof Constructor ? 
cns(exec.getDeclaringClass()) : exec.getName()).append('(');
+                       var pt = exec.getParameterTypes();
+                       for (var i = 0; i < pt.length; i++) {
+                               if (i > 0)
+                                       sb.append(',');
+                               sb.append(cns(pt[i]));
+                       }
+                       sb.append(')');
+                       return sb.toString();
+               }));
+
+               // ClassInfo
+               list.add(Tuple2.of(ClassInfo.class, o -> {
+                       var ci = (ClassInfo)o;
+                       return ci.toString();
+               }));
+
+               // ExecutableInfo
+               list.add(Tuple2.of(ExecutableInfo.class, o -> {
+                       var ei = (ExecutableInfo)o;
+                       return ei.toString();
+               }));
+
+               // FieldInfo
+               list.add(Tuple2.of(FieldInfo.class, o -> {
+                       var fi = (FieldInfo)o;
+                       return fi.toString();
+               }));
+
+               // ParameterInfo
+               list.add(Tuple2.of(ParameterInfo.class, o -> {
+                       var pi = (ParameterInfo)o;
+                       return pi.toString();
+               }));
+
+               // Field
+               list.add(Tuple2.of(Field.class, o -> {
+                       var f = (Field)o;
+                       return cns(f.getDeclaringClass()) + "." + f.getName();
+               }));
+
+               // Parameter
+               list.add(Tuple2.of(Parameter.class, o -> {
+                       var p = (Parameter)o;
+                       var exec = p.getDeclaringExecutable();
+                       var sb = new StringBuilder(64);
+                       sb.append(exec instanceof Constructor ? 
cns(exec.getDeclaringClass()) : exec.getName()).append('[');
+                       var params = exec.getParameters();
+                       for (var i = 0; i < params.length; i++) {
+                               if (params[i] == p) {
+                                       sb.append(i);
+                                       break;
+                               }
+                       }
+                       sb.append(']');
+                       return sb.toString();
+               }));
+
+               return Collections.unmodifiableList(list);
+       }
+
        /**
         * Determines the multiplier value based on the suffix character in a 
string.
         *


Reply via email to