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!")