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 4074c8df16 Utility class modernization
4074c8df16 is described below
commit 4074c8df167272062e488a446c243997b193109b
Author: James Bognar <[email protected]>
AuthorDate: Thu Nov 6 20:47:49 2025 -0500
Utility class modernization
---
.../juneau/common/reflect/ClassInfo.java.bak | 2506 ++++++++++++++++++++
.../org/apache/juneau/csv/CsvMetaProvider.java | 2 +-
.../apache/juneau/html/HtmlBeanPropertyMeta.java | 9 +-
.../org/apache/juneau/html/HtmlMetaProvider.java | 2 +-
.../java/org/apache/juneau/html/HtmlParser.java | 2 +-
.../org/apache/juneau/html/HtmlSerializer.java | 2 +-
.../org/apache/juneau/json/JsonMetaProvider.java | 2 +-
.../juneau/jsonschema/JsonSchemaMetaProvider.java | 2 +-
.../apache/juneau/msgpack/MsgPackMetaProvider.java | 2 +-
.../apache/juneau/oapi/OpenApiMetaProvider.java | 2 +-
.../juneau/plaintext/PlainTextMetaProvider.java | 2 +-
.../apache/juneau/soap/SoapXmlMetaProvider.java | 2 +-
.../org/apache/juneau/uon/UonMetaProvider.java | 2 +-
.../urlencoding/UrlEncodingMetaProvider.java | 2 +-
.../java/org/apache/juneau/xml/XmlBeanMeta.java | 2 +-
.../org/apache/juneau/xml/XmlBeanPropertyMeta.java | 4 +-
.../org/apache/juneau/xml/XmlMetaProvider.java | 2 +-
17 files changed, 2527 insertions(+), 20 deletions(-)
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java.bak
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java.bak
new file mode 100644
index 0000000000..487f559316
--- /dev/null
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java.bak
@@ -0,0 +1,2506 @@
+/*
+ * 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 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.PredicateUtils.*;
+import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
+import static org.apache.juneau.common.reflect.ClassNameFormat.*;
+import static org.apache.juneau.common.reflect.ClassArrayFormat.*;
+import static java.util.stream.Collectors.*;
+import static java.util.stream.Stream.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.security.*;
+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 class.
+ *
+ * <p>
+ * Provides various convenience methods for introspecting
fields/methods/annotations
+ * that aren't provided by the standard Java reflection APIs.
+ *
+ * <p>
+ * Objects are designed to be lightweight to create and threadsafe.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Wrap our class inside a ClassInfo.</jc>
+ * ClassInfo <jv>classInfo</jv> =
ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
+ *
+ * <jc>// Get all methods in parent-to-child order, sorted alphabetically
per class.</jc>
+ * <jk>for</jk> (MethodInfo <jv>methodInfo</jv> :
<jv>classInfo</jv>.getAllMethods()) {
+ * <jc>// Do something with it.</jc>
+ * }
+ *
+ * <jc>// Get all class-level annotations in parent-to-child order.</jc>
+ * <jk>for</jk> (MyAnnotation <jv>annotation</jv> :
<jv>classInfo</jv>.getAnnotations(MyAnnotation.<jk>class</jk>)) {
+ * <jc>// Do something with it.</jc>
+ * }
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * </ul>
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public class ClassInfo extends ElementInfo implements Annotatable {
+
+ private static final Cache<Class,ClassInfo> CACHE =
Cache.of(Class.class, ClassInfo.class).build();
+
+ /** Reusable ClassInfo for Object class. */
+ public static final ClassInfo OBJECT = ClassInfo.of(Object.class);
+
+ private static final Map<Class<?>,Class<?>> pmap1 = new HashMap<>(),
pmap2 = new HashMap<>();
+
+ static {
+ pmap1.put(boolean.class, Boolean.class);
+ pmap1.put(byte.class, Byte.class);
+ pmap1.put(short.class, Short.class);
+ pmap1.put(char.class, Character.class);
+ pmap1.put(int.class, Integer.class);
+ pmap1.put(long.class, Long.class);
+ pmap1.put(float.class, Float.class);
+ pmap1.put(double.class, Double.class);
+ pmap2.put(Boolean.class, boolean.class);
+ pmap2.put(Byte.class, byte.class);
+ pmap2.put(Short.class, short.class);
+ pmap2.put(Character.class, char.class);
+ pmap2.put(Integer.class, int.class);
+ pmap2.put(Long.class, long.class);
+ pmap2.put(Float.class, float.class);
+ pmap2.put(Double.class, double.class);
+ }
+
+ // @formatter:off
+ private static final Map<Class,Object> primitiveDefaultMap =
+ mapb(Class.class,Object.class)
+ .unmodifiable()
+ .add(Boolean.TYPE, false)
+ .add(Character.TYPE, (char)0)
+ .add(Short.TYPE, (short)0)
+ .add(Integer.TYPE, 0)
+ .add(Long.TYPE, 0L)
+ .add(Float.TYPE, 0f)
+ .add(Double.TYPE, 0d)
+ .add(Byte.TYPE, (byte)0)
+ .add(Boolean.class, false)
+ .add(Character.class, (char)0)
+ .add(Short.class, (short)0)
+ .add(Integer.class, 0)
+ .add(Long.class, 0L)
+ .add(Float.class, 0f)
+ .add(Double.class, 0d)
+ .add(Byte.class, (byte)0)
+ .build();
+ // @formatter:on
+
+ /**
+ * 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>.
+ */
+ public static ClassInfo of(Class<?> c) {
+ if (c == null)
+ return null;
+ return CACHE.get(c, () -> new ClassInfo(c, c));
+ }
+
+ /**
+ * 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>.
+ */
+ public static ClassInfo of(Class<?> c, Type t) {
+ if (c == t)
+ return of(c);
+ return new ClassInfo(c, t);
+ }
+
+ /**
+ * 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>.
+ */
+ public static ClassInfo of(Object o) {
+ return of(o == null ? null : o instanceof Class ? (Class<?>)o :
o.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>.
+ */
+ 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);
+ }
+
+ /**
+ * 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>.
+ */
+ public static ClassInfo ofProxy(Object o) {
+ if (o == null)
+ return null;
+ Class<?> c = getProxyFor(o);
+ return c == null ? ClassInfo.of(o) : ClassInfo.of(c);
+ }
+
+ // The underlying Type object (may be Class, ParameterizedType,
GenericArrayType, etc.).
+ private final Type t;
+
+ // The underlying Class object (null for non-class types like
TypeVariable). Effectively final.
+ private Class<?> c;
+
+ // True if this represents a ParameterizedType (e.g., List<String>).
+ private final boolean isParameterizedType;
+
+ // Number of array dimensions (0 if not an array).
+ private final Supplier<Integer> dimensions =
memoize(this::findDimensions);
+
+ // Base component type for arrays (e.g., String for String[][]), also
handles GenericArrayType. Cached and never null.
+ private final Supplier<ClassInfo> componentType =
memoize(this::findComponentType);
+
+ // The package this class belongs to (null for primitive types and
arrays).
+ private final Supplier<PackageInfo> packageInfo = memoize(() ->
opt(c).map(x -> PackageInfo.of(x.getPackage())).orElse(null));
+
+ // All superclasses of this class in child-to-parent order, starting
with this class.
+ private final Supplier<List<ClassInfo>> parents =
memoize(this::findParents);
+
+ // All annotations declared directly on this class, wrapped in
AnnotationInfo.
+ private final Supplier<List<AnnotationInfo>> declaredAnnotations2 =
memoize(() -> (List)opt(c).map(x ->
u(l(x.getDeclaredAnnotations()))).orElse(liste()).stream().map(a ->
AnnotationInfo.of(this, a)).toList());
+
+ // Fully qualified class name with generics (e.g.,
"java.util.List<java.lang.String>").
+ private final Supplier<String> fullName = memoize(() ->
getNameFormatted(FULL, true, '$', BRACKETS));
+
+ // Simple class name with generics (e.g., "List<String>").
+ private final Supplier<String> shortName = memoize(() ->
getNameFormatted(SHORT, true, '$', BRACKETS));
+
+ // Human-readable class name without generics (e.g., "List").
+ private final Supplier<String> readableName = memoize(() ->
getNameFormatted(SIMPLE, false, '$', WORD));
+
+ // All interfaces declared directly by this class.
+ private final Supplier<List<ClassInfo>> declaredInterfaces = memoize(()
-> opt(c).map(x ->
stream(x.getInterfaces()).map(ClassInfo::of).toList()).orElse(liste()));
+
+ // All interfaces implemented by this class and its parents, in
child-to-parent order.
+ private final Supplier<List<ClassInfo>> interfaces = memoize(() ->
getParents().stream().flatMap(x ->
x.getDeclaredInterfaces().stream()).flatMap(ci2 -> concat(Stream.of(ci2),
ci2.getInterfaces().stream())).distinct().toList());
+
+ // All parent classes and interfaces, classes first, then in
child-to-parent order.
+ // TODO - Determine if this field is still needed now that we have
parentsAndInterfaces which handles the hierarchy better.
+ private final Supplier<List<ClassInfo>> allParents = memoize(() ->
concat(getParents().stream(), getInterfaces().stream()).toList());
+
+ // All parent classes and interfaces with proper traversal of interface
hierarchy to avoid duplicates.
+ private final Supplier<List<ClassInfo>> parentsAndInterfaces =
memoize(this::findParentsAndInterfaces);
+
+ /**
+ * Finds all parent classes and interfaces with proper traversal of
interface hierarchy.
+ *
+ * @return A list of all parent classes and interfaces without
duplicates.
+ */
+ private List<ClassInfo> findParentsAndInterfaces() {
+ var set = new LinkedHashSet<ClassInfo>();
+
+ // Process all parent classes (includes this class)
+ var parents = getParents();
+ for (int i = 0; i < parents.size(); i++) {
+ var parent = parents.get(i);
+ set.add(parent);
+
+ // Process interfaces declared on this parent (and
their parent interfaces)
+ var declaredInterfaces = parent.getDeclaredInterfaces();
+ for (int j = 0; j < declaredInterfaces.size(); j++)
+ addInterfaceHierarchy(set,
declaredInterfaces.get(j));
+ }
+
+ return u(new ArrayList<>(set));
+ }
+
+ /**
+ * Helper method to recursively add an interface and its parent
interfaces to the set.
+ *
+ * @param set The set to add to.
+ * @param iface The interface to add.
+ */
+ private void addInterfaceHierarchy(LinkedHashSet<ClassInfo> set,
ClassInfo iface) {
+ if (!set.add(iface))
+ return;
+
+ // Process parent interfaces recursively
+ var parentInterfaces = iface.getDeclaredInterfaces();
+ for (int i = 0; i < parentInterfaces.size(); i++)
+ addInterfaceHierarchy(set, parentInterfaces.get(i));
+ }
+
+ /**
+ * Finds all annotations on this class and parent classes/interfaces in
child-to-parent order.
+ *
+ * <p>
+ * This is similar to {@link
org.apache.juneau.common.reflect.AnnotationProvider2#find(Class)} but without
runtime annotations.
+ *
+ * <p>
+ * Order of traversal:
+ * <ol>
+ * <li>Annotations declared on this class
+ * <li>Annotations declared on parent classes (child-to-parent
order)
+ * <li>For each parent class, annotations on interfaces declared
on that class (child-to-parent interface hierarchy)
+ * <li>Annotations on the package of this class
+ * </ol>
+ *
+ * @return A list of all annotation infos in child-to-parent order.
+ */
+ private List<AnnotationInfo<Annotation>> findAnnotationInfos() {
+ var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+ // On all parent classes and interfaces (properly traversed to
avoid duplicates)
+ var parentsAndInterfaces = getParentsAndInterfaces();
+ for (int i = 0; i < parentsAndInterfaces.size(); i++) {
+ var ci = parentsAndInterfaces.get(i);
+ // Add declared annotations from this class/interface
+ for (var a : ci.inner().getDeclaredAnnotations())
+ for (var a2 : splitRepeated(a))
+ list.add(AnnotationInfo.of(ci, a2));
+ }
+
+ // On the package of this class
+ var pkg = getPackage();
+ if (nn(pkg)) {
+ var pi = PackageInfo.of(pkg.inner());
+ for (var a : pkg.inner().getAnnotations())
+ for (var a2 : splitRepeated(a))
+ list.add(AnnotationInfo.of(pi, a2));
+ }
+
+ return u(list);
+ }
+
+ // All annotations on this class and parent classes/interfaces in
child-to-parent order.
+ private final Supplier<List<AnnotationInfo<Annotation>>>
annotationInfos = memoize(this::findAnnotationInfos);
+
+ // All record components if this is a record class (Java 14+).
+ private final Supplier<List<RecordComponent>> recordComponents =
memoize(() -> opt(c).filter(Class::isRecord).map(x ->
u(l(x.getRecordComponents()))).orElse(liste()));
+
+ // All generic interface types (e.g., List<String> implements
Comparable<List<String>>).
+ private final Supplier<List<Type>> genericInterfaces = memoize(() ->
opt(c).map(x -> u(l(x.getGenericInterfaces()))).orElse(liste()));
+
+ // All type parameters declared on this class (e.g., <T, U> in class
Foo<T, U>).
+ private final Supplier<List<TypeVariable<?>>> typeParameters =
memoize(() -> opt(c).map(x ->
u(l((TypeVariable<?>[])x.getTypeParameters()))).orElse(liste()));
+
+ // All annotated interface types with their annotations.
+ private final Supplier<List<AnnotatedType>> annotatedInterfaces =
memoize(() -> opt(c).map(x ->
u(l(x.getAnnotatedInterfaces()))).orElse(liste()));
+
+ // All signers of this class (for signed JARs).
+ private final Supplier<List<Object>> signers = memoize(() ->
opt(c).map(Class::getSigners).map(x -> u(l(x))).orElse(liste()));
+
+ // All public methods on this class and inherited, excluding Object
methods.
+ private final Supplier<List<MethodInfo>> publicMethods = memoize(() ->
opt(c).map(x -> stream(x.getMethods()).filter(m -> ne(m.getDeclaringClass(),
Object.class)).map(this::getMethodInfo).sorted().toList()).orElse(liste()));
+
+ // All methods declared directly on this class (public, protected,
package, private).
+ private final Supplier<List<MethodInfo>> declaredMethods = memoize(()
-> opt(c).map(x -> stream(x.getDeclaredMethods()).filter(m -> ne("$jacocoInit",
m.getName())).map(this::getMethodInfo).sorted().toList()).orElse(liste()));
+
+ // All methods from this class and all parents, in child-to-parent
order.
+ private final Supplier<List<MethodInfo>> allMethods = memoize(() ->
allParents.get().stream().flatMap(c ->
c.getDeclaredMethods().stream()).toList());
+
+ // All methods from this class and all parents, in parent-to-child
order.
+ private final Supplier<List<MethodInfo>> allMethodsParentFirst =
memoize(() -> rstream(getAllParents()).flatMap(c ->
c.getDeclaredMethods().stream()).toList());
+
+ // All public fields from this class and parents, deduplicated by name
(child wins).
+ private final Supplier<List<FieldInfo>> publicFields = memoize(() ->
parents.get().stream().flatMap(c -> c.getDeclaredFields().stream()).filter(f ->
f.isPublic() && ne("$jacocoData",
f.getName())).collect(toMap(FieldInfo::getName, x -> x, (a, b) -> a,
LinkedHashMap::new)).values().stream().sorted().collect(toList()));
+
+ // All fields declared directly on this class (public, protected,
package, private).
+ private final Supplier<List<FieldInfo>> declaredFields = memoize(() ->
opt(c).map(x -> stream(x.getDeclaredFields()).filter(f -> ne("$jacocoData",
f.getName())).map(this::getFieldInfo).sorted().toList()).orElse(liste()));
+
+ // All fields from this class and all parents, in parent-to-child order.
+ private final Supplier<List<FieldInfo>> allFields = memoize(() ->
rstream(allParents.get()).flatMap(c ->
c.getDeclaredFields().stream()).toList());
+
+ // All public constructors declared on this class.
+ private final Supplier<List<ConstructorInfo>> publicConstructors =
memoize(() -> opt(c).map(x ->
stream(x.getConstructors()).map(this::getConstructorInfo).sorted().toList()).orElse(liste()));
+
+ // All constructors declared on this class (public, protected, package,
private).
+ private final Supplier<List<ConstructorInfo>> declaredConstructors =
memoize(() -> opt(c).map(x ->
stream(x.getDeclaredConstructors()).map(this::getConstructorInfo).sorted().toList()).orElse(liste()));
+
+ // The repeated annotation method (value()) if this class is a
@Repeatable container.
+ private final Supplier<MethodInfo> repeatedAnnotationMethod =
memoize(this::findRepeatedAnnotationMethod);
+
+ // Cache of wrapped Method objects.
+ private final Cache<Method,MethodInfo> methodCache =
Cache.of(Method.class, MethodInfo.class).build();
+
+ // Cache of wrapped Field objects.
+ private final Cache<Field,FieldInfo> fieldCache = Cache.of(Field.class,
FieldInfo.class).build();
+
+ // Cache of wrapped Constructor objects.
+ private final Cache<Constructor,ConstructorInfo> constructorCache =
Cache.of(Constructor.class, ConstructorInfo.class).build();
+
+ /**
+ * Constructor.
+ *
+ * @param c The class type.
+ * @param t The generic type (if parameterized type).
+ */
+ protected ClassInfo(Class<?> c, Type t) {
+ super(c == null ? 0 : c.getModifiers());
+ this.t = t;
+ this.c = c;
+ this.isParameterizedType = t == null ? false : (t instanceof
ParameterizedType);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this type can be used as a parameter for
the specified object.
+ *
+ * @param child The argument to check.
+ * @return <jk>true</jk> if this type can be used as a parameter for
the specified object.
+ */
+ public boolean canAcceptArg(Object child) {
+ if (c == null || child == null)
+ return false;
+ if (c.isInstance(child))
+ return true;
+ if (this.isPrimitive() || child.getClass().isPrimitive()) {
+ return
this.getWrapperIfPrimitive().isAssignableFrom(of(child).getWrapperIfPrimitive());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof ClassInfo o2) && eq(this, o2, (x, y) ->
eq(x.t, y.t));
+ }
+
+
+ /**
+ * Performs an action on all matching annotations on this class and
superclasses/interfaces.
+ *
+ * <p>
+ * Annotations are appended in the following orders:
+ * <ol>
+ * <li>On the package of this class.
+ * <li>On interfaces ordered parent-to-child.
+ * <li>On parent classes ordered parent-to-child.
+ * <li>On this class.
+ * </ol>
+ *
+ * @param <A> The annotation type to look for.
+ * @param annotationProvider The annotation provider.
+ * @param type The annotation to look for.
+ * @param filter A predicate to apply to the entries to determine if
action should be performed. Can be <jk>null</jk>.
+ * @param action An action to perform on the entry.
+ * @return This object.
+ */
+ public <A extends Annotation> ClassInfo
forEachAnnotation(AnnotationProvider annotationProvider, Class<A> type,
Predicate<A> filter, Consumer<A> action) {
+ if (annotationProvider == null)
+ throw unsupportedOp();
+ A t2 = getPackageAnnotation(type);
+ if (nn(t2))
+ consumeIf(filter, action, t2);
+ var interfaces2 = interfaces.get();
+ for (int i = interfaces2.size() - 1; i >= 0; i--)
+
annotationProvider.getAnnotationProvider().findDeclaredParentFirst(type,
interfaces2.get(i).inner()).map(x -> x.inner()).filter(x -> filter == null ||
filter.test(x)).forEach(x -> action.accept(x));
+ var parents2 = parents.get();
+ for (int i = parents2.size() - 1; i >= 0; i--)
+
annotationProvider.getAnnotationProvider().findDeclaredParentFirst(type,
parents2.get(i).inner()).map(x -> x.inner()).filter(x -> filter == null ||
filter.test(x)).forEach(x -> action.accept(x));
+ return this;
+ }
+
+ /**
+ * Returns all fields on this class and all parent classes.
+ *
+ * <p>
+ * Results are ordered parent-to-child, and then alphabetical per
class.
+ *
+ * @return
+ * All declared fields on this class.
+ * <br>List is unmodifiable.
+ */
+ public List<FieldInfo> getAllFields() { return allFields.get(); }
+
+ /**
+ * Returns all declared methods on this class and all parent classes.
+ *
+ * @return
+ * All declared methods on this class and all parent classes.
+ * <br>Results are ordered parent-to-child, and then
alphabetically per class.
+ * <br>List is unmodifiable.
+ */
+ public List<MethodInfo> getAllMethodsParentFirst() { return
allMethodsParentFirst.get(); }
+
+ /**
+ * Returns a list including this class and all parent classes and
interfaces.
+ *
+ * <p>
+ * Results are classes-before-interfaces, then child-to-parent order.
+ *
+ * @return An unmodifiable list including this class and all parent
classes.
+ * <br>Results are ordered child-to-parent order with classes
listed before interfaces.
+ */
+ public List<ClassInfo> getAllParents() { return allParents.get(); }
+
+ /**
+ * Finds the annotation of the specified type defined on this class or
parent class/interface.
+ *
+ * <p>
+ * If the annotation cannot be found on the immediate class, searches
methods with the same signature on the parent classes or interfaces. <br>
+ * The search is performed in child-to-parent order.
+ *
+ * @param <A> The annotation type to look for.
+ * @param annotationProvider The annotation provider.
+ * @param type The annotation to look for.
+ * @return The annotation if found, or <jk>null</jk> if not.
+ */
+ public <A extends Annotation> A getAnnotation(AnnotationProvider
annotationProvider, Class<A> type) {
+ return findAnnotation(annotationProvider, type);
+ }
+
+ /**
+ * Returns all annotations of the specified type defined on this or
parent classes/interfaces.
+ *
+ * <p>
+ * Returns the list in reverse (parent-to-child) order.
+ *
+ * @param <A> The annotation type to look for.
+ * @param annotationProvider The annotation provider.
+ * @param type The annotation type to look for.
+ * @return The matching annotations.
+ */
+ public <A extends Annotation> List<A> getAnnotations(AnnotationProvider
annotationProvider, Class<A> type) {
+ List<A> l = list();
+ forEachAnnotation(annotationProvider, type, x -> true, x ->
l.add(x));
+ return l;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} for this class.
+ *
+ * <p>
+ * If this class represents a primitive type or void, <jk>null</jk> is
returned.
+ *
+ * @return The class loader for this class, or <jk>null</jk> if it
doesn't have one.
+ */
+ public ClassLoader getClassLoader() {
+ return c == null ? null : c.getClassLoader();
+ }
+
+ /**
+ * Returns the base component type of this class.
+ *
+ * <p>
+ * For array types (e.g., <c>String[][]</c>), returns the deepest
component type (e.g., <c>String</c>).
+ * <br>For non-array types, returns this class itself.
+ *
+ * <p>
+ * <b>Note:</b> Unlike {@link Class#getComponentType()}, this method
also handles generic array types (e.g., <c>List<String>[]</c>)
+ * and returns the full parameterized type information (e.g.,
<c>List<String></c>).
+ * Additionally, this method never returns <jk>null</jk> - non-array
types return <jk>this</jk> instead.
+ *
+ * @return The base component type of an array, or this class if not an
array.
+ */
+ public ClassInfo getComponentType() {
+ return componentType.get();
+ }
+
+ /**
+ * Returns the {@link Class} object representing the class or interface
that declares the member class
+ * represented by this class.
+ *
+ * <p>
+ * Returns <jk>null</jk> if this class is not a member class.
+ *
+ * @return The declaring class, or <jk>null</jk> if this class is not a
member of another class.
+ */
+ public ClassInfo getDeclaringClass() {
+ return c == null ? null : of(c.getDeclaringClass());
+ }
+
+ /**
+ * Returns the first matching declared constructor on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The declared constructor that matches the specified
predicate.
+ */
+ public ConstructorInfo
getDeclaredConstructor(Predicate<ConstructorInfo> filter) {
+ for (var ci : declaredConstructors.get())
+ if (test(filter, ci))
+ return ci;
+ return null;
+ }
+
+ /**
+ * Returns all the constructors defined on this class.
+ *
+ * @return
+ * All constructors defined on this class.
+ * <br>List is unmodifiable.
+ */
+ public List<ConstructorInfo> getDeclaredConstructors() { return
declaredConstructors.get(); }
+
+ /**
+ * Returns the first matching declared field on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The declared field, or <jk>null</jk> if not found.
+ */
+ public FieldInfo getDeclaredField(Predicate<FieldInfo> filter) {
+ for (var f : declaredFields.get())
+ if (test(filter, f))
+ return f;
+ return null;
+ }
+
+ /**
+ * Returns all declared fields on this class.
+ *
+ * @return
+ * All declared fields on this class.
+ * <br>Results are in alphabetical order.
+ * <br>List is unmodifiable.
+ */
+ public List<FieldInfo> getDeclaredFields() { return
declaredFields.get(); }
+
+ /**
+ * Returns all public member classes and interfaces declared by this
class and its superclasses.
+ *
+ * <p>
+ * This includes public class and interface members inherited from
superclasses and
+ * public class and interface members declared by the class.
+ *
+ * @return
+ * An unmodifiable list of all public member classes and
interfaces declared by this class.
+ * <br>Returns an empty list if this class has no public member
classes or interfaces.
+ */
+ public List<ClassInfo> getClasses() {
+ if (c == null)
+ return u(l());
+ Class<?>[] classes = c.getClasses();
+ List<ClassInfo> l = listOfSize(classes.length);
+ for (Class<?> cc : classes)
+ l.add(of(cc));
+ return u(l);
+ }
+
+ /**
+ * Returns all classes and interfaces declared as members of this class.
+ *
+ * <p>
+ * This includes public, protected, default (package) access, and
private classes and interfaces
+ * declared by the class, but excludes inherited classes and interfaces.
+ *
+ * @return
+ * An unmodifiable list of all classes and interfaces declared as
members of this class.
+ * <br>Returns an empty list if this class declares no classes or
interfaces as members.
+ */
+ public List<ClassInfo> getDeclaredClasses() {
+ if (c == null)
+ return u(l());
+ Class<?>[] classes = c.getDeclaredClasses();
+ List<ClassInfo> l = listOfSize(classes.length);
+ for (Class<?> cc : classes)
+ l.add(of(cc));
+ return u(l);
+ }
+
+ /**
+ * Returns a list of interfaces declared on this class.
+ *
+ * <p>
+ * Does not include interfaces declared on parent classes.
+ *
+ * <p>
+ * Results are in the same order as Class.getInterfaces().
+ *
+ * @return
+ * An unmodifiable list of interfaces declared on this class.
+ * <br>Results are in the same order as {@link
Class#getInterfaces()}.
+ */
+ public List<ClassInfo> getDeclaredInterfaces() { return
declaredInterfaces.get(); }
+
+ /**
+ * Returns the immediately enclosing class of this class.
+ *
+ * <p>
+ * Returns <jk>null</jk> if this class is a top-level class.
+ *
+ * @return The enclosing class, or <jk>null</jk> if this is a top-level
class.
+ */
+ public ClassInfo getEnclosingClass() {
+ return c == null ? null : of(c.getEnclosingClass());
+ }
+
+ /**
+ * Returns the {@link ConstructorInfo} object representing the
constructor that declares this class if this is a
+ * local or anonymous class declared within a constructor.
+ *
+ * <p>
+ * Returns <jk>null</jk> if this class was not declared within a
constructor.
+ *
+ * @return The enclosing constructor, or <jk>null</jk> if this class
was not declared within a constructor.
+ */
+ public ConstructorInfo getEnclosingConstructor() {
+ if (c == null)
+ return null;
+ Constructor<?> ec = c.getEnclosingConstructor();
+ return ec == null ? null : getConstructorInfo(ec);
+ }
+
+ /**
+ * Returns the {@link MethodInfo} object representing the method that
declares this class if this is a
+ * local or anonymous class declared within a method.
+ *
+ * <p>
+ * Returns <jk>null</jk> if this class was not declared within a method.
+ *
+ * @return The enclosing method, or <jk>null</jk> if this class was not
declared within a method.
+ */
+ public MethodInfo getEnclosingMethod() {
+ if (c == null)
+ return null;
+ Method em = c.getEnclosingMethod();
+ return em == null ? null : getMethodInfo(em);
+ }
+
+ /**
+ * Returns the first matching declared method on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The first matching method, or <jk>null</jk> if no methods
matched.
+ */
+ public MethodInfo getDeclaredMethod(Predicate<MethodInfo> filter) {
+ for (var mi : declaredMethods.get())
+ if (test(filter, mi))
+ return mi;
+ return null;
+ }
+
+ /**
+ * Returns all methods declared on this class.
+ *
+ * @return
+ * All methods declared on this class.
+ * <br>Results are ordered alphabetically.
+ * <br>List is unmodifiable.
+ */
+ public List<MethodInfo> getDeclaredMethods() { return
declaredMethods.get(); }
+
+ /**
+ * Returns the number of dimensions if this is an array type.
+ *
+ * @return The number of dimensions if this is an array type, or
<c>0</c> if it is not.
+ */
+ public int getDimensions() {
+ return dimensions.get();
+ }
+
+ /**
+ * Returns the full name of this class.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <ul>
+ * <li><js>"com.foo.MyClass"</js> - Normal class
+ * <li><js>"com.foo.MyClass[][]"</js> - Array.
+ * <li><js>"com.foo.MyClass$InnerClass"</js> - Inner class.
+ * <li><js>"com.foo.MyClass$InnerClass[][]"</js> - Inner class
array.
+ * <li><js>"int"</js> - Primitive class.
+ * <li><js>"int[][]"</js> - Primitive class class.
+ *
<li><js>"java.util.Map<java.lang.String,java.lang.Object>"</js> -
Parameterized type.
+ * <li><js>"java.util.AbstractMap<K,V>"</js> - Parameterized
generic type.
+ * <li><js>"V"</js> - Parameterized generic type argument.
+ * </ul>
+ *
+ * @return The underlying class name.
+ */
+ public String getNameFull() {
+ return fullName.get();
+ }
+
+ /**
+ * Returns a list of interfaces defined on this class and superclasses.
+ *
+ * <p>
+ * Results are in child-to-parent order.
+ *
+ * @return
+ * An unmodifiable list of interfaces defined on this class and
superclasses.
+ * <br>Results are in child-to-parent order.
+ */
+ public List<ClassInfo> getInterfaces() { return interfaces.get(); }
+
+ /**
+ * Returns all parent classes and interfaces in proper traversal order.
+ *
+ * <p>
+ * This method returns a unique list of all parent classes (including
this class) and all interfaces
+ * (including interface hierarchies) with proper handling of
duplicates. The order is:
+ * <ol>
+ * <li>This class
+ * <li>Parent classes in child-to-parent order
+ * <li>For each class, interfaces declared on that class and their
parent interfaces
+ * </ol>
+ *
+ * <p>
+ * This is useful for annotation processing where you need to traverse
the complete type hierarchy
+ * without duplicates.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Interface hierarchy:</jc>
+ * <jk>interface</jk> ISuperGrandParent {}
+ * <jk>interface</jk> IGrandParent <jk>extends</jk>
ISuperGrandParent {}
+ * <jk>interface</jk> ISuperParent {}
+ * <jk>interface</jk> IParent <jk>extends</jk> ISuperParent {}
+ * <jk>interface</jk> IChild {}
+ *
+ * <jc>// Class hierarchy:</jc>
+ * <jk>class</jk> GrandParent <jk>implements</jk> IGrandParent {}
+ * <jk>class</jk> Parent <jk>extends</jk> GrandParent
<jk>implements</jk> IParent {}
+ * <jk>class</jk> Child <jk>extends</jk> Parent
<jk>implements</jk> IChild {}
+ *
+ * <jc>// For Child, returns (in this order):</jc>
+ * ClassInfo <jv>ci</jv> =
ClassInfo.<jsm>of</jsm>(Child.<jk>class</jk>);
+ * List<ClassInfo> <jv>result</jv> =
<jv>ci</jv>.getParentsAndInterfaces();
+ * <jc>// Result: [</jc>
+ * <jc>// Child, // 1. This class</jc>
+ * <jc>// IChild, // 2. Interface on Child</jc>
+ * <jc>// Parent, // 3. Parent class</jc>
+ * <jc>// IParent, // 4. Interface on Parent</jc>
+ * <jc>// ISuperParent, // 5. Parent interface of
IParent</jc>
+ * <jc>// GrandParent, // 6. Grandparent class</jc>
+ * <jc>// IGrandParent, // 7. Interface on
GrandParent</jc>
+ * <jc>// ISuperGrandParent // 8. Parent interface of
IGrandParent</jc>
+ * <jc>// ]</jc>
+ * </p>
+ *
+ * @return An unmodifiable list of all parent classes and interfaces,
properly ordered without duplicates.
+ */
+ public List<ClassInfo> getParentsAndInterfaces() { return
parentsAndInterfaces.get(); }
+
+ /**
+ * Returns all annotations on this class and parent classes/interfaces
in child-to-parent order.
+ *
+ * <p>
+ * This returns all declared annotations from:
+ * <ol>
+ * <li>This class
+ * <li>Parent classes in child-to-parent order
+ * <li>For each class, interfaces declared on that class and their
parent interfaces
+ * <li>The package of this class
+ * </ol>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider2}.
+ *
+ * @return An unmodifiable list of all annotation infos.
+ */
+ public List<AnnotationInfo<Annotation>> getAnnotationInfos() { return
annotationInfos.get(); }
+
+ /**
+ * Returns all annotations of the specified type on this class and
parent classes/interfaces in child-to-parent order.
+ *
+ * <p>
+ * This returns all declared annotations from:
+ * <ol>
+ * <li>This class
+ * <li>Parent classes in child-to-parent order
+ * <li>For each class, interfaces declared on that class and their
parent interfaces
+ * <li>The package of this class
+ * </ol>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider2}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to filter by.
+ * @return A stream of annotation infos of the specified type.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
getAnnotationInfos(Class<A> type) {
+ assertArgNotNull("type", type);
+ return getAnnotationInfos().stream()
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+
+ /**
+ * Returns the first matching method on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The first matching method, or <jk>null</jk> if no methods
matched.
+ */
+ public MethodInfo getMethod(Predicate<MethodInfo> filter) {
+ for (var mi : allMethods.get())
+ if (test(filter, mi))
+ return mi;
+ return null;
+ }
+
+ /**
+ * Returns all declared methods on this class and all parent classes.
+ *
+ * @return
+ * All declared methods on this class and all parent classes.
+ * <br>Results are ordered child-to-parent, and then
alphabetically per class.
+ * <br>List is unmodifiable.
+ */
+ public List<MethodInfo> getMethods() { return allMethods.get(); }
+
+ /**
+ * Returns the name of the underlying class.
+ *
+ * <p>
+ * Equivalent to calling {@link Class#getName()} or {@link
Type#getTypeName()} depending on whether
+ * this is a class or type.
+ *
+ * <p>
+ * This method returns the JVM internal format for class names:
+ * <ul>
+ * <li>Uses fully qualified package names
+ * <li>Uses <js>'$'</js> separator for nested classes
+ * <li>Uses JVM notation for arrays (e.g.,
<js>"[Ljava.lang.String;"</js>)
+ * <li>Uses single letters for primitive arrays (e.g.,
<js>"[I"</js> for <c>int[]</c>)
+ * </ul>
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <ul>
+ * <li><js>"java.lang.String"</js> - Normal class
+ * <li><js>"[Ljava.lang.String;"</js> - Array
+ * <li><js>"[[Ljava.lang.String;"</js> - Multi-dimensional array
+ * <li><js>"java.util.Map$Entry"</js> - Nested class
+ * <li><js>"int"</js> - Primitive class
+ * <li><js>"[I"</js> - Primitive array
+ * <li><js>"[[I"</js> - Multi-dimensional primitive array
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='spaced-list'>
+ * <li><c>{@link #getNameCanonical()}</c> - Java source code
format (uses <js>'.'</js> and <js>"[]"</js>)
+ * <li><c>{@link #getNameSimple()}</c> - Simple class name without
package
+ * <li><c>{@link #getNameFull()}</c> - Full name with type
parameters
+ * <li><c>{@link #getNameShort()}</c> - Short name with type
parameters
+ * <li><c>{@link #getNameReadable()}</c> - Human-readable name
(uses <js>"Array"</js> suffix)
+ * </ul>
+ *
+ * @return The name of the underlying class in JVM format.
+ */
+ public String getName() { return nn(c) ? c.getName() : t.getTypeName();
}
+
+ /**
+ * Returns the canonical name of the underlying class.
+ *
+ * <p>
+ * The canonical name is the name that would be used in Java source
code to refer to the class.
+ * For example:
+ * <ul>
+ * <li><js>"java.lang.String"</js> - Normal class
+ * <li><js>"java.lang.String[]"</js> - Array
+ * <li><js>"java.util.Map.Entry"</js> - Nested class
+ * <li><jk>null</jk> - Local or anonymous class
+ * </ul>
+ *
+ * @return The canonical name of the underlying class, or <jk>null</jk>
if this class doesn't have a canonical name.
+ */
+ public String getNameCanonical() {
+ // For Class objects, delegate to Class.getCanonicalName()
which handles local/anonymous classes
+ // For Type objects (ParameterizedType, etc.), compute the
canonical name
+ if (c != null && !isParameterizedType) {
+ return c.getCanonicalName();
+ }
+ // For ParameterizedType, we can't have a true canonical name
with type parameters
+ // Return null to maintain consistency with
Class.getCanonicalName() behavior
+ return null;
+ }
+
+ /**
+ * Returns all possible names for this class.
+ *
+ * @return
+ * An array consisting of:
+ * <ul>
+ * <li>{@link #getNameFull()}
+ * <li>{@link Class#getName()} - Note that this might be a
dup.
+ * <li>{@link #getNameShort()}
+ * <li>{@link #getNameSimple()}
+ * </ul>
+ */
+ public String[] getNames() { return a(getNameFull(), c.getName(),
getNameShort(), getNameSimple()); }
+
+ /**
+ * Returns the module that this class is a member of.
+ *
+ * <p>
+ * If this class is not in a named module, returns the unnamed module
of the class loader for this class.
+ *
+ * @return The module that this class is a member of.
+ */
+ public Module getModule() {
+ return c == null ? null : c.getModule();
+ }
+
+ /**
+ * Locates the no-arg constructor for this class.
+ *
+ * <p>
+ * Constructor must match the visibility requirements specified by
parameter 'v'.
+ * If class is abstract, always returns <jk>null</jk>.
+ * Note that this also returns the 1-arg constructor for non-static
member classes.
+ *
+ * @param v The minimum visibility.
+ * @return The constructor, or <jk>null</jk> if no no-arg constructor
exists with the required visibility.
+ */
+ public ConstructorInfo getNoArgConstructor(Visibility v) {
+ if (isAbstract())
+ return null;
+ int expectedParams = isNonStaticMemberClass() ? 1 : 0;
+ return getDeclaredConstructors().stream()
+ .filter(cc -> cc.hasNumParameters(expectedParams))
+ .filter(cc -> cc.isVisible(v))
+ .map(cc -> cc.accessible(v))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Returns the package of this class.
+ *
+ * @return The package of this class wrapped in a {@link PackageInfo},
or <jk>null</jk> if this class has no package.
+ */
+ public PackageInfo getPackage() {
+ return packageInfo.get();
+ }
+
+ /**
+ * Returns the specified annotation only if it's been declared on the
package of this class.
+ *
+ * @param <A> The annotation type to look for.
+ * @param type The annotation class.
+ * @return The annotation, or <jk>null</jk> if not found.
+ */
+ public <A extends Annotation> A getPackageAnnotation(Class<A> type) {
+ PackageInfo pi = getPackage();
+ if (pi == null)
+ return null;
+ var ai = pi.getAnnotation(type);
+ return ai == null ? null : ai.inner();
+ }
+
+ /**
+ * Finds the real parameter type of this class.
+ *
+ * @param index The zero-based index of the parameter to resolve.
+ * @param pt The parameterized type class containing the parameterized
type to resolve (e.g. <c>HashMap</c>).
+ * @return The resolved real class.
+ */
+ public Class<?> getParameterType(int index, Class<?> pt) {
+ assertArgNotNull("pt", pt);
+
+ // We need to make up a mapping of type names.
+ var typeMap = new HashMap<Type,Type>();
+ Class<?> cc = c;
+ while (pt != cc.getSuperclass()) {
+ extractTypes(typeMap, cc);
+ cc = cc.getSuperclass();
+ assertArg(nn(cc), "Class ''{0}'' is not a subclass of
parameterized type ''{1}''", c.getSimpleName(), pt.getSimpleName());
+ }
+
+ Type gsc = cc.getGenericSuperclass();
+
+ assertArg(gsc instanceof ParameterizedType, "Class ''{0}'' is
not a parameterized type", pt.getSimpleName());
+
+ var cpt = (ParameterizedType)gsc;
+ Type[] atArgs = cpt.getActualTypeArguments();
+ assertArg(index < atArgs.length, "Invalid type index.
index={0}, argsLength={1}", index, atArgs.length);
+ Type actualType = cpt.getActualTypeArguments()[index];
+
+ if (typeMap.containsKey(actualType))
+ actualType = typeMap.get(actualType);
+
+ if (actualType instanceof Class) {
+ return (Class<?>)actualType;
+
+ } else if (actualType instanceof GenericArrayType) {
+ Type gct =
((GenericArrayType)actualType).getGenericComponentType();
+ if (gct instanceof ParameterizedType pt3)
+ return Array.newInstance((Class<?>)pt3.getRawType(),
0).getClass();
+ } else if (actualType instanceof TypeVariable<?> typeVariable) {
+ List<Class<?>> nestedOuterTypes = new LinkedList<>();
+ for (Class<?> ec = cc.getEnclosingClass(); nn(ec); ec =
ec.getEnclosingClass()) {
+ Class<?> outerClass = cc.getClass();
+ nestedOuterTypes.add(outerClass);
+ var outerTypeMap = new HashMap<Type,Type>();
+ extractTypes(outerTypeMap, outerClass);
+ for (var entry : outerTypeMap.entrySet()) {
+ var key = entry.getKey();
+ var value = entry.getValue();
+ if (key instanceof TypeVariable<?> keyType) {
+ if
(keyType.getName().equals(typeVariable.getName()) &&
isInnerClass(keyType.getGenericDeclaration(),
typeVariable.getGenericDeclaration())) {
+ if (value instanceof Class<?> c)
+ return c;
+ typeVariable =
(TypeVariable<?>)entry.getValue();
+ }
+ }
+ }
+ }
+ } else if (actualType instanceof ParameterizedType) {
+ return
(Class<?>)((ParameterizedType)actualType).getRawType();
+ }
+ throw illegalArg("Could not resolve variable ''{0}'' to a
type.", actualType.getTypeName());
+ }
+
+ /**
+ * Returns a list including this class and all parent classes.
+ *
+ * <p>
+ * Does not include interfaces.
+ *
+ * <p>
+ * Results are in child-to-parent order.
+ *
+ * @return An unmodifiable list including this class and all parent
classes.
+ * <br>Results are in child-to-parent order.
+ */
+ public List<ClassInfo> getParents() { return parents.get(); }
+
+ /**
+ * Returns the default value for this primitive class.
+ *
+ * @return The default value, or <jk>null</jk> if this is not a
primitive class.
+ */
+ public Object getPrimitiveDefault() { return
primitiveDefaultMap.get(c); }
+
+ /**
+ * If this class is a primitive wrapper (e.g.
<code><jk>Integer</jk>.<jk>class</jk></code>) returns it's
+ * primitive class (e.g. <code>int.<jk>class</jk></code>).
+ *
+ * @return The primitive class, or <jk>null</jk> if class is not a
primitive wrapper.
+ */
+ public Class<?> getPrimitiveForWrapper() { return pmap2.get(c); }
+
+ /**
+ * If this class is a primitive (e.g.
<code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class
+ * (e.g. <code>Integer.<jk>class</jk></code>).
+ *
+ * @return The wrapper class, or <jk>null</jk> if class is not a
primitive.
+ */
+ public Class<?> getPrimitiveWrapper() { return pmap1.get(c); }
+
+ /**
+ * Returns the first matching public constructor on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The public constructor that matches the specified predicate.
+ */
+ public ConstructorInfo getPublicConstructor(Predicate<ConstructorInfo>
filter) {
+ for (var ci : publicConstructors.get())
+ if (test(filter, ci))
+ return ci;
+ return null;
+ }
+
+ /**
+ * Returns all the public constructors defined on this class.
+ *
+ * @return All public constructors defined on this class.
+ */
+ public List<ConstructorInfo> getPublicConstructors() { return
publicConstructors.get(); }
+
+ /**
+ * Returns the first matching public field on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The public field, or <jk>null</jk> if not found.
+ */
+ public FieldInfo getPublicField(Predicate<FieldInfo> filter) {
+ for (var f : publicFields.get())
+ if (test(filter, f))
+ return f;
+ return null;
+ }
+
+ /**
+ * Returns all public fields on this class.
+ *
+ * <p>
+ * Hidden fields are excluded from the results.
+ *
+ * @return
+ * All public fields on this class.
+ * <br>Results are in alphabetical order.
+ * <br>List is unmodifiable.
+ */
+ public List<FieldInfo> getPublicFields() { return publicFields.get(); }
+
+ /**
+ * Returns the first matching public method on this class.
+ *
+ * @param filter A predicate to apply to the entries to determine if
value should be used. Can be <jk>null</jk>.
+ * @return The first matching method, or <jk>null</jk> if no methods
matched.
+ */
+ public MethodInfo getPublicMethod(Predicate<MethodInfo> filter) {
+ for (var mi : publicMethods.get())
+ if (test(filter, mi))
+ return mi;
+ return null;
+ }
+
+ /**
+ * Returns all public methods on this class.
+ *
+ * <p>
+ * Methods defined on the {@link Object} class are excluded from the
results.
+ *
+ * @return
+ * All public methods on this class.
+ * <br>Results are ordered alphabetically.
+ */
+ public List<MethodInfo> getPublicMethods() { return
publicMethods.get(); }
+
+ /**
+ * Same as {@link #getNameSimple()} but uses <js>"Array"</js> instead
of <js>"[]"</js>.
+ *
+ * @return The readable name for this class.
+ */
+ public String getNameReadable() {
+ return readableName.get();
+ }
+
+ /**
+ * Returns the repeated annotation method on this class.
+ *
+ * <p>
+ * The repeated annotation method is the <code>value()</code> method
that returns an array
+ * of annotations who themselves are marked with the {@link Repeatable
@Repeatable} annotation
+ * of this class.
+ *
+ * @return The repeated annotation method on this class, or
<jk>null</jk> if it doesn't exist.
+ */
+ public MethodInfo getRepeatedAnnotationMethod() {
+ return repeatedAnnotationMethod.get();
+ }
+
+ /**
+ * Returns the short name of the underlying class.
+ *
+ * <p>
+ * Similar to {@link #getNameSimple()} but also renders local or member
class name prefixes.
+ *
+ * @return The short name of the underlying class.
+ */
+ public String getNameShort() {
+ return shortName.get();
+ }
+
+ /**
+ * Returns the simple name of the underlying class.
+ *
+ * <p>
+ * Returns either {@link Class#getSimpleName()} or {@link
Type#getTypeName()} depending on whether
+ * this is a class or type.
+ *
+ * @return The simple name of the underlying class;
+ */
+ public String getNameSimple() { return nn(c) ? c.getSimpleName() :
t.getTypeName(); }
+
+ /**
+ * Returns a formatted class name with configurable options.
+ *
+ * <p>
+ * This is a unified method that can produce output equivalent to all
other name methods
+ * by varying the parameters.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Given: java.util.HashMap<String,Integer>[]</jc>
+ *
+ * <jc>// Full name with generics</jc>
+ * getFormattedName(ClassNameFormat.<jsf>FULL</jsf>,
<jk>true</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
+ * <jc>// →
"java.util.HashMap<java.lang.String,java.lang.Integer>[]"</jc>
+ *
+ * <jc>// Short name with generics</jc>
+ * getFormattedName(ClassNameFormat.<jsf>SHORT</jsf>,
<jk>true</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
+ * <jc>// → "HashMap<String,Integer>[]"</jc>
+ *
+ * <jc>// Simple name</jc>
+ * getFormattedName(ClassNameFormat.<jsf>SIMPLE</jsf>,
<jk>false</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
+ * <jc>// → "HashMap[]"</jc>
+ *
+ * <jc>// With dot separator</jc>
+ * getFormattedName(ClassNameFormat.<jsf>SHORT</jsf>,
<jk>false</jk>, '.', ClassArrayFormat.<jsf>BRACKETS</jsf>)
+ * <jc>// → "Map.Entry"</jc>
+ *
+ * <jc>// Word format for arrays</jc>
+ * getFormattedName(ClassNameFormat.<jsf>SIMPLE</jsf>,
<jk>false</jk>, '$', ClassArrayFormat.<jsf>WORD</jsf>)
+ * <jc>// → "HashMapArray"</jc>
+ * </p>
+ *
+ * <h5 class='section'>Equivalent Methods:</h5>
+ * <ul class='spaced-list'>
+ * <li><c>getName()</c> = <c>getFormattedName(FULL,
<jk>false</jk>, '$', JVM)</c>
+ * <li><c>getCanonicalName()</c> = <c>getFormattedName(FULL,
<jk>false</jk>, '.', BRACKETS)</c>
+ * <li><c>getSimpleName()</c> = <c>getFormattedName(SIMPLE,
<jk>false</jk>, '$', BRACKETS)</c>
+ * <li><c>getFullName()</c> = <c>getFormattedName(FULL,
<jk>true</jk>, '$', BRACKETS)</c>
+ * <li><c>getShortName()</c> = <c>getFormattedName(SHORT,
<jk>true</jk>, '$', BRACKETS)</c>
+ * <li><c>getReadableName()</c> = <c>getFormattedName(SIMPLE,
<jk>false</jk>, '$', WORD)</c>
+ * </ul>
+ *
+ * @param nameFormat
+ * Controls which parts of the class name to include (package,
outer classes).
+ * @param includeTypeParams
+ * If <jk>true</jk>, include generic type parameters recursively.
+ * <br>For example: <js>"HashMap<String,Integer>"</js>
instead of <js>"HashMap"</js>
+ * @param separator
+ * Character to use between outer and inner class names.
+ * <br>Typically <js>'$'</js> (JVM format) or <js>'.'</js>
(canonical format).
+ * <br>Ignored when <c>nameFormat</c> is {@link
ClassNameFormat#SIMPLE}.
+ * @param arrayFormat
+ * How to format array dimensions.
+ * @return
+ * The formatted class name.
+ */
+ public String getNameFormatted(ClassNameFormat nameFormat, boolean
includeTypeParams, char separator, ClassArrayFormat arrayFormat) {
+ var sb = new StringBuilder(128);
+ appendNameFormatted(sb, nameFormat, includeTypeParams,
separator, arrayFormat);
+ return sb.toString();
+ }
+
+ /**
+ * Appends a formatted class name to a StringBuilder with configurable
options.
+ *
+ * <p>
+ * This is the core implementation method used by all other name
formatting methods.
+ * Using this method directly avoids String allocations when building
complex strings.
+ * The method is recursive to handle nested generic type parameters.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * StringBuilder sb = <jk>new</jk> StringBuilder();
+ * sb.append(<js>"Class: "</js>);
+ *
ClassInfo.<jsm>of</jsm>(HashMap.<jk>class</jk>).appendFormattedName(sb,
ClassNameFormat.<jsf>FULL</jsf>, <jk>true</jk>, '$',
ClassArrayFormat.<jsf>BRACKETS</jsf>);
+ * <jc>// sb now contains: "Class: java.util.HashMap"</jc>
+ * </p>
+ *
+ * @param sb
+ * The StringBuilder to append to.
+ * @param nameFormat
+ * Controls which parts of the class name to include (package,
outer classes).
+ * @param includeTypeParams
+ * If <jk>true</jk>, include generic type parameters recursively.
+ * @param separator
+ * Character to use between outer and inner class names.
+ * <br>Ignored when <c>nameFormat</c> is {@link
ClassNameFormat#SIMPLE}.
+ * @param arrayFormat
+ * How to format array dimensions.
+ * @return
+ * The same StringBuilder for method chaining.
+ */
+ @SuppressWarnings("null")
+ public StringBuilder appendNameFormatted(StringBuilder sb,
ClassNameFormat nameFormat, boolean includeTypeParams, char separator,
ClassArrayFormat arrayFormat) {
+ var dim = getDimensions();
+
+ // Handle arrays - format component type recursively, then add
array notation
+ if (dim > 0) {
+ var componentType = getComponentType();
+ componentType.appendNameFormatted(sb, nameFormat,
includeTypeParams, separator, arrayFormat);
+
+ if (arrayFormat == ClassArrayFormat.WORD) {
+ for (int i = 0; i < dim; i++)
+ sb.append("Array");
+ } else if (arrayFormat == ClassArrayFormat.BRACKETS) {
+ for (int i = 0; i < dim; i++)
+ sb.append("[]");
+ }
+ // JVM format is already in getName() - would need
special handling
+
+ return sb;
+ }
+
+ // Get the raw class - for ParameterizedType, extract the raw
type
+ var ct = c;
+ if (ct == null && isParameterizedType) {
+ var pt = (ParameterizedType)t;
+ ct = (Class<?>)pt.getRawType();
+ }
+
+ // Append base class name based on format
+ switch (nameFormat) {
+ case FULL:
+ // Full package name + outer classes
+ if (nn(ct)) {
+ sb.append(ct.getName());
+ // Apply separator if not '$'
+ if (separator != '$' && sb.indexOf("$")
!= -1) {
+ for (int i = 0; i <
sb.length(); i++) {
+ if (sb.charAt(i) == '$')
+ sb.setCharAt(i,
separator);
+ }
+ }
+ } else {
+ sb.append(t.getTypeName());
+ }
+ break;
+
+ case SHORT:
+ // Outer classes but no package
+ if (nn(ct)) {
+ if (ct.isLocalClass()) {
+ // Local class: include
enclosing class simple name
+
sb.append(of(ct.getEnclosingClass()).getNameSimple())
+ .append(separator)
+ .append(ct.getSimpleName());
+ } else if (ct.isMemberClass()) {
+ // Member class: include
declaring class simple name
+
sb.append(of(ct.getDeclaringClass()).getNameSimple())
+ .append(separator)
+ .append(ct.getSimpleName());
+ } else {
+ // Regular class: just simple
name
+ sb.append(ct.getSimpleName());
+ }
+ } else {
+ sb.append(t.getTypeName());
+ }
+ break;
+
+ default /* SIMPLE */:
+ // Simple name only - no package, no outer
classes
+ if (nn(ct)) {
+ sb.append(ct.getSimpleName());
+ } else {
+ sb.append(t.getTypeName());
+ }
+ break;
+ }
+
+ // Append type parameters if requested
+ if (includeTypeParams && isParameterizedType) {
+ var pt = (ParameterizedType)t;
+ sb.append('<');
+ var first = true;
+ for (var t2 : pt.getActualTypeArguments()) {
+ if (!first)
+ sb.append(',');
+ first = false;
+ of(t2).appendNameFormatted(sb, nameFormat,
includeTypeParams, separator, arrayFormat);
+ }
+ sb.append('>');
+ }
+
+ return sb;
+ }
+
+ /**
+ * Returns the parent class.
+ *
+ * @return
+ * The parent class, or <jk>null</jk> if the class has no parent.
+ */
+ public ClassInfo getSuperclass() { return c == null ? null :
of(c.getSuperclass()); }
+
+ /**
+ * Returns the nest host of this class.
+ *
+ * <p>
+ * Every class belongs to exactly one nest. A class that is not a
member of a nest
+ * is its own nest host.
+ *
+ * @return The nest host of this class.
+ */
+ public ClassInfo getNestHost() {
+ return c == null ? null : of(c.getNestHost());
+ }
+
+ /**
+ * Returns an array containing all the classes and interfaces that are
members of the nest
+ * to which this class belongs.
+ *
+ * @return
+ * An unmodifiable list of all classes and interfaces in the same
nest as this class.
+ * <br>Returns an empty list if this object does not represent a
class.
+ */
+ public List<ClassInfo> getNestMembers() {
+ if (c == null)
+ return u(l());
+ var members = c.getNestMembers();
+ List<ClassInfo> l = listOfSize(members.length);
+ for (Class<?> cc : members)
+ l.add(of(cc));
+ return u(l);
+ }
+
+ /**
+ * Returns the permitted subclasses of this sealed class.
+ *
+ * <p>
+ * If this class is not sealed, returns an empty list.
+ *
+ * @return
+ * An unmodifiable list of permitted subclasses if this is a
sealed class.
+ * <br>Returns an empty list if this class is not sealed.
+ */
+ public List<ClassInfo> getPermittedSubclasses() {
+ if (c == null || ! c.isSealed())
+ return u(l());
+ Class<?>[] permitted = c.getPermittedSubclasses();
+ List<ClassInfo> l = listOfSize(permitted.length);
+ for (Class<?> cc : permitted)
+ l.add(of(cc));
+ return u(l);
+ }
+
+ /**
+ * Returns the record components of this record class.
+ *
+ * <p>
+ * Returns a cached, unmodifiable list of record components.
+ * The components are returned in the same order as they appear in the
record declaration.
+ * If this class is not a record, returns an empty list.
+ *
+ * @return An unmodifiable list of record components, or an empty list
if this class is not a record.
+ */
+ public List<RecordComponent> getRecordComponents() {
+ return recordComponents.get();
+ }
+
+ /**
+ * Returns the {@link Type} representing the direct superclass of this
class.
+ *
+ * <p>
+ * If the superclass is a parameterized type, the {@link Type} returned
reflects the actual
+ * type parameters used in the source code.
+ *
+ * @return
+ * The superclass of this class as a {@link Type},
+ * or <jk>null</jk> if this class represents {@link Object}, an
interface, a primitive type, or void.
+ */
+ public Type getGenericSuperclass() {
+ return c == null ? null : c.getGenericSuperclass();
+ }
+
+ /**
+ * Returns the {@link Type}s representing the interfaces directly
implemented by this class.
+ *
+ * <p>
+ * Returns a cached, unmodifiable list.
+ * If a superinterface is a parameterized type, the {@link Type}
returned for it reflects the actual
+ * type parameters used in the source code.
+ *
+ * @return
+ * An unmodifiable list of {@link Type}s representing the
interfaces directly implemented by this class.
+ * <br>Returns an empty list if this class implements no
interfaces.
+ */
+ public List<Type> getGenericInterfaces() {
+ return genericInterfaces.get();
+ }
+
+ /**
+ * Returns a list of {@link TypeVariable} objects that represent the
type variables declared by this class.
+ *
+ * <p>
+ * Returns a cached, unmodifiable list.
+ * The type variables are returned in the same order as they appear in
the class declaration.
+ *
+ * @return
+ * An unmodifiable list of {@link TypeVariable} objects
representing the type parameters of this class.
+ * <br>Returns an empty list if this class declares no type
parameters.
+ */
+ public List<TypeVariable<?>> getTypeParameters() {
+ return typeParameters.get();
+ }
+
+ /**
+ * Returns an {@link AnnotatedType} object that represents the
annotated superclass of this class.
+ *
+ * <p>
+ * If this class represents a class type whose superclass is annotated,
the returned object reflects
+ * the annotations used in the source code to declare the superclass.
+ *
+ * @return
+ * An {@link AnnotatedType} object representing the annotated
superclass,
+ * or <jk>null</jk> if this class represents {@link Object}, an
interface, a primitive type, or void.
+ */
+ public AnnotatedType getAnnotatedSuperclass() {
+ return c == null ? null : c.getAnnotatedSuperclass();
+ }
+
+ /**
+ * Returns a list of {@link AnnotatedType} objects that represent the
annotated interfaces
+ * implemented by this class.
+ *
+ * <p>
+ * Returns a cached, unmodifiable list.
+ * If this class represents a class or interface whose superinterfaces
are annotated,
+ * the returned objects reflect the annotations used in the source code
to declare the superinterfaces.
+ *
+ * @return
+ * An unmodifiable list of {@link AnnotatedType} objects
representing the annotated superinterfaces.
+ * <br>Returns an empty list if this class implements no
interfaces.
+ */
+ public List<AnnotatedType> getAnnotatedInterfaces() {
+ return annotatedInterfaces.get();
+ }
+
+ /**
+ * Returns the {@link ProtectionDomain} of this class.
+ *
+ * <p>
+ * If a security manager is installed, this method requires
<c>RuntimePermission("getProtectionDomain")</c>.
+ *
+ * @return The {@link ProtectionDomain} of this class, or <jk>null</jk>
if the class does not have a protection domain.
+ */
+ public java.security.ProtectionDomain getProtectionDomain() {
+ return c == null ? null : c.getProtectionDomain();
+ }
+
+ /**
+ * Returns the signers of this class.
+ *
+ * <p>
+ * Returns a cached, unmodifiable list.
+ *
+ * @return An unmodifiable list of signers, or an empty list if there
are no signers.
+ */
+ public List<Object> getSigners() {
+ return signers.get();
+ }
+
+ /**
+ * Finds a resource with a given name.
+ *
+ * <p>
+ * The rules for searching resources associated with a given class are
implemented by the defining class loader.
+ *
+ * @param name The resource name.
+ * @return A URL object for reading the resource, or <jk>null</jk> if
the resource could not be found.
+ */
+ public java.net.URL getResource(String name) {
+ return c == null ? null : c.getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name and returns an input stream for
reading the resource.
+ *
+ * <p>
+ * The rules for searching resources associated with a given class are
implemented by the defining class loader.
+ *
+ * @param name The resource name.
+ * @return An input stream for reading the resource, or <jk>null</jk>
if the resource could not be found.
+ */
+ public java.io.InputStream getResourceAsStream(String name) {
+ return c == null ? null : c.getResourceAsStream(name);
+ }
+
+ /**
+ * Returns the descriptor string of this class.
+ *
+ * <p>
+ * The descriptor is a string representing the type of the class, as
specified in JVMS 4.3.2.
+ * For example, the descriptor for <c>String</c> is
<js>"Ljava/lang/String;"</js>.
+ *
+ * @return The descriptor string of this class.
+ */
+ public String descriptorString() {
+ return c == null ? null : c.descriptorString();
+ }
+
+ /**
+ * If this class is a primitive (e.g.
<code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class
+ * (e.g. <code>Integer.<jk>class</jk></code>).
+ *
+ * @return The wrapper class if it's primitive, or the same class if
class is not a primitive.
+ */
+ public Class<?> getWrapperIfPrimitive() {
+ if (nn(c) && ! c.isPrimitive())
+ return c;
+ return pmap1.get(c);
+ }
+
+ /**
+ * Same as {@link #getWrapperIfPrimitive()} but wraps it in a {@link
ClassInfo}.
+ *
+ * @return The wrapper class if it's primitive, or the same class if
class is not a primitive.
+ */
+ public ClassInfo getWrapperInfoIfPrimitive() {
+ if (c == null || ! c.isPrimitive())
+ return this;
+ return of(pmap1.get(c));
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class has the specified annotation.
+ *
+ * @param <A> The annotation type to look for.
+ * @param annotationProvider The annotation provider.
+ * @param type The annotation to look for.
+ * @return The <jk>true</jk> if annotation if found.
+ */
+ public <A extends Annotation> boolean hasAnnotation(AnnotationProvider
annotationProvider, Class<A> type) {
+ if (annotationProvider == null)
+ throw unsupportedOp();
+ // Inline Context.firstAnnotation() call
+ return nn(annotationProvider.getAnnotationProvider().find(type,
c).map(x -> x.inner()).filter(x -> true).findFirst().orElse(null));
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class has the specified annotation.
+ *
+ * @param <A> The annotation type to look for.
+ * @param type The annotation to look for.
+ * @return The <jk>true</jk> if annotation if found.
+ */
+ public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
+ return getAnnotationInfos(type).findFirst().isPresent();
+ }
+
+ @Override
+ public int hashCode() {
+ return t.hashCode();
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class doesn't have the specified
annotation.
+ *
+ * @param <A> The annotation type to look for.
+ * @param type The annotation to look for.
+ * @return The <jk>true</jk> if annotation if not found.
+ */
+ public <A extends Annotation> boolean hasNoAnnotation(Class<A> type) {
+ return ! hasAnnotation(type);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is not in the root package.
+ *
+ * @return <jk>true</jk> if this class is not in the root package.
+ */
+ public boolean hasPackage() {
+ return nn(getPackage());
+ }
+
+ /**
+ * Returns <jk>true</jk> if the {@link #getPrimitiveWrapper()} method
returns a value.
+ *
+ * @return <jk>true</jk> if the {@link #getPrimitiveWrapper()} method
returns a value.
+ */
+ public boolean hasPrimitiveWrapper() {
+ return pmap1.containsKey(c);
+ }
+
+ /**
+ * Returns the wrapped class as a {@link Class}.
+ *
+ * @param <T> The inner class type.
+ * @return The wrapped class as a {@link Class}, or <jk>null</jk> if
it's not a class (e.g. it's a {@link ParameterizedType}).
+ */
+ public <T> Class<T> inner() {
+ return (Class<T>)c;
+ }
+
+ /**
+ * Returns the wrapped class as a {@link Type}.
+ *
+ * @return The wrapped class as a {@link Type}.
+ */
+ public Type innerType() {
+ return t;
+ }
+
+ /**
+ * Checks for equality with the specified class.
+ *
+ * @param c The class to check equality with.
+ * @return <jk>true</jk> if the specified class is the same as this one.
+ */
+ public boolean is(Class<?> c) {
+ return nn(this.c) && this.c.equals(c);
+ }
+
+ /**
+ * Checks for equality with the specified class.
+ *
+ * @param c The class to check equality with.
+ * @return <jk>true</jk> if the specified class is the same as this one.
+ */
+ public boolean is(ClassInfo c) {
+ if (nn(this.c))
+ return this.c.equals(c.inner());
+ return t.equals(c.t);
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to this
class.
+ *
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to this
class.
+ */
+ @Override
+ public boolean is(ElementFlag flag) {
+ return switch (flag) {
+ case ANNOTATION -> isAnnotation();
+ case NOT_ANNOTATION -> !isAnnotation();
+ case ANONYMOUS -> isAnonymousClass();
+ case NOT_ANONYMOUS -> !isAnonymousClass();
+ case ARRAY -> isArray();
+ case NOT_ARRAY -> !isArray();
+ case CLASS -> !isInterface();
+ case DEPRECATED -> isDeprecated();
+ case NOT_DEPRECATED -> isNotDeprecated();
+ case ENUM -> isEnum();
+ case NOT_ENUM -> !isEnum();
+ case LOCAL -> isLocalClass();
+ case NOT_LOCAL -> !isLocalClass();
+ case MEMBER -> isMemberClass();
+ case NOT_MEMBER -> isNotMemberClass();
+ case NON_STATIC_MEMBER -> isNonStaticMemberClass();
+ case NOT_NON_STATIC_MEMBER -> !isNonStaticMemberClass();
+ case PRIMITIVE -> isPrimitive();
+ case NOT_PRIMITIVE -> !isPrimitive();
+ case RECORD -> isRecord();
+ case NOT_RECORD -> !isRecord();
+ case SEALED -> isSealed();
+ case NOT_SEALED -> !isSealed();
+ case SYNTHETIC -> isSynthetic();
+ case NOT_SYNTHETIC -> !isSynthetic();
+ default -> super.is(flag);
+ };
+ }
+
+ @Override
+ public boolean isAll(ElementFlag...flags) {
+ return stream(flags).allMatch(this::is);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is an annotation.
+ *
+ * @return <jk>true</jk> if this class is an annotation.
+ */
+ public boolean isAnnotation() { return nn(c) && c.isAnnotation(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is an anonymous class.
+ *
+ * <p>
+ * An anonymous class is a local class declared within a method or
constructor that has no name.
+ *
+ * @return <jk>true</jk> if this class is an anonymous class.
+ */
+ public boolean isAnonymousClass() { return nn(c) &&
c.isAnonymousClass(); }
+
+ @Override
+ public boolean isAny(ElementFlag...flags) {
+ return stream(flags).anyMatch(this::is);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is any of the specified types.
+ *
+ * @param types The types to check against.
+ * @return <jk>true</jk> if this class is any of the specified types.
+ */
+ public boolean isAny(Class<?>...types) {
+ for (var cc : types)
+ if (is(cc))
+ return true;
+ return false;
+ }
+
+
+ /**
+ * Returns <jk>true</jk> if this class is an array.
+ *
+ * @return <jk>true</jk> if this class is an array.
+ */
+ public boolean isArray() { return nn(c) && c.isArray(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a child or the same as
<c>parent</c>.
+ *
+ * @param parent The parent class.
+ * @return <jk>true</jk> if this class is a child or the same as
<c>parent</c>.
+ */
+ public boolean isChildOf(Class<?> parent) {
+ return nn(c) && nn(parent) && parent.isAssignableFrom(c);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a child or the same as
<c>parent</c>.
+ *
+ * @param parent The parent class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>parent</c>.
+ */
+ public boolean isChildOf(ClassInfo parent) {
+ return isChildOf(parent.inner());
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a child or the same as
<c>parent</c>.
+ *
+ * @param parent The parent class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>parent</c>.
+ */
+ public boolean isChildOf(Type parent) {
+ if (parent instanceof Class)
+ return isChildOf((Class<?>)parent);
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a child or the same as any of
the <c>parents</c>.
+ *
+ * @param parents The parents class.
+ * @return <jk>true</jk> if this class is a child or the same as any of
the <c>parents</c>.
+ */
+ public boolean isChildOfAny(Class<?>...parents) {
+ for (var p : parents)
+ if (isChildOf(p))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is not an interface.
+ *
+ * @return <jk>true</jk> if this class is not an interface.
+ */
+ public boolean isClass() { return nn(c) && ! c.isInterface(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link Collection} or an
array.
+ *
+ * @return <jk>true</jk> if this class is a {@link Collection} or an
array.
+ */
+ public boolean isCollectionOrArray() { return nn(c) &&
(Collection.class.isAssignableFrom(c) || c.isArray()); }
+
+ /**
+ * Returns <jk>true</jk> if this class has the {@link Deprecated
@Deprecated} annotation on it.
+ *
+ * @return <jk>true</jk> if this class has the {@link Deprecated
@Deprecated} annotation on it.
+ */
+ public boolean isDeprecated() { return nn(c) &&
c.isAnnotationPresent(Deprecated.class); }
+
+ /**
+ * Returns <jk>true</jk> if this class is an enum.
+ *
+ * @return <jk>true</jk> if this class is an enum.
+ */
+ public boolean isEnum() { return nn(c) && c.isEnum(); }
+
+ /**
+ * Returns <jk>true</jk> if the specified value is an instance of this
class.
+ *
+ * @param value The value to check.
+ * @return <jk>true</jk> if the specified value is an instance of this
class.
+ */
+ public boolean isInstance(Object value) {
+ if (nn(this.c))
+ return c.isInstance(value);
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a local class.
+ *
+ * @return <jk>true</jk> if this class is a local class.
+ */
+ public boolean isLocalClass() { return nn(c) && c.isLocalClass(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a member class.
+ *
+ * @return <jk>true</jk> if this class is a member class.
+ */
+ public boolean isMemberClass() { return nn(c) && c.isMemberClass(); }
+
+ /**
+ * Determines if this class and the specified class are nestmates.
+ *
+ * <p>
+ * Two classes are nestmates if they belong to the same nest.
+ *
+ * @param c The class to check.
+ * @return <jk>true</jk> if this class and the specified class are
nestmates.
+ */
+ public boolean isNestmateOf(Class<?> c) {
+ return nn(this.c) && nn(c) && this.c.isNestmateOf(c);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a member class and not static.
+ *
+ * @return <jk>true</jk> if this class is a member class and not static.
+ */
+ public boolean isNonStaticMemberClass() { return nn(c) &&
c.isMemberClass() && ! isStatic(); }
+
+ /**
+ * Returns <jk>true</jk> if this class doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ *
+ * @return <jk>true</jk> if this class doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ */
+ public boolean isNotDeprecated() { return c == null || !
c.isAnnotationPresent(Deprecated.class); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a local class.
+ *
+ * @return <jk>true</jk> if this class is a local class.
+ */
+ public boolean isNotLocalClass() { return c == null || !
c.isLocalClass(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a member class.
+ *
+ * @return <jk>true</jk> if this class is a member class.
+ */
+ public boolean isNotMemberClass() { return c == null || !
c.isMemberClass(); }
+
+ /**
+ * Returns <jk>false</jk> if this class is a member class and not
static.
+ *
+ * @return <jk>false</jk> if this class is a member class and not
static.
+ */
+ public boolean isNotNonStaticMemberClass() { return !
isNonStaticMemberClass(); }
+
+ /**
+ * Returns <jk>true</jk> if this is not a primitive class.
+ *
+ * @return <jk>true</jk> if this is not a primitive class.
+ */
+ public boolean isNotPrimitive() { return c == null || !
c.isPrimitive(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ */
+ public boolean isParentOf(Class<?> child) {
+ return nn(c) && nn(child) && c.isAssignableFrom(child);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ */
+ public boolean isParentOf(Type child) {
+ if (child instanceof Class)
+ return isParentOf((Class<?>)child);
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ *
+ * <p>
+ * Primitive classes are converted to wrapper classes and compared.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ *
ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfLenient(String.<jk>class</jk>);
<jc>// true</jc>
+ *
ClassInfo.<jsm>of</jsm>(CharSequence.<jk>class</jk>).isParentOfLenient(String.<jk>class</jk>);
<jc>// true</jc>
+ *
ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfLenient(CharSequence.<jk>class</jk>);
<jc>// false</jc>
+ *
ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(Integer.<jk>class</jk>);
<jc>// true</jc>
+ *
ClassInfo.<jsm>of</jsm>(Integer.<jk>class</jk>).isParentOfLenient(<jk>int</jk>.<jk>class</jk>);
<jc>// true</jc>
+ *
ClassInfo.<jsm>of</jsm>(Number.<jk>class</jk>).isParentOfLenient(<jk>int</jk>.<jk>class</jk>);
<jc>// true</jc>
+ *
ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(Number.<jk>class</jk>);
<jc>// false</jc>
+ *
ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(<jk>long</jk>.<jk>class</jk>);
<jc>// false</jc>
+ * </p>
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ */
+ public boolean isParentOfLenient(Class<?> child) {
+ if (c == null || child == null)
+ return false;
+ if (c.isAssignableFrom(child))
+ return true;
+ if (this.isPrimitive() || child.isPrimitive()) {
+ return
this.getWrapperIfPrimitive().isAssignableFrom(of(child).getWrapperIfPrimitive());
+ }
+ return false;
+ }
+
+ /**
+ * Same as {@link #isParentOfLenient(Class)} but takes in a {@link
ClassInfo}.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ */
+ public boolean isParentOfLenient(ClassInfo child) {
+ if (c == null || child == null)
+ return false;
+ if (c.isAssignableFrom(child.inner()))
+ return true;
+ if (this.isPrimitive() || child.isPrimitive()) {
+ return
this.getWrapperIfPrimitive().isAssignableFrom(child.getWrapperIfPrimitive());
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as
<c>child</c>.
+ */
+ public boolean isParentOfLenient(Type child) {
+ if (child instanceof Class)
+ return isParentOfLenient((Class<?>)child);
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this is a primitive class.
+ *
+ * @return <jk>true</jk> if this is a primitive class.
+ */
+ public boolean isPrimitive() { return nn(c) && c.isPrimitive(); }
+
+ /**
+ * Returns <jk>true</jk> if this is a repeated annotation class.
+ *
+ * <p>
+ * A repeated annotation has a single <code>value()</code> method that
returns an array
+ * of annotations who themselves are marked with the {@link Repeatable
@Repeatable} annotation
+ * of this class.
+ *
+ * @return <jk>true</jk> if this is a repeated annotation class.
+ */
+ public boolean isRepeatedAnnotation() {
+ return getRepeatedAnnotationMethod() != null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link RuntimeException}.
+ *
+ * @return <jk>true</jk> if this class is a {@link RuntimeException}.
+ */
+ public boolean isRuntimeException() { return
isChildOf(RuntimeException.class); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a record class.
+ *
+ * <p>
+ * A record class is a final class that extends {@link
java.lang.Record}.
+ *
+ * @return <jk>true</jk> if this class is a record class.
+ */
+ public boolean isRecord() { return nn(c) && c.isRecord(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a sealed class.
+ *
+ * <p>
+ * A sealed class is a class that can only be extended by a permitted
set of subclasses.
+ *
+ * @return <jk>true</jk> if this class is a sealed class.
+ */
+ public boolean isSealed() { return nn(c) && c.isSealed(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a synthetic class.
+ *
+ * <p>
+ * A synthetic class is one that is generated by the compiler and does
not appear in source code.
+ *
+ * @return <jk>true</jk> if this class is synthetic.
+ */
+ public boolean isSynthetic() { return nn(c) && c.isSynthetic(); }
+
+ /**
+ * Returns <jk>true</jk> if this class is a child of <c>parent</c>.
+ *
+ * @param parent The parent class.
+ * @return <jk>true</jk> if this class is a parent of <c>child</c>.
+ */
+ public boolean isStrictChildOf(Class<?> parent) {
+ return nn(c) && nn(parent) && parent.isAssignableFrom(c) && !
c.equals(parent);
+ }
+
+ /**
+ * Identifies if the specified visibility matches this constructor.
+ *
+ * @param v The visibility to validate against.
+ * @return <jk>true</jk> if this visibility matches the modifier
attribute of this constructor.
+ */
+ public boolean isVisible(Visibility v) {
+ return nn(c) && v.isVisible(c);
+ }
+
+ /**
+ * Returns the last matching annotation on this class and
superclasses/interfaces.
+ *
+ * <p>
+ * Annotations are searched in the following orders:
+ * <ol>
+ * <li>On this class.
+ * <li>On parent classes ordered child-to-parent.
+ * <li>On interfaces ordered child-to-parent.
+ * <li>On the package of this class.
+ * </ol>
+ *
+ * @param <A> The annotation type to look for.
+ * @param annotationProvider The annotation provider.
+ * @param type The annotation to look for.
+ * @param filter A predicate to apply to the entries to determine if
annotation should be returned. Can be <jk>null</jk>.
+ * @return This object.
+ */
+ public <A extends Annotation> A lastAnnotation(AnnotationProvider
annotationProvider, Class<A> type, Predicate<A> filter) {
+ if (annotationProvider == null)
+ throw unsupportedOp();
+ A x = null;
+ x = annotationProvider.getAnnotationProvider().find(type,
inner()).map(y -> y.inner()).filter(y ->
filter.test(y)).findFirst().orElse(null);
+ if (nn(x) && test(filter, x))
+ return x;
+ var parents2 = parents.get();
+ for (var parent : parents2) {
+ x =
annotationProvider.getAnnotationProvider().find(type, parent.inner()).map(y ->
y.inner()).filter(y -> filter.test(y)).findFirst().orElse(null);
+ if (nn(x))
+ return x;
+ }
+ var interfaces2 = interfaces.get();
+ for (var element : interfaces2) {
+ x =
annotationProvider.getAnnotationProvider().find(type, element.inner()).map(y ->
y.inner()).filter(y -> filter.test(y)).findFirst().orElse(null);
+ if (nn(x))
+ return x;
+ }
+ x = getPackageAnnotation(type);
+ if (nn(x) && test(filter, x))
+ return x;
+ return null;
+ }
+
+ /**
+ * Returns the last matching annotation on this class and
superclasses/interfaces.
+ *
+ * <p>
+ * Annotations are searched in the following orders:
+ * <ol>
+ * <li>On this class.
+ * <li>On parent classes ordered child-to-parent.
+ * <li>On interfaces ordered child-to-parent.
+ * <li>On the package of this class.
+ * </ol>
+ *
+ * @param <A> The annotation type to look for.
+ * @param type The annotation to look for.
+ * @param filter A predicate to apply to the entries to determine if
annotation should be returned. Can be <jk>null</jk>.
+ * @return This object.
+ */
+ public <A extends Annotation> A lastAnnotation(Class<A> type,
Predicate<A> filter) {
+ // Inline implementation using reflection directly instead of
delegating to AnnotationProvider
+ if (!nn(type))
+ return null;
+
+ // Search annotations using reflection (reverse order for
"last")
+ var annotations = rstream(l(c.getAnnotationsByType(type)));
+ var result = annotations.filter(a -> test(filter,
a)).findFirst().orElse(null);
+ if (nn(result))
+ return result;
+
+ // Search parents
+ var parents2 = parents.get();
+ for (var parent : parents2) {
+ var parentAnnotations =
rstream(l(parent.inner().getAnnotationsByType(type)));
+ result = parentAnnotations.filter(a -> test(filter,
a)).findFirst().orElse(null);
+ if (nn(result))
+ return result;
+ }
+
+ // Search interfaces
+ var interfaces2 = interfaces.get();
+ for (var iface : interfaces2) {
+ var ifaceAnnotations =
rstream(l(iface.inner().getAnnotationsByType(type)));
+ result = ifaceAnnotations.filter(a -> test(filter,
a)).findFirst().orElse(null);
+ if (nn(result))
+ return result;
+ }
+
+ // Search package
+ return getPackageAnnotation(type);
+ }
+
+ /**
+ * Shortcut for calling
<c>Class.getDeclaredConstructor().newInstance()</c> on the underlying class.
+ *
+ * @return A new instance of the underlying class
+ * @throws ExecutableException Exception occurred on invoked
constructor/method/field.
+ */
+ public Object newInstance() throws ExecutableException {
+ if (c == null)
+ throw new ExecutableException("Type ''{0}'' cannot be
instantiated", getNameFull());
+ try {
+ return c.getDeclaredConstructor().newInstance();
+ } catch (InvocationTargetException | NoSuchMethodException |
InstantiationException | IllegalAccessException e) {
+ throw new ExecutableException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return t.toString();
+ }
+
+ /**
+ * Unwrap this class if it's a parameterized type of the specified type
such as {@link Value} or {@link Optional}.
+ *
+ * @param wrapperTypes The parameterized types to unwrap if this class
is one of those types.
+ * @return The class info on the unwrapped type, or just this type if
this isn't one of the specified types.
+ */
+ public ClassInfo unwrap(Class<?>...wrapperTypes) {
+ for (var wt : wrapperTypes) {
+ if (isParameterizedTypeOf(wt)) {
+ Type t = getFirstParameterType(wt);
+ if (nn(t))
+ return of(t).unwrap(wrapperTypes); //
Recursively do it again.
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Casts an object to the class represented by this {@link ClassInfo}
object.
+ *
+ * @param <T> The type to cast to.
+ * @param obj The object to be cast.
+ * @return The object after casting, or <jk>null</jk> if obj is
<jk>null</jk>.
+ * @throws ClassCastException If the object is not <jk>null</jk> and is
not assignable to this class.
+ */
+ public <T> T cast(Object obj) {
+ return c == null ? null : (T)c.cast(obj);
+ }
+
+ /**
+ * Casts this {@link ClassInfo} object to represent a subclass of the
class represented by the specified class object.
+ *
+ * @param <U> The type to cast to.
+ * @param clazz The class of the type to cast to.
+ * @return This {@link ClassInfo} object, cast to represent a subclass
of the specified class object.
+ * @throws ClassCastException If this class is not assignable to the
specified class.
+ */
+ public <U> ClassInfo asSubclass(Class<U> clazz) {
+ if (c == null)
+ return null;
+ c.asSubclass(clazz); // Throws ClassCastException if not
assignable
+ return this;
+ }
+
+ /**
+ * Returns a {@link ClassInfo} for an array type whose component type
is this class.
+ *
+ * @return A {@link ClassInfo} representing an array type whose
component type is this class.
+ */
+ public ClassInfo arrayType() {
+ return c == null ? null : of(c.arrayType());
+ }
+
+ /**
+ * Returns the component type of this class if it is an array type.
+ *
+ * <p>
+ * This is equivalent to {@link Class#getComponentType()} but returns a
{@link ClassInfo} instead.
+ * Note that {@link #getComponentType()} also exists and returns the
base component type for multi-dimensional arrays.
+ *
+ * @return The {@link ClassInfo} representing the component type, or
<jk>null</jk> if this class does not represent an array type.
+ */
+ public ClassInfo componentType() {
+ return c == null ? null : of(c.componentType());
+ }
+
+ private <A extends Annotation> A findAnnotation(AnnotationProvider ap,
Class<A> a) {
+ if (a == null)
+ return null;
+ if (ap == null)
+ throw unsupportedOp();
+ // Inline Context.firstDeclaredAnnotation() call
+ A t = ap.getAnnotationProvider().findDeclared(a, c).map(x ->
x.inner()).filter(x -> true).findFirst().orElse(null);
+ if (nn(t))
+ return t;
+ ClassInfo sci = getSuperclass();
+ if (nn(sci)) {
+ t = sci.getAnnotation(ap, a);
+ if (nn(t))
+ return t;
+ }
+ for (var c2 : interfaces.get()) {
+ t = c2.getAnnotation(ap, a);
+ if (nn(t))
+ return t;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the first type parameter of this type if it's parameterized
(e.g., T in Optional<T>).
+ *
+ * @param parameterizedType The expected parameterized class (e.g.,
Optional.class).
+ * @return The first type parameter, or <jk>null</jk> if not
parameterized or no parameters exist.
+ */
+ private Type getFirstParameterType(Class<?> parameterizedType) {
+ if (t instanceof ParameterizedType pt) {
+ var ta = pt.getActualTypeArguments();
+ if (ta.length > 0)
+ return ta[0];
+ } else if (t instanceof Class<?> c) /* Class that extends
Optional<T> */ {
+ if (c != parameterizedType &&
parameterizedType.isAssignableFrom(c))
+ return ClassInfo.of(c).getParameterType(0,
parameterizedType);
+ }
+ return null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this type is a parameterized type of the
specified class or a subclass of it.
+ *
+ * @param c The class to check against (e.g., Optional.class,
List.class).
+ * @return <jk>true</jk> if this is Optional<T> or a subclass
like MyOptional extends Optional<String>.
+ */
+ private boolean isParameterizedTypeOf(Class<?> c) {
+ return (t instanceof ParameterizedType t2 && t2.getRawType() ==
c) || (t instanceof Class && c.isAssignableFrom((Class<?>)t));
+ }
+
+ /**
+ * Returns all annotations declared directly on this class, wrapped in
{@link AnnotationInfo} objects.
+ *
+ * <p>
+ * This includes annotations explicitly applied to the class
declaration, but excludes inherited annotations
+ * from parent classes. Each annotation is wrapped for additional
functionality such as annotation member
+ * access and metadata inspection.
+ *
+ * @return
+ * An unmodifiable list of {@link AnnotationInfo} wrappers for
annotations declared directly on this class.
+ * <br>List is empty if no annotations are declared.
+ * <br>Results are in declaration order.
+ */
+ public List<AnnotationInfo> getDeclaredAnnotationInfos() {
+ return declaredAnnotations2.get();
+ }
+
+ ConstructorInfo getConstructorInfo(Constructor<?> x) {
+ return constructorCache.get(x, () -> new ConstructorInfo(this,
x));
+ }
+
+ FieldInfo getFieldInfo(Field x) {
+ return fieldCache.get(x, () -> new FieldInfo(this, x));
+ }
+
+ MethodInfo getMethodInfo(Method x) {
+ return methodCache.get(x, () -> new MethodInfo(this, x));
+ }
+
+ private List<ClassInfo> findParents() {
+ List<ClassInfo> l = list();
+ Class<?> pc = c;
+ while (nn(pc) && pc != Object.class) {
+ l.add(of(pc));
+ pc = pc.getSuperclass();
+ }
+ return u(l);
+ }
+
+ private MethodInfo findRepeatedAnnotationMethod() {
+ return getPublicMethods().stream()
+ .filter(m -> m.hasName("value"))
+ .filter(m -> m.getReturnType().isArray())
+ .filter(m -> {
+ var rct = m.getReturnType().getComponentType();
+ if (rct.hasAnnotation(Repeatable.class)) {
+ var r =
rct.getAnnotationInfos(Repeatable.class).findFirst().map(AnnotationInfo::inner).orElse(null);
+ return r != null && r.value().equals(c);
+ }
+ return false;
+ })
+ .findFirst()
+ .orElse(null);
+ }
+
+ private int findDimensions() {
+ int d = 0;
+ Type ct = t;
+
+ // Handle GenericArrayType (e.g., List<String>[])
+ while (ct instanceof GenericArrayType gat) {
+ d++;
+ ct = gat.getGenericComponentType();
+ }
+
+ // Handle regular arrays
+ Class<?> cc = c;
+ while (nn(cc) && cc.isArray()) {
+ d++;
+ cc = cc.getComponentType();
+ }
+
+ return d;
+ }
+
+ private ClassInfo findComponentType() {
+ Type ct = t;
+ Class<?> cc = c;
+
+ // Handle GenericArrayType (e.g., List<String>[])
+ while (ct instanceof GenericArrayType gat) {
+ ct = gat.getGenericComponentType();
+ }
+
+ // Handle regular arrays
+ while (nn(cc) && cc.isArray()) {
+ cc = cc.getComponentType();
+ }
+
+ // Return the deepest component type found
+ if (ct != t) {
+ return of(ct);
+ } else if (cc != c) {
+ return of(cc);
+ } else {
+ return this;
+ }
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Annotatable interface methods
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Override /* Annotatable */
+ public AnnotatableType getAnnotatableType() {
+ return AnnotatableType.CLASS_TYPE;
+ }
+
+ @Override /* Annotatable */
+ public ClassInfo getClassInfo() {
+ return this;
+ }
+
+ @Override /* Annotatable */
+ public String getAnnotatableName() {
+ return getNameSimple();
+ }
+}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvMetaProvider.java
index b83590fa74..6b6bf2f27b 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvMetaProvider.java
@@ -25,7 +25,7 @@ import org.apache.juneau.common.reflect.*;
* <h5 class='section'>See Also:</h5><ul>
* </ul>
*/
-public interface CsvMetaProvider extends AnnotationProvider {
+public interface CsvMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
index 11994e8b80..19037772de 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
@@ -19,6 +19,7 @@ package org.apache.juneau.html;
import static org.apache.juneau.common.utils.Utils.*;
import org.apache.juneau.*;
+import org.apache.juneau.common.reflect.*;
import org.apache.juneau.cp.*;
import org.apache.juneau.html.annotation.*;
@@ -75,16 +76,16 @@ public class HtmlBeanPropertyMeta extends
ExtendedBeanPropertyMeta {
* @param bpm The metadata of the bean property of this additional
metadata.
* @param mp HTML metadata provider (for finding information about
other artifacts).
*/
- public HtmlBeanPropertyMeta(BeanPropertyMeta bpm, HtmlMetaProvider mp) {
+ public HtmlBeanPropertyMeta(BeanPropertyMeta bpm, AnnotationProvider2
annotationProvider, HtmlMetaProvider mp) {
super(bpm);
Builder b = new Builder();
if (nn(bpm.getInnerField()))
- mp.getAnnotationProvider().find(Html.class,
bpm.getInnerField()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
+ annotationProvider.find(Html.class,
bpm.getInnerField()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
if (nn(bpm.getGetter()))
- mp.getAnnotationProvider().find(Html.class,
bpm.getGetter()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
+ annotationProvider.find(Html.class,
bpm.getGetter()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
if (nn(bpm.getSetter()))
- mp.getAnnotationProvider().find(Html.class,
bpm.getSetter()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
+ annotationProvider.find(Html.class,
bpm.getSetter()).map(x -> x.inner()).filter(x -> true).forEach(x ->
b.findHtmlInfo(x));
this.format = b.format;
this.noTables = b.noTables;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
index dc653a5b56..60aefdd399 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/HtmlBasics">HTML Basics</a>
* </ul>
*/
-public interface HtmlMetaProvider extends AnnotationProvider {
+public interface HtmlMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
index c44dc378a5..f0211d943b 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
@@ -679,7 +679,7 @@ public class HtmlParser extends XmlParser implements
HtmlMetaProvider {
return HtmlBeanPropertyMeta.DEFAULT;
HtmlBeanPropertyMeta m = htmlBeanPropertyMetas.get(bpm);
if (m == null) {
- m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(),
this);
+ m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(),
this.getAnnotationProvider(), this);
htmlBeanPropertyMetas.put(bpm, m);
}
return m;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
index 28e1d205c2..a0280072db 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
@@ -1463,7 +1463,7 @@ public class HtmlSerializer extends XmlSerializer
implements HtmlMetaProvider {
return HtmlBeanPropertyMeta.DEFAULT;
HtmlBeanPropertyMeta m = htmlBeanPropertyMetas.get(bpm);
if (m == null) {
- m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(),
this);
+ m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(),
this.getAnnotationProvider(), this);
htmlBeanPropertyMetas.put(bpm, m);
}
return m;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
index d73e474b6f..a495feffd0 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/JsonBasics">JSON Basics</a>
* </ul>
*/
-public interface JsonMetaProvider extends AnnotationProvider {
+public interface JsonMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
index ff4913805a..6d7f9efcc9 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/JsonSchemaDetails">JSON-Schema
Support</a>
* </ul>
*/
-public interface JsonSchemaMetaProvider extends AnnotationProvider {
+public interface JsonSchemaMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackMetaProvider.java
index 0eadd71c46..99cb096fba 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/MessagePackBasics">MessagePack
Basics</a>
* </ul>
*/
-public interface MsgPackMetaProvider extends AnnotationProvider {
+public interface MsgPackMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiMetaProvider.java
index 002bd0e678..797d6cd4c7 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/OpenApiBasics">OpenApi Basics</a>
* </ul>
*/
-public interface OpenApiMetaProvider extends AnnotationProvider {
+public interface OpenApiMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/plaintext/PlainTextMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/plaintext/PlainTextMetaProvider.java
index 25a535511f..938c4b20e3 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/plaintext/PlainTextMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/plaintext/PlainTextMetaProvider.java
@@ -25,7 +25,7 @@ import org.apache.juneau.common.reflect.*;
* <h5 class='section'>See Also:</h5><ul>
* </ul>
*/
-public interface PlainTextMetaProvider extends AnnotationProvider {
+public interface PlainTextMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/soap/SoapXmlMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/soap/SoapXmlMetaProvider.java
index 57a280e651..5b20e8c4a2 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/soap/SoapXmlMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/soap/SoapXmlMetaProvider.java
@@ -25,7 +25,7 @@ import org.apache.juneau.common.reflect.*;
* <h5 class='section'>See Also:</h5><ul>
* </ul>
*/
-public interface SoapXmlMetaProvider extends AnnotationProvider {
+public interface SoapXmlMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonMetaProvider.java
index 3afd529c62..e0758a59de 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/UonBasics">UON Basics</a>
* </ul>
*/
-public interface UonMetaProvider extends AnnotationProvider {
+public interface UonMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
index 36d7beda85..13506821e7 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/UrlEncodingBasics">URL-Encoding
Basics</a>
* </ul>
*/
-public interface UrlEncodingMetaProvider extends AnnotationProvider {
+public interface UrlEncodingMetaProvider {
/**
* Returns the language-specific metadata on the specified bean
property.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
index 27345722a1..e06253ab9d 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
@@ -46,7 +46,7 @@ public class XmlBeanMeta extends ExtendedBeanMeta {
Class<?> c = beanMeta.getClassMeta().getInnerClass();
Value<XmlFormat> defaultFormat = Value.empty();
- mp.getAnnotationProvider().find(Xml.class, c).map(x ->
x.inner()).filter(x -> true).forEach(x -> {
+
beanMeta.getClassMeta().getBeanContext().getAnnotationProvider().find(Xml.class,
c).map(x -> x.inner()).filter(x -> true).forEach(x -> {
XmlFormat xf = x.format();
if (xf == ATTRS)
defaultFormat.set(XmlFormat.ATTR);
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
index 8d4a041714..87758337b2 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
@@ -52,7 +52,7 @@ public class XmlBeanPropertyMeta extends
ExtendedBeanPropertyMeta {
super(bpm);
this.xmlMetaProvider = mp;
- bpm.forEachAnnotation(Xml.class, x -> true, x -> findXmlInfo(x,
mp));
+ bpm.forEachAnnotation(Xml.class, x -> true, x -> findXmlInfo(x,
bpm.getClassMeta().getBeanContext().getAnnotationProvider()));
if (namespace == null)
namespace =
mp.getXmlClassMeta(bpm.getBeanMeta().getClassMeta()).getNamespace();
@@ -98,7 +98,7 @@ public class XmlBeanPropertyMeta extends
ExtendedBeanPropertyMeta {
*/
public XmlFormat getXmlFormat() { return xmlFormat; }
- private void findXmlInfo(Xml xml, AnnotationProvider mp) {
+ private void findXmlInfo(Xml xml, AnnotationProvider2 mp) {
if (xml == null)
return;
var bpm = getBeanPropertyMeta();
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
index 9e292cfd00..82361c653f 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
@@ -26,7 +26,7 @@ import org.apache.juneau.common.reflect.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/XmlBasics">XML Basics</a>
* </ul>
*/
-public interface XmlMetaProvider extends AnnotationProvider {
+public interface XmlMetaProvider {
/**
* Returns the language-specific metadata on the specified bean.