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 8a924beaa2 Utility class modernization
8a924beaa2 is described below

commit 8a924beaa207b43fd0eb7466c8d31ed90741fa7b
Author: James Bognar <[email protected]>
AuthorDate: Fri Nov 7 16:50:14 2025 -0500

    Utility class modernization
---
 .../juneau/common/reflect/AnnotationProvider.java  |   4 +-
 .../apache/juneau/common/reflect/ClassInfo.java    |  93 +--
 .../juneau/common/reflect/ConstructorInfo.java     |  42 +-
 .../juneau/common/reflect/ExecutableInfo.java      |   2 +-
 .../apache/juneau/common/reflect/FieldInfo.java    |  35 +-
 .../apache/juneau/common/reflect/MethodInfo.java   |  32 +-
 .../apache/juneau/common/reflect/PackageInfo.java  |  30 +-
 .../juneau/common/reflect/ReflectionMap2.java      | 763 +++++++++++++++++++++
 .../java/org/apache/juneau/cp/BeanCreator.java     |   2 +-
 .../java/org/apache/juneau/swap/ObjectSwap.java    |   4 +-
 .../org/apache/juneau/rest/client/RestClient.java  |   4 +
 .../java/org/apache/juneau/http/HttpHeaders.java   |   2 +
 .../java/org/apache/juneau/http/HttpParts.java     |   2 +
 .../org/apache/juneau/http/part/BasicPart.java     |   2 +
 .../apache/juneau/rest/debug/DebugEnablement.java  |   6 +-
 .../juneau/rest/debug/DebugEnablementMap.java      |   4 +-
 .../juneau/common/reflect/ClassInfo_Test.java      |   6 +-
 .../juneau/common/reflect/ConstructorInfoTest.java |   4 +-
 .../juneau/common/reflect/FieldInfo_Test.java      |   4 +-
 .../juneau/common/reflect/MethodInfo_Test.java     |   4 +-
 .../java/org/apache/juneau/cp/BeanStore_Test.java  |   2 +-
 scripts/build-and-test.py                          |  37 +-
 22 files changed, 944 insertions(+), 140 deletions(-)

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 dfdd8c281d..54ef0c396f 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
@@ -209,7 +209,7 @@ public class AnnotationProvider {
         */
        public static class Builder {
                boolean disableCaching;
-               ReflectionMap.Builder<Annotation> runtimeAnnotations = 
ReflectionMap.create(Annotation.class);
+               ReflectionMap2.Builder<Annotation> runtimeAnnotations = 
ReflectionMap2.create(Annotation.class);
 
                Builder() {
                        disableCaching = DISABLE_ANNOTATION_CACHING;
@@ -407,7 +407,7 @@ public class AnnotationProvider {
        private final Cache<Method,List<AnnotationInfo<Annotation>>> 
methodAnnotations;
        private final Cache<Field,List<AnnotationInfo<Annotation>>> 
fieldAnnotations;
        private final Cache<Constructor<?>,List<AnnotationInfo<Annotation>>> 
constructorAnnotations;
-       private final ReflectionMap<Annotation> runtimeAnnotations;
+       private final ReflectionMap2<Annotation> runtimeAnnotations;
        // @formatter:on
 
        /**
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 99549929a0..ae91c751a9 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
@@ -120,63 +120,57 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
        /**
         * Returns a class info wrapper around the specified class type.
         *
-        * @param c The class type.
-        * @return The constructed class info, or <jk>null</jk> if the type was 
<jk>null</jk>.
+        * @param inner The class type.
+        * @return The constructed class info.
         */
-       public static ClassInfo of(Class<?> c) {
-               if (c == null)
-                       return null;
-               return CACHE.get(c, () -> new ClassInfo(c, c));
+       public static ClassInfo of(Class<?> inner) {
+               return CACHE.get(inner, () -> new ClassInfo(inner, inner));
        }
 
        /**
         * Returns a class info wrapper around the specified class type.
         *
-        * @param c The class type.
-        * @param t The generic type (if parameterized type).
-        * @return The constructed class info, or <jk>null</jk> if the type was 
<jk>null</jk>.
+        * @param inner The class type.
+        * @param innerType The generic type (if parameterized type).
+        * @return The constructed class info.
         */
-       public static ClassInfo of(Class<?> c, Type t) {
-               if (c == t)
-                       return of(c);
-               return new ClassInfo(c, t);
+       public static ClassInfo of(Class<?> inner, Type innerType) {
+               if (inner == innerType)
+                       return of(inner);
+               return new ClassInfo(inner, innerType);
        }
 
        /**
         * Same as using the constructor, but operates on an object instance.
         *
-        * @param o The class instance.
-        * @return The constructed class info, or <jk>null</jk> if the object 
was <jk>null</jk>.
+        * @param object The class instance.
+        * @return The constructed class info.
         */
-       public static ClassInfo of(Object o) {
-               return of(o == null ? null : o instanceof Class ? (Class<?>)o : 
o.getClass());
+       public static ClassInfo of(Object object) {
+               return of(object instanceof Class ? (Class<?>)object : 
object.getClass());
        }
 
        /**
         * Returns a class info wrapper around the specified class type.
         *
-        * @param t The class type.
-        * @return The constructed class info, or <jk>null</jk> if the type was 
<jk>null</jk>.
+        * @param innerType The class type.
+        * @return The constructed class info.
         */
-       public static ClassInfo of(Type t) {
-               if (t == null)
-                       return null;
-               if (t instanceof Class)
-                       return of((Class<?>)t);
-               return new ClassInfo(toClass(t), t);
+       public static ClassInfo of(Type innerType) {
+               if (innerType instanceof Class)
+                       return of((Class<?>)innerType);
+               return new ClassInfo(toClass(innerType), innerType);
        }
 
        /**
         * Same as {@link #of(Object)} but attempts to deproxify the object if 
it's wrapped in a CGLIB proxy.
         *
-        * @param o The class instance.
-        * @return The constructed class info, or <jk>null</jk> if the object 
was <jk>null</jk>.
+        * @param object The class instance.
+        * @return The constructed class info.
         */
-       public static ClassInfo ofProxy(Object o) {
-               if (o == null)
-                       return null;
-               Class<?> c = getProxyFor(o);
-               return c == null ? ClassInfo.of(o) : ClassInfo.of(c);
+       public static ClassInfo ofProxy(Object object) {
+               Class<?> inner = getProxyFor(object);
+               return inner == null ? ClassInfo.of(object) : 
ClassInfo.of(inner);
        }
 
        private final Type innerType;  // The underlying Type object (may be 
Class, ParameterizedType, GenericArrayType, etc.).
@@ -223,12 +217,13 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         */
        protected ClassInfo(Class<?> inner, Type innerType) {
                super(inner == null ? 0 : inner.getModifiers());
+               assertArg(inner != null || innerType != null, "At least one of 
inner or innerType must be specified.");
                this.innerType = innerType;
                this.inner = inner;
                this.isParameterizedType = innerType == null ? false : 
(innerType instanceof ParameterizedType);
                this.dimensions = memoize(this::findDimensions);
                this.componentType = memoize(this::findComponentType);
-               this.packageInfo = memoize(() -> opt(inner).map(x -> 
PackageInfo.of(x.getPackage())).orElse(null));
+               this.packageInfo = memoize(() -> opt(inner).map(x -> 
x.getPackage()).filter(p -> p != null).map(PackageInfo::of).orElse(null));  // 
PackageInfo may be null for primitive types and arrays.
                this.parents = memoize(this::findParents);
                this.declaredAnnotations = memoize(() -> (List)opt(inner).map(x 
-> u(l(x.getDeclaredAnnotations()))).orElse(liste()).stream().map(a -> 
AnnotationInfo.of(this, a)).toList());
                this.fullName = memoize(() -> getNameFormatted(FULL, true, '$', 
BRACKETS));
@@ -435,7 +430,10 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The declaring class, or <jk>null</jk> if this class is not a 
member of another class.
         */
        public ClassInfo getDeclaringClass() {
-               return inner == null ? null : of(inner.getDeclaringClass());
+               if (inner == null)
+                       return null;
+               Class<?> dc = inner.getDeclaringClass();
+               return dc == null ? null : of(dc);
        }
 
        /**
@@ -572,7 +570,10 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The enclosing class, or <jk>null</jk> if this is a top-level 
class.
         */
        public ClassInfo getEnclosingClass() {
-               return inner == null ? null : of(inner.getEnclosingClass());
+               if (inner == null)
+                       return null;
+               Class<?> ec = inner.getEnclosingClass();
+               return ec == null ? null : of(ec);
        }
 
        /**
@@ -1387,7 +1388,12 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return
         *      The parent class, or <jk>null</jk> if the class has no parent.
         */
-       public ClassInfo getSuperclass() { return inner == null ? null : 
of(inner.getSuperclass()); }
+       public ClassInfo getSuperclass() {
+               if (inner == null)
+                       return null;
+               Class<?> sc = inner.getSuperclass();
+               return sc == null ? null : of(sc);
+       }
 
        /**
         * Returns the nest host of this class.
@@ -1399,7 +1405,10 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The nest host of this class.
         */
        public ClassInfo getNestHost() {
-               return inner == null ? null : of(inner.getNestHost());
+               if (inner == null)
+                       return null;
+               Class<?> nh = inner.getNestHost();
+               return nh == null ? null : of(nh);
        }
 
        /**
@@ -2230,7 +2239,10 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return A {@link ClassInfo} representing an array type whose 
component type is this class.
         */
        public ClassInfo arrayType() {
-               return inner == null ? null : of(inner.arrayType());
+               if (inner == null)
+                       return null;
+               Class<?> at = inner.arrayType();
+               return at == null ? null : of(at);
        }
 
        /**
@@ -2243,7 +2255,10 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The {@link ClassInfo} representing the component type, or 
<jk>null</jk> if this class does not represent an array type.
         */
        public ClassInfo componentType() {
-               return inner == null ? null : of(inner.componentType());
+               if (inner == null)
+                       return null;
+               Class<?> ct = inner.componentType();
+               return ct == null ? null : of(ct);
        }
 
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
index 179b63e1cc..6b3b874af9 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
@@ -16,6 +16,8 @@
  */
 package org.apache.juneau.common.reflect;
 
+import static org.apache.juneau.common.utils.AssertionUtils.*;
+
 import java.lang.reflect.*;
 
 import org.apache.juneau.common.utils.*;
@@ -31,38 +33,36 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
         * Convenience method for instantiating a {@link ConstructorInfo};
         *
         * @param declaringClass The class that declares this method.
-        * @param c The constructor being wrapped.
-        * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if 
the method was null;
+        * @param inner The constructor being wrapped.
+        * @return A new {@link ConstructorInfo} object.
         */
-       public static ConstructorInfo of(ClassInfo declaringClass, 
Constructor<?> c) {
-               if (c == null)
-                       return null;
-               return ClassInfo.of(declaringClass).getConstructorInfo(c);
+       public static ConstructorInfo of(ClassInfo declaringClass, 
Constructor<?> inner) {
+               assertArgNotNull("declaringClass", declaringClass);
+               return declaringClass.getConstructorInfo(inner);
        }
 
        /**
         * Convenience method for instantiating a {@link ConstructorInfo};
         *
-        * @param c The constructor being wrapped.
-        * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if 
the method was null;
+        * @param inner The constructor being wrapped.
+        * @return A new {@link ConstructorInfo} object.
         */
-       public static ConstructorInfo of(Constructor<?> c) {
-               if (c == null)
-                       return null;
-               return 
ClassInfo.of(c.getDeclaringClass()).getConstructorInfo(c);
+       public static ConstructorInfo of(Constructor<?> inner) {
+               assertArgNotNull("inner", inner);
+               return 
ClassInfo.of(inner.getDeclaringClass()).getConstructorInfo(inner);
        }
 
-       private final Constructor<?> c;
+       private final Constructor<?> inner;
 
        /**
         * Constructor.
         *
         * @param declaringClass The class that declares this method.
-        * @param c The constructor being wrapped.
+        * @param inner The constructor being wrapped.
         */
-       protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> c) {
-               super(declaringClass, c);
-               this.c = c;
+       protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> 
inner) {
+               super(declaringClass, inner);
+               this.inner = inner;
        }
 
        @Override /* Overridden from ExecutableInfo */
@@ -83,7 +83,7 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
         *      met or call to {@link Constructor#setAccessible(boolean)} 
throws a security exception.
         */
        public ConstructorInfo accessible(Visibility v) {
-               if (v.transform(c) == null)
+               if (v.transform(inner) == null)
                        return null;
                return this;
        }
@@ -113,7 +113,7 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
         */
        @SuppressWarnings("unchecked")
        public <T> Constructor<T> inner() {
-               return (Constructor<T>)c;
+               return (Constructor<T>)inner;
        }
 
        /**
@@ -127,7 +127,7 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
        @SuppressWarnings("unchecked")
        public <T> T newInstance(Object...args) throws ExecutableException {
                try {
-                       return (T)c.newInstance(args);
+                       return (T)inner.newInstance(args);
                } catch (InvocationTargetException e) {
                        throw new ExecutableException(e.getTargetException());
                } catch (Exception e) {
@@ -150,7 +150,7 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
         * @throws ExecutableException Exception occurred on invoked 
constructor/method/field.
         */
        public <T> T newInstanceLenient(Object...args) throws 
ExecutableException {
-               return 
newInstance(ClassUtils.getMatchingArgs(c.getParameterTypes(), args));
+               return 
newInstance(ClassUtils.getMatchingArgs(inner.getParameterTypes(), args));
        }
        
//-----------------------------------------------------------------------------------------------------------------
        // Annotatable interface methods
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
index d506fb79bf..7a12148879 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
@@ -58,7 +58,7 @@ public abstract class ExecutableInfo extends AccessibleInfo {
         * @param inner The constructor or method that this info represents.
         */
        protected ExecutableInfo(ClassInfo declaringClass, Executable inner) {
-               super(inner, inner.getModifiers());
+               super(inner, assertArgNotNull("inner", inner).getModifiers());
                this.declaringClass = declaringClass;
                this.inner = inner;
                this.isConstructor = inner instanceof Constructor;
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
index 302867c820..827c0f486b 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
@@ -18,9 +18,9 @@ package org.apache.juneau.common.reflect;
 
 import static org.apache.juneau.common.reflect.ClassArrayFormat.*;
 import static org.apache.juneau.common.reflect.ClassNameFormat.*;
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.apache.juneau.common.utils.PredicateUtils.*;
+import static org.apache.juneau.common.utils.AssertionUtils.*;
 import static org.apache.juneau.common.utils.Utils.*;
+import static java.util.Arrays.*;
 
 import java.lang.annotation.*;
 import java.lang.reflect.*;
@@ -28,8 +28,6 @@ import java.util.*;
 import java.util.function.*;
 import java.util.stream.*;
 
-import org.apache.juneau.common.collections.*;
-
 /**
  * Lightweight utility class for introspecting information about a field.
  *
@@ -45,24 +43,22 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
         *
         * @param declaringClass The class that declares this method.
         * @param inner The field being wrapped.
-        * @return A new {@link FieldInfo} object, or <jk>null</jk> if the 
field was null.
+        * @return A new {@link FieldInfo} object.
         */
        public static FieldInfo of(ClassInfo declaringClass, Field inner) {
-               if (inner == null)
-                       return null;
-               return ClassInfo.of(declaringClass).getFieldInfo(inner);
+               assertArgNotNull("declaringClass", declaringClass);
+               return declaringClass.getFieldInfo(inner);
        }
 
        /**
         * Convenience method for instantiating a {@link FieldInfo};
         *
-        * @param f The field being wrapped.
-        * @return A new {@link FieldInfo} object, or <jk>null</jk> if the 
field was null.
+        * @param inner The field being wrapped.
+        * @return A new {@link FieldInfo} object.
         */
-       public static FieldInfo of(Field f) {
-               if (f == null)
-                       return null;
-               return ClassInfo.of(f.getDeclaringClass()).getFieldInfo(f);
+       public static FieldInfo of(Field inner) {
+               assertArgNotNull("inner", inner);
+               return 
ClassInfo.of(inner.getDeclaringClass()).getFieldInfo(inner);
        }
 
        private final Field inner;
@@ -75,13 +71,14 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
         * Constructor.
         *
         * @param declaringClass The class that declares this method.
-        * @param f The field being wrapped.
+        * @param inner The field being wrapped.
         */
-       protected FieldInfo(ClassInfo declaringClass, Field f) {
-               super(f, f.getModifiers());
+       protected FieldInfo(ClassInfo declaringClass, Field inner) {
+               super(inner, inner.getModifiers());
+               assertArgNotNull("inner", inner);
                this.declaringClass = declaringClass;
-               this.inner = f;
-               this.type = memoize(() -> ClassInfo.of(f.getType()));
+               this.inner = inner;
+               this.type = memoize(() -> ClassInfo.of(inner.getType()));
                this.declaredAnnotations = memoize(() -> 
stream(inner.getAnnotations()).map(a -> AnnotationInfo.of(this, a)).toList());
                this.fullName = memoize(this::findFullName);
        }
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
index b469cecf83..df85eb8552 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
@@ -42,38 +42,34 @@ public class MethodInfo extends ExecutableInfo implements 
Comparable<MethodInfo>
         * Convenience method for instantiating a {@link MethodInfo};
         *
         * @param declaringClass The class that declares this method.
-        * @param m The method being wrapped.
-        * @return A new {@link MethodInfo} object, or <jk>null</jk> if the 
method was null;
+        * @param inner The method being wrapped.
+        * @return A new {@link MethodInfo} object.
         */
-       public static MethodInfo of(Class<?> declaringClass, Method m) {
-               if (m == null)
-                       return null;
-               return ClassInfo.of(declaringClass).getMethodInfo(m);
+       public static MethodInfo of(Class<?> declaringClass, Method inner) {
+               return ClassInfo.of(declaringClass).getMethodInfo(inner);
        }
 
        /**
         * Convenience method for instantiating a {@link MethodInfo};
         *
         * @param declaringClass The class that declares this method.
-        * @param m The method being wrapped.
-        * @return A new {@link MethodInfo} object, or <jk>null</jk> if the 
method was null;
+        * @param inner The method being wrapped.
+        * @return A new {@link MethodInfo} object.
         */
-       public static MethodInfo of(ClassInfo declaringClass, Method m) {
-               if (m == null)
-                       return null;
-               return declaringClass.getMethodInfo(m);
+       public static MethodInfo of(ClassInfo declaringClass, Method inner) {
+               assertArgNotNull("declaringClass", declaringClass);
+               return declaringClass.getMethodInfo(inner);
        }
 
        /**
         * Convenience method for instantiating a {@link MethodInfo};
         *
-        * @param m The method being wrapped.
-        * @return A new {@link MethodInfo} object, or <jk>null</jk> if the 
method was null;
+        * @param inner The method being wrapped.
+        * @return A new {@link MethodInfo} object.
         */
-       public static MethodInfo of(Method m) {
-               if (m == null)
-                       return null;
-               return ClassInfo.of(m.getDeclaringClass()).getMethodInfo(m);
+       public static MethodInfo of(Method inner) {
+               assertArgNotNull("inner", inner);
+               return 
ClassInfo.of(inner.getDeclaringClass()).getMethodInfo(inner);
        }
 
        private final Method inner;
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/PackageInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/PackageInfo.java
index 1ecf8889f0..9d32103004 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/PackageInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/PackageInfo.java
@@ -16,6 +16,7 @@
  */
 package org.apache.juneau.common.reflect;
 
+import static org.apache.juneau.common.utils.AssertionUtils.*;
 import static org.apache.juneau.common.utils.ClassUtils.*;
 import static org.apache.juneau.common.utils.CollectionUtils.*;
 import static org.apache.juneau.common.utils.Utils.*;
@@ -48,33 +49,31 @@ public class PackageInfo implements Annotatable {
        /**
         * Returns a package info wrapper around the specified package object.
         *
-        * @param inner The package object.  Can be <jk>null</jk>.
-        * @return A package info wrapper, or <jk>null</jk> if the parameter 
was null.
+        * @param inner The package object.
+        * @return A package info wrapper.
         */
        public static PackageInfo of(Package inner) {
-               if (inner == null)
-                       return null;
                return CACHE.get(inner, () -> new PackageInfo(inner));
        }
 
        /**
         * Returns a package info wrapper around the package of the specified 
class.
         *
-        * @param c The class whose package to retrieve.  Can be <jk>null</jk>.
-        * @return A package info wrapper, or <jk>null</jk> if the parameter 
was null or has no package.
+        * @param childClass The class whose package to retrieve.
+        * @return A package info wrapper.
         */
-       public static PackageInfo of(Class<?> c) {
-               return c == null ? null : of(c.getPackage());
+       public static PackageInfo of(Class<?> childClass) {
+               return of(childClass.getPackage());
        }
 
        /**
         * Returns a package info wrapper around the package of the specified 
class info.
         *
-        * @param ci The class info whose package to retrieve.  Can be 
<jk>null</jk>.
-        * @return A package info wrapper, or <jk>null</jk> if the parameter 
was null or has no package.
+        * @param childClass The class info whose package to retrieve.
+        * @return A package info wrapper.
         */
-       public static PackageInfo of(ClassInfo ci) {
-               return ci == null ? null : ci.getPackage();
+       public static PackageInfo of(ClassInfo childClass) {
+               return childClass.getPackage();
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -87,10 +86,11 @@ public class PackageInfo implements Annotatable {
        /**
         * Constructor.
         *
-        * @param p The package object.
+        * @param inner The package object.
         */
-       protected PackageInfo(Package p) {
-               this.inner = p;
+       protected PackageInfo(Package inner) {
+               assertArgNotNull("inner", inner);
+               this.inner = inner;
                this.annotations = memoize(() -> opt(inner).map(pkg -> 
stream(pkg.getAnnotations()).flatMap(a -> stream(splitRepeated(a))).map(a -> 
AnnotationInfo.of(this, a)).toList()).orElse(liste()));
        }
 
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
new file mode 100644
index 0000000000..25cc709bed
--- /dev/null
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
@@ -0,0 +1,763 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.common.reflect;
+
+import static java.lang.Character.*;
+import static org.apache.juneau.common.utils.CollectionUtils.*;
+import static org.apache.juneau.common.utils.StringUtils.*;
+import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+
+import org.apache.juneau.common.utils.*;
+
+/**
+ * Allows arbitrary objects to be mapped to classes and methods base on 
class/method name keys.
+ *
+ * <p>
+ * The valid pattern matches are:
+ * <ul class='spaced-list'>
+ *  <li>Classes:
+ *             <ul>
+ *                     <li>Fully qualified:
+ *                             <ul>
+ *                                     <li><js>"com.foo.MyClass"</js>
+ *                             </ul>
+ *                     <li>Fully qualified inner class:
+ *                             <ul>
+ *                                     
<li><js>"com.foo.MyClass$Inner1$Inner2"</js>
+ *                             </ul>
+ *                     <li>Simple:
+ *                             <ul>
+ *                                     <li><js>"MyClass"</js>
+ *                             </ul>
+ *                     <li>Simple inner:
+ *                             <ul>
+ *                                     <li><js>"MyClass$Inner1$Inner2"</js>
+ *                                     <li><js>"Inner1$Inner2"</js>
+ *                                     <li><js>"Inner2"</js>
+ *                             </ul>
+ *             </ul>
+ *     <li>Methods:
+ *             <ul>
+ *                     <li>Fully qualified with args:
+ *                             <ul>
+ *                                     
<li><js>"com.foo.MyClass.myMethod(String,int)"</js>
+ *                                     
<li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
+ *                                     
<li><js>"com.foo.MyClass.myMethod()"</js>
+ *                             </ul>
+ *                     <li>Fully qualified:
+ *                             <ul>
+ *                                     <li><js>"com.foo.MyClass.myMethod"</js>
+ *                             </ul>
+ *                     <li>Simple with args:
+ *                             <ul>
+ *                                     
<li><js>"MyClass.myMethod(String,int)"</js>
+ *                                     
<li><js>"MyClass.myMethod(java.lang.String,int)"</js>
+ *                                     <li><js>"MyClass.myMethod()"</js>
+ *                             </ul>
+ *                     <li>Simple:
+ *                             <ul>
+ *                                     <li><js>"MyClass.myMethod"</js>
+ *                             </ul>
+ *                     <li>Simple inner class:
+ *                             <ul>
+ *                                     
<li><js>"MyClass$Inner1$Inner2.myMethod"</js>
+ *                                     <li><js>"Inner1$Inner2.myMethod"</js>
+ *                                     <li><js>"Inner2.myMethod"</js>
+ *                             </ul>
+ *             </ul>
+ *     <li>Fields:
+ *             <ul>
+ *                     <li>Fully qualified:
+ *                             <ul>
+ *                                     <li><js>"com.foo.MyClass.myField"</js>
+ *                             </ul>
+ *                     <li>Simple:
+ *                             <ul>
+ *                                     <li><js>"MyClass.myField"</js>
+ *                             </ul>
+ *                     <li>Simple inner class:
+ *                             <ul>
+ *                                     
<li><js>"MyClass$Inner1$Inner2.myField"</js>
+ *                                     <li><js>"Inner1$Inner2.myField"</js>
+ *                                     <li><js>"Inner2.myField"</js>
+ *                             </ul>
+ *             </ul>
+ *     <li>Constructors:
+ *             <ul>
+ *                     <li>Fully qualified with args:
+ *                             <ul>
+ *                                     
<li><js>"com.foo.MyClass(String,int)"</js>
+ *                                     
<li><js>"com.foo.MyClass(java.lang.String,int)"</js>
+ *                                     <li><js>"com.foo.MyClass()"</js>
+ *                             </ul>
+ *                     <li>Simple with args:
+ *                             <ul>
+ *                                     <li><js>"MyClass(String,int)"</js>
+ *                                     
<li><js>"MyClass(java.lang.String,int)"</js>
+ *                                     <li><js>"MyClass()"</js>
+ *                             </ul>
+ *                     <li>Simple inner class:
+ *                             <ul>
+ *                                     <li><js>"MyClass$Inner1$Inner2()"</js>
+ *                                     <li><js>"Inner1$Inner2()"</js>
+ *                                     <li><js>"Inner2()"</js>
+ *                             </ul>
+ *             </ul>
+ *     <li>A comma-delimited list of anything on this list.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * </ul>
+ *
+ * @param <V> The type of object in this map.
+ */
+public class ReflectionMap2<V> {
+
+       /**
+        * Builder class.
+        * @param <V> The type of object in this map.
+        */
+       public static class Builder<V> {
+               final List<ClassEntry<V>> classEntries;
+               final List<MethodEntry<V>> methodEntries;
+               final List<FieldEntry<V>> fieldEntries;
+               final List<ConstructorEntry<V>> constructorEntries;
+
+               /**
+                * Constructor.
+                */
+               protected Builder() {
+                       classEntries = list();
+                       methodEntries = list();
+                       fieldEntries = list();
+                       constructorEntries = list();
+               }
+
+               /**
+                * Copy constructor.
+                *
+                * @param copyFrom The builder being copied.
+                */
+               protected Builder(Builder<V> copyFrom) {
+                       classEntries = copyOf(copyFrom.classEntries);
+                       methodEntries = copyOf(copyFrom.methodEntries);
+                       fieldEntries = copyOf(copyFrom.fieldEntries);
+                       constructorEntries = 
copyOf(copyFrom.constructorEntries);
+               }
+
+               /**
+                * Adds a mapping to this builder.
+                *
+                * @param key
+                *      The mapping key.
+                *      <br>Can be any of the following:
+                *      <ul>
+                *              <li>Full class name (e.g. 
<js>"com.foo.MyClass"</js>).
+                *              <li>Simple class name (e.g. <js>"MyClass"</js>).
+                *              <li>All classes (e.g. <js>"*"</js>).
+                *              <li>Full method name (e.g. 
<js>"com.foo.MyClass.myMethod"</js>).
+                *              <li>Simple method name (e.g. 
<js>"MyClass.myMethod"</js>).
+                *              <li>A comma-delimited list of anything on this 
list.
+                *      </ul>
+                * @param value The value for this mapping.
+                * @return This object.
+                */
+               public Builder<V> append(String key, V value) {
+                       if (StringUtils.isEmpty(key))
+                               throw runtimeException("Invalid reflection 
signature: [{0}]", key);
+                       try {
+                               splitNames(key, k -> {
+                                       if (k.endsWith(")")) {
+                                               int i = k.substring(0, 
k.indexOf('(')).lastIndexOf('.');
+                                               if (i == -1 || 
isUpperCase(k.charAt(i + 1))) {
+                                                       
constructorEntries.add(new ConstructorEntry<>(k, value));
+                                               } else {
+                                                       methodEntries.add(new 
MethodEntry<>(k, value));
+                                               }
+                                       } else {
+                                               int i = k.lastIndexOf('.');
+                                               if (i == -1) {
+                                                       classEntries.add(new 
ClassEntry<>(k, value));
+                                               } else if 
(isUpperCase(k.charAt(i + 1))) {
+                                                       classEntries.add(new 
ClassEntry<>(k, value));
+                                                       fieldEntries.add(new 
FieldEntry<>(k, value));
+                                               } else {
+                                                       methodEntries.add(new 
MethodEntry<>(k, value));
+                                                       fieldEntries.add(new 
FieldEntry<>(k, value));
+                                               }
+                                       }
+                               });
+                       } catch (@SuppressWarnings("unused") 
IndexOutOfBoundsException e) {
+                               throw runtimeException("Invalid reflection 
signature: [{0}]", key);
+                       }
+
+                       return this;
+               }
+
+               /**
+                * Create new instance of {@link ReflectionMap2} based on the 
contents of this builder.
+                *
+                * @return A new {@link ReflectionMap2} object.
+                */
+               public ReflectionMap2<V> build() {
+                       return new ReflectionMap2<>(this);
+               }
+
+               /**
+                * Creates a copy of this builder.
+                *
+                * @return A copy of this builder.
+                */
+               public Builder<V> copy() {
+                       return new Builder<>(this);
+               }
+       }
+
+       static class ClassEntry<V> {
+               final String simpleName, fullName;
+               final V value;
+
+               ClassEntry(String name, V value) {
+                       this.simpleName = simpleClassName(name);
+                       this.fullName = name;
+                       this.value = value;
+               }
+
+               public boolean matches(Class<?> c) {
+                       if (c == null)
+                               return false;
+                       return classMatches(simpleName, fullName, c);
+               }
+
+               @Override
+               public String toString() {
+                       // @formatter:off
+                       return mapb().filtered()
+                               .add("simpleName", simpleName)
+                               .add("fullName", fullName)
+                               .add("value", value)
+                               .toString();
+                       // @formatter:on
+               }
+       }
+
+       static class ConstructorEntry<V> {
+               String simpleClassName, fullClassName, args[];
+               V value;
+
+               ConstructorEntry(String name, V value) {
+                       int i = name.indexOf('(');
+                       this.args = splita(name.substring(i + 1, name.length() 
- 1));
+                       name = name.substring(0, i).trim();
+                       this.simpleClassName = simpleClassName(name);
+                       this.fullClassName = name;
+                       this.value = value;
+               }
+
+               public boolean matches(Constructor<?> m) {
+                       if (m == null)
+                               return false;
+                       var c = m.getDeclaringClass();
+                       return classMatches(simpleClassName, fullClassName, c) 
&& (argsMatch(args, m.getParameterTypes()));
+               }
+
+               @Override
+               public String toString() {
+                       // @formatter:off
+                       return mapb().filtered()
+                               .add("simpleClassName", simpleClassName)
+                               .add("fullClassName", fullClassName)
+                               .add("args", args)
+                               .add("value", value)
+                               .toString();
+                       // @formatter:on
+               }
+       }
+
+       static class FieldEntry<V> {
+               String simpleClassName, fullClassName, fieldName;
+               V value;
+
+               FieldEntry(String name, V value) {
+                       int i = name.lastIndexOf('.');
+                       var s1 = name.substring(0, i);
+                       var s2 = name.substring(i + 1);
+                       this.simpleClassName = simpleClassName(s1);
+                       this.fullClassName = s1;
+                       this.fieldName = s2;
+                       this.value = value;
+               }
+
+               public boolean matches(Field f) {
+                       if (f == null)
+                               return false;
+                       var c = f.getDeclaringClass();
+                       return classMatches(simpleClassName, fullClassName, c) 
&& (eq(f.getName(), fieldName));
+               }
+
+               @Override
+               public String toString() {
+                       // @formatter:off
+                       return mapb().filtered()
+                               .add("simpleClassName", simpleClassName)
+                               .add("fullClassName", fullClassName)
+                               .add("fieldName", fieldName)
+                               .add("value", value)
+                               .toString();
+                       // @formatter:on
+               }
+       }
+
+       static class MethodEntry<V> {
+               String simpleClassName, fullClassName, methodName, args[];
+               V value;
+
+               MethodEntry(String name, V value) {
+                       int i = name.indexOf('(');
+                       this.args = i == -1 ? null : 
splitMethodArgs(name.substring(i + 1, name.length() - 1));
+                       if (nn(args)) {
+                               for (int j = 0; j < args.length; j++) {
+
+                                       // Strip off generic parameters.
+                                       int k = args[j].indexOf('<');
+                                       if (k > 0)
+                                               args[j] = args[j].substring(0, 
k);
+
+                                       // Convert from xxx[][] to [[Lxxx; 
notation.
+                                       if (args[j].endsWith("[]")) {
+                                               int l = 0;
+                                               while (args[j].endsWith("[]")) {
+                                                       l++;
+                                                       args[j] = 
args[j].substring(0, args[j].length() - 2);
+                                               }
+                                               var sb = new 
StringBuilder(args[j].length() + l + 2);
+                                               for (int m = 0; m < l; m++)
+                                                       sb.append('[');
+                                               
sb.append('L').append(args[j]).append(';');
+                                               args[j] = sb.toString();
+                                       }
+                               }
+                       }
+                       name = i == -1 ? name : name.substring(0, i);
+                       i = name.lastIndexOf('.');
+                       var s1 = name.substring(0, i).trim();
+                       var s2 = name.substring(i + 1).trim();
+                       this.simpleClassName = simpleClassName(s1);
+                       this.fullClassName = s1;
+                       this.methodName = s2;
+                       this.value = value;
+               }
+
+               public boolean matches(Method m) {
+                       if (m == null)
+                               return false;
+                       var c = m.getDeclaringClass();
+                       // @formatter:off
+                       return
+                               classMatches(simpleClassName, fullClassName, c)
+                               && (eq(m.getName(), methodName))
+                               && (argsMatch(args, m.getParameterTypes()));
+                       // @formatter:on
+               }
+
+               @Override
+               public String toString() {
+                       // @formatter:off
+                       return mapb().filtered()
+                               .add("simpleClassName", simpleClassName)
+                               .add("fullClassName", fullClassName)
+                               .add("methodName", methodName)
+                               .add("args", args)
+                               .add("value", value)
+                               .toString();
+                       // @formatter:on
+               }
+       }
+
+       /**
+        * Static builder creator.
+        *
+        * @param <V> The type of object in this map.
+        * @param c The type of object in this map.
+        * @return A new instance of this object.
+        */
+       public static <V> Builder<V> create(Class<V> c) {
+               return new Builder<>();
+       }
+
+       static boolean argsMatch(String[] names, Class<?>[] args) {
+               if (names == null)
+                       return true;
+               if (names.length != args.length)
+                       return false;
+               for (int i = 0; i < args.length; i++) {
+                       var n = names[i];
+                       var a = args[i];
+                       if (! (eq(n, a.getSimpleName()) || eq(n, a.getName())))
+                               return false;
+               }
+               return true;
+       }
+
+       static boolean classMatches(String simpleName, String fullName, 
Class<?> c) {
+               // For class 
org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder
+               // c.getSimpleName() == "Builder"
+               // c.getFullName() == 
"org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder"
+               // c.getPackage() == "org.apache.juneau.a.rttests"
+               var cSimple = scn(c);
+               var cFull = cn(c);
+               if (eq(simpleName, cSimple) || eq(fullName, cFull) || 
"*".equals(simpleName))
+                       return true;
+               if (cFull.indexOf('$') != -1) {
+                       var p = c.getPackage();
+                       if (nn(p))
+                               cFull = cFull.substring(p.getName().length() + 
1);
+                       if (eq(simpleName, cFull))
+                               return true;
+                       int i = cFull.indexOf('$');
+                       while (i != -1) {
+                               cFull = cFull.substring(i + 1);
+                               if (eq(simpleName, cFull))
+                                       return true;
+                               i = cFull.indexOf('$');
+                       }
+               }
+               return false;
+       }
+
+       static String simpleClassName(String name) {
+               int i = name.indexOf('.');
+               if (i == -1)
+                       return name;
+               return null;
+       }
+
+       static void splitNames(String key, Consumer<String> consumer) {
+               if (key.indexOf(',') == -1) {
+                       consumer.accept(key);
+               } else {
+                       int m = 0;
+                       boolean escaped = false;
+                       for (int i = 0; i < key.length(); i++) {
+                               char c = key.charAt(i);
+                               if (c == '(')
+                                       escaped = true;
+                               else if (c == ')')
+                                       escaped = false;
+                               else if (c == ',' && ! escaped) {
+                                       consumer.accept(key.substring(m, 
i).trim());
+                                       m = i + 1;
+                               }
+                       }
+                       consumer.accept(key.substring(m).trim());
+               }
+       }
+
+       final List<ClassEntry<V>> classEntries;
+
+       final List<MethodEntry<V>> methodEntries;
+
+       final List<FieldEntry<V>> fieldEntries;
+
+       final List<ConstructorEntry<V>> constructorEntries;
+
+       /**
+        * Constructor.
+        *
+        * @param b Initializer object.
+        */
+       protected ReflectionMap2(Builder<V> b) {
+               this.classEntries = u(copyOf(b.classEntries));
+               this.methodEntries = u(copyOf(b.methodEntries));
+               this.fieldEntries =  u(copyOf(b.fieldEntries));
+               this.constructorEntries = u(copyOf(b.constructorEntries));
+       }
+
+       public Stream<V> findMatching(Class<?> c) {
+               return classEntries.stream().filter(x -> x.matches(c)).map(x -> 
x.value);
+       }
+
+       public Stream<V> findMatching(Method m) {
+               return methodEntries.stream().filter(x -> x.matches(m)).map(x 
-> x.value);
+       }
+
+       public Stream<V> findMatching(Field f) {
+               return fieldEntries.stream().filter(x -> x.matches(f)).map(x -> 
x.value);
+       }
+
+       public Stream<V> findMatching(Constructor c) {
+               return constructorEntries.stream().filter(x -> 
x.matches(c)).map(x -> x.value);
+       }
+
+       /**
+        * Finds first value in this map that matches the specified class.
+        *
+        * @param c The class to test for.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Class<?> c) {
+               return find(c, null);
+       }
+
+       /**
+        * Finds first value in this map that matches the specified class.
+        *
+        * @param c The class to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Class<?> c, Class<? extends V> ofType) {
+               return opt(findMatching(c).filter(x -> x != null && (ofType == 
null || ofType.isInstance(x))).findFirst().orElse(null));
+       }
+
+       /**
+        * Finds first value in this map that matches the specified constructor.
+        *
+        * @param c The constructor to test for.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Constructor<?> c) {
+               return find(c, null);
+       }
+
+       /**
+        * Finds first value in this map that matches the specified constructor.
+        *
+        * @param c The constructor to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Constructor<?> c, Class<? extends V> ofType) {
+               return opt(findMatching(c).filter(x -> x != null && (ofType == 
null || ofType.isInstance(x))).findFirst().orElse(null));
+       }
+
+       /**
+        * Finds first value in this map that matches the specified field.
+        *
+        * @param f The field to test for.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Field f) {
+               return find(f, null);
+       }
+
+       /**
+        * Finds first value in this map that matches the specified field.
+        *
+        * @param f The field to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Field f, Class<? extends V> ofType) {
+               return opt(findMatching(f).filter(x -> x != null && (ofType == 
null || ofType.isInstance(x))).findFirst().orElse(null));
+       }
+
+       /**
+        * Finds first value in this map that matches the specified method.
+        *
+        * @param m The method to test for.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Method m) {
+               return find(m, null);
+       }
+
+       /**
+        * Finds first value in this map that matches the specified method.
+        *
+        * @param m The method to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching object.  Never <jk>null</jk>.
+        */
+       public Optional<V> find(Method m, Class<? extends V> ofType) {
+               return opt(findMatching(m).filter(x -> x != null && (ofType == 
null || ofType.isInstance(x))).findFirst().orElse(null));
+       }
+
+       /**
+        * Finds all values in this map that match the specified class.
+        *
+        * @param c The class to test for.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Class<?> c) {
+               return findAll(c, null);
+       }
+
+       /**
+        * Finds all values in this map that match the specified class.
+        *
+        * @param c The class to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Class<?> c, Class<? extends V> ofType) {
+               return findMatching(c).filter(x -> ofType == null || 
ofType.isInstance(x)).toList();
+       }
+
+       /**
+        * Finds all values in this map that match the specified constructor.
+        *
+        * @param c The constructor to test for.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Constructor<?> c) {
+               return findAll(c, null);
+       }
+
+       /**
+        * Finds all values in this map that match the specified constructor.
+        *
+        * @param c The constructor to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Constructor<?> c, Class<? extends V> ofType) {
+               return findMatching(c).filter(x -> ofType == null || 
ofType.isInstance(x)).toList();
+       }
+
+       /**
+        * Finds all values in this map that match the specified field.
+        *
+        * @param f The field to test for.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Field f) {
+               return findAll(f, null);
+       }
+
+       /**
+        * Finds all values in this map that match the specified field.
+        *
+        * @param f The field to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Field f, Class<? extends V> ofType) {
+               return findMatching(f).filter(x -> ofType == null || 
ofType.isInstance(x)).toList();
+       }
+
+       /**
+        * Finds all values in this map that match the specified method.
+        *
+        * @param m The method to test for.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Method m) {
+               return findAll(m, null);
+       }
+
+       /**
+        * Finds all values in this map that match the specified method.
+        *
+        * @param m The method to test for.
+        * @param ofType Only return objects of the specified type.
+        * @return The matching objects.  Never <jk>null</jk>.
+        */
+       public List<V> findAll(Method m, Class<? extends V> ofType) {
+               return findMatching(m).filter(x -> ofType == null || 
ofType.isInstance(x)).toList();
+       }
+
+       /**
+        * Finds all values in this map that match the specified class and 
appends them to the specified array.
+        *
+        * @param c The class to test for.
+        * @param ofType Only return objects of the specified type.
+        * @param array The array to append to.
+        * @return The array with matching objects appended.
+        */
+       public V[] appendAll(Class<?> c, Class<? extends V> ofType, V[] array) {
+               var list = findAll(c, ofType);
+               if (list.isEmpty())
+                       return array;
+               var newArray = Arrays.copyOf(array, array.length + list.size());
+               for (int i = 0; i < list.size(); i++)
+                       newArray[array.length + i] = list.get(i);
+               return newArray;
+       }
+
+       /**
+        * Finds all values in this map that match the specified constructor 
and appends them to the specified array.
+        *
+        * @param c The constructor to test for.
+        * @param ofType Only return objects of the specified type.
+        * @param array The array to append to.
+        * @return The array with matching objects appended.
+        */
+       public V[] appendAll(Constructor<?> c, Class<? extends V> ofType, V[] 
array) {
+               var list = findAll(c, ofType);
+               if (list.isEmpty())
+                       return array;
+               var newArray = Arrays.copyOf(array, array.length + list.size());
+               for (int i = 0; i < list.size(); i++)
+                       newArray[array.length + i] = list.get(i);
+               return newArray;
+       }
+
+       /**
+        * Finds all values in this map that match the specified field and 
appends them to the specified array.
+        *
+        * @param f The field to test for.
+        * @param ofType Only return objects of the specified type.
+        * @param array The array to append to.
+        * @return The array with matching objects appended.
+        */
+       public V[] appendAll(Field f, Class<? extends V> ofType, V[] array) {
+               var list = findAll(f, ofType);
+               if (list.isEmpty())
+                       return array;
+               var newArray = Arrays.copyOf(array, array.length + list.size());
+               for (int i = 0; i < list.size(); i++)
+                       newArray[array.length + i] = list.get(i);
+               return newArray;
+       }
+
+       /**
+        * Finds all values in this map that match the specified method and 
appends them to the specified array.
+        *
+        * @param m The method to test for.
+        * @param ofType Only return objects of the specified type.
+        * @param array The array to append to.
+        * @return The array with matching objects appended.
+        */
+       public V[] appendAll(Method m, Class<? extends V> ofType, V[] array) {
+               var list = findAll(m, ofType);
+               if (list.isEmpty())
+                       return array;
+               var newArray = Arrays.copyOf(array, array.length + list.size());
+               for (int i = 0; i < list.size(); i++)
+                       newArray[array.length + i] = list.get(i);
+               return newArray;
+       }
+
+       @Override /* Overridden from Object */
+       public String toString() {
+               // @formatter:off
+               return mapb().filtered()
+                       .add("classEntries", classEntries)
+                       .add("methodEntries", methodEntries)
+                       .add("fieldEntries", fieldEntries)
+                       .add("constructorEntries", constructorEntries)
+                       .toString();
+               // @formatter:on
+       }
+}
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreator.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreator.java
index e5be46b5d2..94fa1594e0 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreator.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreator.java
@@ -388,7 +388,7 @@ public class BeanCreator<T> {
         * @return This object.
         */
        public BeanCreator<T> type(Class<?> value) {
-               type = ClassInfo.of(value);
+               type = opt(value).map(x -> ClassInfo.of(x)).orElse(null);
                return this;
        }
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/ObjectSwap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/ObjectSwap.java
index 74c6acd569..0873bccade 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/ObjectSwap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/ObjectSwap.java
@@ -140,8 +140,8 @@ public abstract class ObjectSwap<T,S> {
        protected ObjectSwap(Class<T> normalClass, Class<?> swapClass) {
                this.normalClass = normalClass;
                this.swapClass = swapClass;
-               normalClassInfo = ClassInfo.of(normalClass);
-               swapClassInfo = ClassInfo.of(swapClass);
+               normalClassInfo = opt(normalClass).map(x -> 
ClassInfo.of(x)).orElse(null);
+               swapClassInfo = opt(swapClass).map(x -> 
ClassInfo.of(x)).orElse(null);
                this.forMediaTypes = forMediaTypes();
                this.template = withTemplate();
        }
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 5823047d95..a2e81ad577 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -2890,6 +2890,8 @@ public class RestClient extends BeanContextable 
implements HttpClient, Closeable
                 */
                public Builder interceptors(Class<?>...values) throws Exception 
{
                        for (var c : values) {
+                               if (c == null)
+                                       continue;
                                ClassInfo ci = ClassInfo.of(c);
                                if (nn(ci)) {
                                        if 
(ci.isChildOfAny(RestCallInterceptor.class, HttpRequestInterceptor.class, 
HttpResponseInterceptor.class))
@@ -2953,6 +2955,8 @@ public class RestClient extends BeanContextable 
implements HttpClient, Closeable
                public Builder interceptors(Object...value) {
                        List<RestCallInterceptor> l = list();
                        for (var o : value) {
+                               if (o == null)
+                                       continue;
                                ClassInfo ci = ClassInfo.of(o);
                                if (nn(ci)) {
                                        if (! 
ci.isChildOfAny(HttpRequestInterceptor.class, HttpResponseInterceptor.class, 
RestCallInterceptor.class))
diff --git 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpHeaders.java
 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpHeaders.java
index f50b7bf2b0..85b5093ed2 100644
--- 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpHeaders.java
+++ 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpHeaders.java
@@ -502,6 +502,8 @@ public class HttpHeaders {
         * @return <jk>true</jk> if the {@link #cast(Object)} method can be 
used on the specified object.
         */
        public static boolean canCast(Object o) {
+               if (o == null)
+                       return false;
                var ci = ClassInfo.of(o);
                return nn(ci) && ci.isChildOfAny(Header.class, 
Headerable.class, NameValuePair.class, NameValuePairable.class, 
Map.Entry.class);
        }
diff --git 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpParts.java
 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpParts.java
index 8ff8cfa3e2..e628bc0933 100644
--- 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpParts.java
+++ 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/HttpParts.java
@@ -158,6 +158,8 @@ public class HttpParts {
         * @return <jk>true</jk> if the {@link #cast(Object)} method can be 
used on the specified object.
         */
        public static boolean canCast(Object o) {
+               if (o == null)
+                       return false;
                var ci = ClassInfo.of(o);
                return nn(ci) && ci.isChildOfAny(Headerable.class, 
NameValuePair.class, NameValuePairable.class, Map.Entry.class);
        }
diff --git 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/BasicPart.java
 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/BasicPart.java
index f66ac0cb4a..3794a60d96 100644
--- 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/BasicPart.java
+++ 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/BasicPart.java
@@ -57,6 +57,8 @@ public class BasicPart implements NameValuePair, Headerable {
         * @return <jk>true</jk> if the {@link #cast(Object)} method can be 
used on the specified object.
         */
        public static boolean canCast(Object o) {
+               if (o == null)
+                       return false;
                var ci = ClassInfo.of(o);
                return nn(ci) && ci.isChildOfAny(Headerable.class, 
NameValuePair.class, NameValuePairable.class, Map.Entry.class);
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
index 99c33132c7..ddaa303113 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
@@ -45,7 +45,7 @@ public abstract class DebugEnablement {
         */
        public static class Builder {
 
-               ReflectionMap.Builder<Enablement> mapBuilder;
+               ReflectionMap2.Builder<Enablement> mapBuilder;
                Enablement defaultEnablement = NEVER;
                Predicate<HttpServletRequest> conditional;
                BeanCreator<DebugEnablement> creator;
@@ -56,7 +56,7 @@ public abstract class DebugEnablement {
                 * @param beanStore The bean store to use for creating beans.
                 */
                protected Builder(BeanStore beanStore) {
-                       mapBuilder = ReflectionMap.create(Enablement.class);
+                       mapBuilder = ReflectionMap2.create(Enablement.class);
                        defaultEnablement = NEVER;
                        conditional = x -> 
"true".equalsIgnoreCase(x.getHeader("Debug"));
                        creator = 
beanStore.createBean(DebugEnablement.class).type(BasicDebugEnablement.class).builder(Builder.class,
 this);
@@ -211,7 +211,7 @@ public abstract class DebugEnablement {
        }
 
        private final Enablement defaultEnablement;
-       private final ReflectionMap<Enablement> enablementMap;
+       private final ReflectionMap2<Enablement> enablementMap;
        private final Predicate<HttpServletRequest> conditionalPredicate;
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablementMap.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablementMap.java
index 2eb384c6ce..2672a5c4ef 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablementMap.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablementMap.java
@@ -26,11 +26,11 @@ import org.apache.juneau.common.reflect.*;
  *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging";>Logging
 / Debugging</a>
  * </ul>
  */
-public class DebugEnablementMap extends ReflectionMap<Enablement> {
+public class DebugEnablementMap extends ReflectionMap2<Enablement> {
        /**
         * Builder class.
         */
-       public static class Builder extends ReflectionMap.Builder<Enablement> {
+       public static class Builder extends ReflectionMap2.Builder<Enablement> {
 
                /**
                 * Constructor.
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
index 550d30203b..18321b1a54 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
@@ -155,9 +155,9 @@ public class ClassInfo_Test extends TestBase {
        }
 
        @Test void ofTypeOnNulls() {
-               check(null, of((Class<?>)null));
-               check(null, of((Type)null));
-               check(null, of((Object)null));
+               assertThrows(IllegalArgumentException.class, () -> 
of((Class<?>)null));
+               assertThrows(IllegalArgumentException.class, () -> 
of((Type)null));
+               assertThrows(NullPointerException.class, () -> 
of((Object)null));
        }
 
        @Test void inner() {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ConstructorInfoTest.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ConstructorInfoTest.java
index 17dc5e003a..2502414f90 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ConstructorInfoTest.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ConstructorInfoTest.java
@@ -82,8 +82,8 @@ class ConstructorInfoTest extends TestBase {
        }
 
        @Test void of_null() {
-               check(null, ConstructorInfo.of(null));
-               check(null, ConstructorInfo.of(null, null));
+               assertThrows(IllegalArgumentException.class, () -> 
ConstructorInfo.of(null));
+               assertThrows(IllegalArgumentException.class, () -> 
ConstructorInfo.of(null, null));
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_Test.java
index dcfc4586b5..7b2ec35704 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_Test.java
@@ -92,8 +92,8 @@ class FieldInfo_Test extends TestBase {
        }
 
        @Test void of_null() {
-               check(null, FieldInfo.of(null));
-               check(null, FieldInfo.of(null, null));
+               assertThrows(IllegalArgumentException.class, () -> 
FieldInfo.of(null));
+               assertThrows(IllegalArgumentException.class, () -> 
FieldInfo.of(null, null));
        }
 
        @Test void getDeclaringClass() {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/MethodInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/MethodInfo_Test.java
index a621fa562e..2f394ed0c0 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/MethodInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/MethodInfo_Test.java
@@ -126,8 +126,8 @@ class MethodInfo_Test extends TestBase {
        }
 
        @Test void of_null() {
-               check(null, MethodInfo.of(null));
-               check(null, MethodInfo.of((ClassInfo)null, null));
+               assertThrows(IllegalArgumentException.class, () -> 
MethodInfo.of(null));
+               assertThrows(IllegalArgumentException.class, () -> 
MethodInfo.of((ClassInfo)null, null));
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
index 04a1593aee..f208f50351 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
@@ -786,7 +786,7 @@ class BeanStore_Test extends TestBase {
                var bs = BeanStore.create().outer(new BeanStore_Test()).build();
                assertNotNull(bs.createBean(D1a.class).run());
                assertNotNull(bs.createBean(D1b.class).run());
-               assertNull(bs.createBean(null).run());
+               assertThrows(IllegalArgumentException.class, () -> 
bs.createBean(null).run());
        }
 
        public static class D2 {
diff --git a/scripts/build-and-test.py b/scripts/build-and-test.py
index b5bc39bf81..4021f454d2 100755
--- a/scripts/build-and-test.py
+++ b/scripts/build-and-test.py
@@ -30,14 +30,16 @@ import sys
 import os
 
 def run_command(cmd, verbose=False):
-    """Run a command and return exit code."""
+    """Run a command and return exit code and full output."""
     print(f"Running: {cmd}")
     print("-" * 80)
     
     if verbose:
         # Show full output
-        result = subprocess.run(cmd, shell=True, 
cwd="/Users/james.bognar/git/juneau")
-        return result.returncode
+        result = subprocess.run(cmd, shell=True, 
cwd="/Users/james.bognar/git/juneau", capture_output=True, text=True)
+        print(result.stdout)
+        print(result.stderr, file=sys.stderr)
+        return result.returncode, result.stdout + result.stderr
     else:
         # Run command and capture all output, then show last lines
         result = subprocess.run(
@@ -55,7 +57,7 @@ def run_command(cmd, verbose=False):
             print('\n'.join(lines[-50:]))
         else:
             print(output)
-        return result.returncode
+        return result.returncode, output
 
 def build(verbose=False):
     """Run Maven clean install without tests."""
@@ -65,6 +67,21 @@ def test(verbose=False):
     """Run Maven tests."""
     return run_command("mvn test -q -Drat.skip=true", verbose)
 
+def parse_test_results(output):
+    """Parse Maven test output and extract failure/error counts."""
+    import re
+    # Look for the last occurrence of: [ERROR] Tests run: 25916, Failures: 0, 
Errors: 12, Skipped: 1
+    # This will be the total across all modules
+    matches = list(re.finditer(r'\[ERROR\]\s+Tests 
run:\s+(\d+),\s+Failures:\s+(\d+),\s+Errors:\s+(\d+)', output))
+    if matches:
+        # Use the last match (final total)
+        match = matches[-1]
+        total = int(match.group(1))
+        failures = int(match.group(2))
+        errors = int(match.group(3))
+        return total, failures, errors
+    return None, None, None
+
 def main():
     args = sys.argv[1:]
     
@@ -97,7 +114,7 @@ def main():
     exit_code = 0
     
     if build_only or full:
-        exit_code = build(verbose)
+        exit_code, output = build(verbose)
         if exit_code != 0:
             print("\n❌ Build failed!")
             return exit_code
@@ -106,9 +123,15 @@ def main():
     if test_only or full:
         if full:
             print("\n" + "=" * 80)
-        exit_code = test(verbose)
+        exit_code, output = test(verbose)
         if exit_code != 0:
-            print("\n❌ Tests failed!")
+            # Try to parse test results
+            total, failures, errors = parse_test_results(output)
+            if failures is not None and errors is not None:
+                failed_count = failures + errors
+                print(f"\n❌ Tests failed! ({failed_count} failed: {failures} 
failures, {errors} errors)")
+            else:
+                print("\n❌ Tests failed!")
             return exit_code
         print("\n✅ Tests passed!")
     

Reply via email to