001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.reflect;
018
019import static org.apache.juneau.commons.reflect.ClassArrayFormat.*;
020import static org.apache.juneau.commons.reflect.ClassNameFormat.*;
021import static org.apache.juneau.commons.utils.AnnotationUtils.*;
022import static org.apache.juneau.commons.utils.AssertionUtils.*;
023import static org.apache.juneau.commons.utils.ClassUtils.*;
024import static org.apache.juneau.commons.utils.CollectionUtils.*;
025import static org.apache.juneau.commons.utils.PredicateUtils.*;
026import static org.apache.juneau.commons.utils.ThrowableUtils.*;
027import static org.apache.juneau.commons.utils.Utils.*;
028import static java.util.stream.Collectors.*;
029import static java.util.stream.Stream.*;
030
031import java.lang.annotation.*;
032import java.lang.reflect.*;
033import java.security.*;
034import java.util.*;
035import java.util.function.*;
036import java.util.stream.*;
037
038import org.apache.juneau.commons.collections.*;
039import org.apache.juneau.commons.lang.*;
040
041/**
042 * Lightweight utility class for introspecting information about a class.
043 *
044 * <p>
045 * Provides various convenience methods for introspecting fields/methods/annotations
046 * that aren't provided by the standard Java reflection APIs.
047 *
048 * <p>
049 * Objects are designed to be lightweight to create and threadsafe.
050 *
051 * <h5 class='figure'>Example:</h5>
052 * <p class='bjava'>
053 *    <jc>// Wrap our class inside a ClassInfo.</jc>
054 *    ClassInfo <jv>classInfo</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
055 *
056 *    <jc>// Get all methods in parent-to-child order, sorted alphabetically per class.</jc>
057 *    <jk>for</jk> (MethodInfo <jv>methodInfo</jv> : <jv>classInfo</jv>.getAllMethods()) {
058 *       <jc>// Do something with it.</jc>
059 *    }
060 *
061 *    <jc>// Get all class-level annotations in parent-to-child order.</jc>
062 *    <jk>for</jk> (MyAnnotation <jv>annotation</jv> : <jv>classInfo</jv>.getAnnotations(MyAnnotation.<jk>class</jk>)) {
063 *       <jc>// Do something with it.</jc>
064 *    }
065 * </p>
066 *
067 */
068@SuppressWarnings({ "unchecked", "rawtypes" })
069public class ClassInfo extends ElementInfo implements Annotatable, Type, Comparable<ClassInfo> {
070
071   private static final Cache<Class,ClassInfoTyped> CACHE = Cache.of(Class.class, ClassInfoTyped.class).build();
072
073   /** Reusable ClassInfo for Object class. */
074   public static final ClassInfo OBJECT = ClassInfo.of(Object.class);
075
076   private static final Map<Class<?>,Class<?>> pmap1 = new HashMap<>(), pmap2 = new HashMap<>();
077
078   static {
079      pmap1.put(boolean.class, Boolean.class);
080      pmap1.put(byte.class, Byte.class);
081      pmap1.put(short.class, Short.class);
082      pmap1.put(char.class, Character.class);
083      pmap1.put(int.class, Integer.class);
084      pmap1.put(long.class, Long.class);
085      pmap1.put(float.class, Float.class);
086      pmap1.put(double.class, Double.class);
087      pmap2.put(Boolean.class, boolean.class);
088      pmap2.put(Byte.class, byte.class);
089      pmap2.put(Short.class, short.class);
090      pmap2.put(Character.class, char.class);
091      pmap2.put(Integer.class, int.class);
092      pmap2.put(Long.class, long.class);
093      pmap2.put(Float.class, float.class);
094      pmap2.put(Double.class, double.class);
095   }
096
097   // @formatter:off
098   private static final Map<Class,Object> primitiveDefaultMap =
099      mapb(Class.class,Object.class)
100         .unmodifiable()
101         .add(Boolean.TYPE, false)
102         .add(Character.TYPE, (char)0)
103         .add(Short.TYPE, (short)0)
104         .add(Integer.TYPE, 0)
105         .add(Long.TYPE, 0L)
106         .add(Float.TYPE, 0f)
107         .add(Double.TYPE, 0d)
108         .add(Byte.TYPE, (byte)0)
109         .add(Boolean.class, false)
110         .add(Character.class, (char)0)
111         .add(Short.class, (short)0)
112         .add(Integer.class, 0)
113         .add(Long.class, 0L)
114         .add(Float.class, 0f)
115         .add(Double.class, 0d)
116         .add(Byte.class, (byte)0)
117         .build();
118   // @formatter:on
119
120   /**
121    * Returns a class info wrapper around the specified class type.
122    *
123    * @param inner The class type.
124    * @param innerType The generic type (if parameterized type).
125    * @return The constructed class info.
126    */
127   public static ClassInfo of(Class<?> inner, Type innerType) {
128      if (inner == innerType)
129         return of(inner);
130      if (inner != null)
131         return new ClassInfoTyped<>(inner, innerType);
132      return new ClassInfo(null, innerType);
133   }
134
135   /**
136    * Returns a class info wrapper around the specified class type.
137    *
138    * @param <T> The class type.
139    * @param inner The class type.
140    * @return The constructed class info.
141    */
142   public static <T> ClassInfoTyped<T> of(Class<T> inner) {
143      return CACHE.get(inner, () -> new ClassInfoTyped<>(inner));
144   }
145
146   /**
147    * Same as using the constructor, but operates on an object instance.
148    *
149    * @param object The class instance.
150    * @return The constructed class info.
151    */
152   public static ClassInfo of(Object object) {
153      return of(object instanceof Class object2 ? (Class<?>)object2 : object.getClass());
154   }
155
156   /**
157    * Returns a class info wrapper around the specified class type.
158    *
159    * @param innerType The class type.
160    * @return The constructed class info.
161    */
162   public static ClassInfo of(Type innerType) {
163      if (innerType instanceof Class<?> c)
164         return of(c);
165      return new ClassInfo(toClass(innerType), innerType);
166   }
167
168   /**
169    * Same as {@link #of(Object)} but attempts to deproxify the object if it's wrapped in a CGLIB proxy.
170    *
171    * @param object The class instance.
172    * @return The constructed class info.
173    */
174   public static ClassInfo ofProxy(Object object) {
175      var inner = getProxyFor(object);
176      return inner == null ? ClassInfo.of(object) : ClassInfo.of(inner);
177   }
178
179   private final Type innerType;  // The underlying Type object (may be Class, ParameterizedType, GenericArrayType, etc.).
180   private final Class<?> inner;  // The underlying Class object (null for non-class types like TypeVariable).
181   private final boolean isParameterizedType;  // True if this represents a ParameterizedType (e.g., List<String>).
182
183   private final Supplier<Integer> dimensions;  // Number of array dimensions (0 if not an array).
184   private final Supplier<ClassInfo> componentType;  // Base component type for arrays (e.g., String for String[][]), also handles GenericArrayType.  Cached and never null.
185   private final Supplier<PackageInfo> packageInfo;  // The package this class belongs to (null for primitive types and arrays).
186   private final Supplier<List<ClassInfo>> parents;  // All superclasses of this class in child-to-parent order, starting with this class.
187   private final Supplier<List<AnnotationInfo>> declaredAnnotations;  // All annotations declared directly on this class, wrapped in AnnotationInfo.
188   private final Supplier<String> fullName;  // Fully qualified class name with generics (e.g., "java.util.List<java.lang.String>").
189   private final Supplier<String> shortName;  // Simple class name with generics (e.g., "List<String>").
190   private final Supplier<String> readableName;  // Human-readable class name without generics (e.g., "List").
191   private final Supplier<List<ClassInfo>> declaredInterfaces;  // All interfaces declared directly by this class.
192   private final Supplier<List<ClassInfo>> interfaces;  // All interfaces implemented by this class and its parents, in child-to-parent order.
193   private final Supplier<List<ClassInfo>> allParents;  // All parent classes and interfaces, classes first, then in child-to-parent order.
194   private final Supplier<List<ClassInfo>> parentsAndInterfaces;  // All parent classes and interfaces with proper traversal of interface hierarchy to avoid duplicates.
195   private final Supplier<List<AnnotationInfo<? extends Annotation>>> annotationInfos;  // All annotations on this class and parent classes/interfaces in child-to-parent order.
196   private final Supplier<List<RecordComponent>> recordComponents;  // All record components if this is a record class (Java 14+).
197   private final Supplier<List<Type>> genericInterfaces;  // All generic interface types (e.g., List<String> implements Comparable<List<String>>).
198   private final Supplier<List<TypeVariable<?>>> typeParameters;  // All type parameters declared on this class (e.g., <T, U> in class Foo<T, U>).
199   private final Supplier<List<AnnotatedType>> annotatedInterfaces;  // All annotated interface types with their annotations.
200   private final Supplier<List<Object>> signers;  // All signers of this class (for signed JARs).
201   private final Supplier<List<MethodInfo>> publicMethods;  // All public methods on this class and inherited, excluding Object methods.
202   private final Supplier<List<MethodInfo>> declaredMethods;  // All methods declared directly on this class (public, protected, package, private).
203   private final Supplier<List<MethodInfo>> allMethods;  // All methods from this class and all parents, in child-to-parent order.
204   private final Supplier<List<MethodInfo>> allMethodsTopDown;  // All methods from this class and all parents, in parent-to-child order.
205   private final Supplier<List<FieldInfo>> publicFields;  // All public fields from this class and parents, deduplicated by name (child wins).
206   private final Supplier<List<FieldInfo>> declaredFields;  // All fields declared directly on this class (public, protected, package, private).
207   private final Supplier<List<FieldInfo>> allFields;  // All fields from this class and all parents, in parent-to-child order.
208   private final Supplier<List<ConstructorInfo>> publicConstructors;  // All public constructors declared on this class.
209   private final Supplier<List<ConstructorInfo>> declaredConstructors;  // All constructors declared on this class (public, protected, package, private).
210   private final Supplier<MethodInfo> repeatedAnnotationMethod;  // The repeated annotation method (value()) if this class is a @Repeatable container.
211   private final Cache<Method,MethodInfo> methodCache;  // Cache of wrapped Method objects.
212   private final Cache<Field,FieldInfo> fieldCache;  // Cache of wrapped Field objects.
213   private final Cache<Constructor,ConstructorInfo> constructorCache;  // Cache of wrapped Constructor objects.
214
215   /**
216    * Constructor.
217    *
218    * @param inner The class type.
219    * @param innerType The generic type (if parameterized type).
220    */
221   protected ClassInfo(Class<?> inner, Type innerType) {
222      // @formatter:off
223      super(inner == null ? 0 : inner.getModifiers());
224      assertArg(inner != null || innerType != null, "At least one of inner or innerType must be specified.");
225      this.innerType = innerType;
226      this.inner = inner;
227      this.isParameterizedType = innerType == null ? false : (innerType instanceof ParameterizedType);
228      this.dimensions = mem(this::findDimensions);
229      this.componentType = mem(this::findComponentType);
230      this.packageInfo = mem(() -> opt(inner).map(x -> x.getPackage()).filter(p -> p != null).map(PackageInfo::of).orElse(null));  // PackageInfo may be null for primitive types and arrays.
231      this.parents = mem(this::findParents);
232      this.declaredAnnotations = mem(() -> (List)opt(inner).map(x -> u(l(x.getDeclaredAnnotations()))).orElse(liste()).stream().flatMap(a -> streamRepeated(a)).map(a -> ai(this, a)).toList());
233      this.fullName = mem(() -> getNameFormatted(FULL, true, '$', BRACKETS));
234      this.shortName = mem(() -> getNameFormatted(SHORT, true, '$', BRACKETS));
235      this.readableName = mem(() -> getNameFormatted(SIMPLE, false, '$', WORD));
236      this.declaredInterfaces = mem(() -> opt(inner).map(x -> stream(x.getGenericInterfaces()).map(ClassInfo::of).map(ClassInfo.class::cast).toList()).orElse(liste()));
237      this.interfaces = mem(() -> getParents().stream().flatMap(x -> x.getDeclaredInterfaces().stream()).flatMap(ci2 -> concat(Stream.of(ci2), ci2.getInterfaces().stream())).distinct().toList());
238      this.allParents = mem(() -> concat(getParents().stream(), getInterfaces().stream()).toList());
239      this.parentsAndInterfaces = mem(this::findParentsAndInterfaces);
240      this.annotationInfos = mem(this::findAnnotations);
241      this.recordComponents = mem(() -> opt(inner).filter(Class::isRecord).map(x -> u(l(x.getRecordComponents()))).orElse(liste()));
242      this.genericInterfaces = mem(() -> opt(inner).map(x -> u(l(x.getGenericInterfaces()))).orElse(liste()));
243      this.typeParameters = mem(() -> opt(inner).map(x -> u(l((TypeVariable<?>[])x.getTypeParameters()))).orElse(liste()));
244      this.annotatedInterfaces = mem(() -> opt(inner).map(x -> u(l(x.getAnnotatedInterfaces()))).orElse(liste()));
245      this.signers = mem(() -> opt(inner).map(Class::getSigners).map(x -> u(l(x))).orElse(liste()));
246      this.publicMethods = mem(() -> opt(inner).map(x -> stream(x.getMethods()).filter(m -> neq(m.getDeclaringClass(), Object.class)).map(this::getMethod).sorted().toList()).orElse(liste()));
247      this.declaredMethods = mem(() -> opt(inner).map(x -> stream(x.getDeclaredMethods()).filter(m -> neq("$jacocoInit", m.getName())).map(this::getMethod).sorted().toList()).orElse(liste()));
248      this.allMethods = mem(() -> allParents.get().stream().flatMap(c2 -> c2.getDeclaredMethods().stream()).toList());
249      this.allMethodsTopDown = mem(() -> rstream(getAllParents()).flatMap(c2 -> c2.getDeclaredMethods().stream()).toList());
250      this.publicFields = mem(() -> parents.get().stream().flatMap(c2 -> c2.getDeclaredFields().stream()).filter(f -> f.isPublic() && neq("$jacocoData", f.getName())).collect(toMap(FieldInfo::getName, x -> x, (a, b) -> a, LinkedHashMap::new)).values().stream().sorted().collect(toList()));
251      this.declaredFields = mem(() -> opt(inner).map(x -> stream(x.getDeclaredFields()).filter(f -> neq("$jacocoData", f.getName())).map(this::getField).sorted().toList()).orElse(liste()));
252      this.allFields = mem(() -> rstream(allParents.get()).flatMap(c2 -> c2.getDeclaredFields().stream()).toList());
253      this.publicConstructors = mem(() -> opt(inner).map(x -> stream(x.getConstructors()).map(this::getConstructor).sorted().toList()).orElse(liste()));
254      this.declaredConstructors = mem(() -> opt(inner).map(x -> stream(x.getDeclaredConstructors()).map(this::getConstructor).sorted().toList()).orElse(liste()));
255      this.repeatedAnnotationMethod = mem(this::findRepeatedAnnotationMethod);
256      this.methodCache = Cache.of(Method.class, MethodInfo.class).build();
257      this.fieldCache = Cache.of(Field.class, FieldInfo.class).build();
258      this.constructorCache = Cache.of(Constructor.class, ConstructorInfo.class).build();
259      // @formatter:on
260   }
261
262   /**
263    * Appends a formatted class name to a StringBuilder with configurable options.
264    *
265    * <p>
266    * This is the core implementation method used by all other name formatting methods.
267    * Using this method directly avoids String allocations when building complex strings.
268    * The method is recursive to handle nested generic type parameters.
269    *
270    * <h5 class='section'>Example:</h5>
271    * <p class='bjava'>
272    *    StringBuilder sb = <jk>new</jk> StringBuilder();
273    *    sb.append(<js>"Class: "</js>);
274    *    ClassInfo.<jsm>of</jsm>(HashMap.<jk>class</jk>).appendFormattedName(sb, ClassNameFormat.<jsf>FULL</jsf>, <jk>true</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>);
275    *    <jc>// sb now contains: "Class: java.util.HashMap"</jc>
276    * </p>
277    *
278    * @param sb
279    *    The StringBuilder to append to.
280    * @param nameFormat
281    *    Controls which parts of the class name to include (package, outer classes).
282    * @param includeTypeParams
283    *    If <jk>true</jk>, include generic type parameters recursively.
284    * @param separator
285    *    Character to use between outer and inner class names.
286    *    <br>Ignored when <c>nameFormat</c> is {@link ClassNameFormat#SIMPLE}.
287    * @param arrayFormat
288    *    How to format array dimensions.
289    * @return
290    *    The same StringBuilder for method chaining.
291    */
292   @SuppressWarnings("null")
293   public StringBuilder appendNameFormatted(StringBuilder sb, ClassNameFormat nameFormat, boolean includeTypeParams, char separator, ClassArrayFormat arrayFormat) {
294      var dim = getDimensions();
295
296      // Handle arrays - format component type recursively, then add array notation
297      if (dim > 0) {
298         var componentType = getComponentType();
299         componentType.appendNameFormatted(sb, nameFormat, includeTypeParams, separator, arrayFormat);
300
301         if (arrayFormat == ClassArrayFormat.WORD) {
302            for (var i = 0; i < dim; i++)
303               sb.append("Array");
304         } else if (arrayFormat == ClassArrayFormat.BRACKETS) {
305            for (var i = 0; i < dim; i++)
306               sb.append("[]");
307         }
308         // JVM format is already in getName() - would need special handling
309
310         return sb;
311      }
312
313      // Get the raw class - for ParameterizedType, extract the raw type
314      var ct = inner;
315      if (ct == null && isParameterizedType) {
316         var pt = (ParameterizedType)innerType;
317         ct = (Class<?>)pt.getRawType();
318      }
319
320      // Append base class name based on format
321      switch (nameFormat) {
322         case FULL:
323            // Full package name + outer classes
324            if (nn(ct)) {
325               sb.append(ct.getName());
326               // Apply separator if not '$'
327               if (separator != '$' && sb.indexOf("$") != -1) {
328                  for (var i = 0; i < sb.length(); i++) {
329                     if (sb.charAt(i) == '$')
330                        sb.setCharAt(i, separator);
331                  }
332               }
333            } else {
334               sb.append(innerType.getTypeName());
335            }
336            break;
337
338         case SHORT:
339            // Outer classes but no package
340            if (nn(ct)) {
341               if (ct.isLocalClass()) {
342                  // Local class: include enclosing class simple name
343                  sb.append(of(ct.getEnclosingClass()).getNameSimple()).append(separator).append(ct.getSimpleName());
344               } else if (ct.isMemberClass()) {
345                  // Member class: include declaring class simple name
346                  sb.append(of(ct.getDeclaringClass()).getNameSimple()).append(separator).append(ct.getSimpleName());
347               } else {
348                  // Regular class: just simple name
349                  sb.append(ct.getSimpleName());
350               }
351            } else {
352               sb.append(innerType.getTypeName());
353            }
354            break;
355
356         default /* SIMPLE */:
357            // Simple name only - no package, no outer classes
358            if (nn(ct)) {
359               sb.append(ct.getSimpleName());
360            } else {
361               sb.append(innerType.getTypeName());
362            }
363            break;
364      }
365
366      // Append type parameters if requested
367      if (includeTypeParams && isParameterizedType) {
368         var pt = (ParameterizedType)innerType;
369         sb.append('<');
370         var first = true;
371         for (var t2 : pt.getActualTypeArguments()) {
372            if (! first)
373               sb.append(',');
374            first = false;
375            of(t2).appendNameFormatted(sb, nameFormat, includeTypeParams, separator, arrayFormat);
376         }
377         sb.append('>');
378      }
379
380      return sb;
381   }
382
383   /**
384    * Returns a {@link ClassInfo} for an array type whose component type is this class.
385    *
386    * @return A {@link ClassInfo} representing an array type whose component type is this class.
387    */
388   public ClassInfo arrayType() {
389      if (inner == null)
390         return null;
391      return of(inner.arrayType());
392   }
393
394   /**
395    * Casts this {@link ClassInfo} object to represent a subclass of the class represented by the specified class object.
396    *
397    * @param <U> The type to cast to.
398    * @param clazz The class of the type to cast to.
399    * @return This {@link ClassInfo} object, cast to represent a subclass of the specified class object.
400    * @throws ClassCastException If this class is not assignable to the specified class.
401    */
402   public <U> ClassInfo asSubclass(Class<U> clazz) {
403      if (inner == null)
404         return null;
405      inner.asSubclass(clazz);  // Throws ClassCastException if not assignable
406      return this;
407   }
408
409   /**
410    * Returns <jk>true</jk> if this type can be used as a parameter for the specified object.
411    *
412    * <p>
413    * For null values, returns <jk>true</jk> unless this type is a primitive
414    * (since primitives cannot accept null values in Java).
415    *
416    * <h5 class='section'>Examples:</h5>
417    * <ul class='spaced-list'>
418    *    <li><c>ClassInfo.of(String.class).canAcceptArg("foo")</c> - returns <jk>true</jk>
419    *    <li><c>ClassInfo.of(String.class).canAcceptArg(null)</c> - returns <jk>true</jk>
420    *    <li><c>ClassInfo.of(int.class).canAcceptArg(5)</c> - returns <jk>true</jk>
421    *    <li><c>ClassInfo.of(int.class).canAcceptArg(null)</c> - returns <jk>false</jk> (primitives can't be null)
422    *    <li><c>ClassInfo.of(Integer.class).canAcceptArg(null)</c> - returns <jk>true</jk>
423    * </ul>
424    *
425    * @param child The argument to check.
426    * @return <jk>true</jk> if this type can be used as a parameter for the specified object.
427    */
428   public boolean canAcceptArg(Object child) {
429      if (inner == null)
430         return false;
431      if (child == null)
432         return ! isPrimitive();  // Primitives can't accept null, all other types can
433      if (inner.isInstance(child))
434         return true;
435      if (this.isPrimitive())
436         return this.getWrapperIfPrimitive().isParentOf(of(child).getWrapperIfPrimitive());
437      return false;
438   }
439
440   /**
441    * Casts an object to the class represented by this {@link ClassInfo} object.
442    *
443    * @param <T> The type to cast to.
444    * @param obj The object to be cast.
445    * @return The object after casting, or <jk>null</jk> if obj is <jk>null</jk>.
446    * @throws ClassCastException If the object is not <jk>null</jk> and is not assignable to this class.
447    */
448   public <T> T cast(Object obj) {
449      return inner == null ? null : (T)inner.cast(obj);
450   }
451
452   /**
453    * Finds the annotation of the specified type defined on this class or parent class/interface.
454    *
455    * <p>
456    * If the annotation cannot be found on the immediate class, searches methods with the same signature on the parent classes or interfaces. <br>
457    * The search is performed in child-to-parent order.
458    *
459    * @param <A> The annotation type to look for.
460    * @param annotationProvider The annotation provider.
461    * @param type The annotation to look for.
462    * @return The annotation if found, or <jk>null</jk> if not.
463    */
464
465   /**
466    * Returns the component type of this class if it is an array type.
467    *
468    * <p>
469    * This is equivalent to {@link Class#getComponentType()} but returns a {@link ClassInfo} instead.
470    * Note that {@link #getComponentType()} also exists and returns the base component type for multi-dimensional arrays.
471    *
472    * @return The {@link ClassInfo} representing the component type, or <jk>null</jk> if this class does not represent an array type.
473    */
474   public ClassInfo componentType() {
475      if (inner == null)
476         return null;
477      var ct = inner.componentType();
478      return ct == null ? null : of(ct);
479   }
480
481   /**
482    * Returns the descriptor string of this class.
483    *
484    * <p>
485    * The descriptor is a string representing the type of the class, as specified in JVMS 4.3.2.
486    * For example, the descriptor for <c>String</c> is <js>"Ljava/lang/String;"</js>.
487    *
488    * @return The descriptor string of this class.
489    */
490   public String descriptorString() {
491      return inner == null ? null : inner.descriptorString();
492   }
493
494   @Override
495   public boolean equals(Object o) {
496      return (o instanceof ClassInfo o2) && eq(this, o2, (x, y) -> eq(x.innerType, y.innerType));
497   }
498
499   @Override
500   public int compareTo(ClassInfo o) {
501      if (o == null)
502         return 1;
503      return cmp(getName(), o.getName());
504   }
505
506   /**
507    * Returns all fields on this class and all parent classes.
508    *
509    * <p>
510    *    Results are ordered parent-to-child, and then alphabetical per class.
511    *
512    * @return
513    *    All declared fields on this class.
514    *    <br>List is unmodifiable.
515    */
516   public List<FieldInfo> getAllFields() { return allFields.get(); }
517
518   /**
519    * Returns all declared methods on this class and all parent classes in <b>child-to-parent order</b>.
520    *
521    * <p>
522    * This method returns methods of <b>all visibility levels</b> (public, protected, package-private, and private).
523    *
524    * <p>
525    * Methods are returned in <b>child-to-parent order</b> - methods from the current class appear first,
526    * followed by methods from the immediate parent, then grandparent, etc. Within each class, methods
527    * are sorted alphabetically.
528    *
529    * <h5 class='section'>Example:</h5>
530    * <p class='bjava'>
531    *    <jc>// Given class hierarchy:</jc>
532    *    <jk>class</jk> Parent {
533    *       <jk>void</jk> method1() {}
534    *       <jk>void</jk> method2() {}
535    *    }
536    *    <jk>class</jk> Child <jk>extends</jk> Parent {
537    *       <jk>void</jk> method3() {}
538    *       <jk>void</jk> method4() {}
539    *    }
540    *
541    *    <jc>// getAllMethods() returns in child-to-parent order:</jc>
542    *    ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(Child.<jk>class</jk>);
543    *    List&lt;MethodInfo&gt; <jv>methods</jv> = <jv>ci</jv>.getAllMethods();
544    *    <jc>// Returns: [method3, method4, method1, method2]</jc>
545    *    <jc>//          ^Child methods^   ^Parent methods^</jc>
546    * </p>
547    *
548    * <h5 class='section'>Comparison with Similar Methods:</h5>
549    * <ul class='spaced-list'>
550    *    <li>{@link #getDeclaredMethods()} - Returns methods declared on this class only (all visibility levels)
551    *    <li>{@link #getAllMethods()} - Returns all methods in <b>child-to-parent order</b> ← This method
552    *    <li>{@link #getAllMethodsTopDown()} - Returns all methods in <b>parent-to-child order</b>
553    *    <li>{@link #getPublicMethods()} - Returns public methods only on this class and parents
554    * </ul>
555    *
556    * <h5 class='section'>Notes:</h5>
557    * <ul class='spaced-list'>
558    *    <li>Unlike Java's {@link Class#getMethods()}, this returns methods of all visibility levels, not just public ones.
559    *    <li>Methods from {@link Object} class are excluded from the results.
560    *    <li>Use {@link #getAllMethodsTopDown()} if you need parent methods to appear before child methods.
561    * </ul>
562    *
563    * @return
564    *    All declared methods on this class and all parent classes (all visibility levels).
565    *    <br>Results are ordered child-to-parent, and then alphabetically per class.
566    *    <br>List is unmodifiable.
567    */
568   public List<MethodInfo> getAllMethods() { return allMethods.get(); }
569
570   /**
571    * Returns all declared methods on this class and all parent classes in <b>parent-to-child order</b>.
572    *
573    * <p>
574    * This method returns methods of <b>all visibility levels</b> (public, protected, package-private, and private).
575    *
576    * <p>
577    * Methods are returned in <b>parent-to-child order</b> - methods from the most distant ancestor appear first,
578    * followed by methods from each subsequent child class, ending with methods from the current class. Within
579    * each class, methods are sorted alphabetically.
580    *
581    * <p>
582    * This ordering is useful for initialization hooks and lifecycle methods where parent methods should execute
583    * before child methods (e.g., <c>@RestInit</c>, <c>@PostConstruct</c>, etc.).
584    *
585    * <h5 class='section'>Example:</h5>
586    * <p class='bjava'>
587    *    <jc>// Given class hierarchy:</jc>
588    *    <jk>class</jk> Parent {
589    *       <jk>void</jk> method1() {}
590    *       <jk>void</jk> method2() {}
591    *    }
592    *    <jk>class</jk> Child <jk>extends</jk> Parent {
593    *       <jk>void</jk> method3() {}
594    *       <jk>void</jk> method4() {}
595    *    }
596    *
597    *    <jc>// getAllMethodsParentFirst() returns in parent-to-child order:</jc>
598    *    ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(Child.<jk>class</jk>);
599    *    List&lt;MethodInfo&gt; <jv>methods</jv> = <jv>ci</jv>.getAllMethodsParentFirst();
600    *    <jc>// Returns: [method1, method2, method3, method4]</jc>
601    *    <jc>//          ^Parent methods^  ^Child methods^</jc>
602    * </p>
603    *
604    * <h5 class='section'>Comparison with Similar Methods:</h5>
605    * <ul class='spaced-list'>
606    *    <li>{@link #getDeclaredMethods()} - Returns methods declared on this class only (all visibility levels)
607    *    <li>{@link #getAllMethods()} - Returns all methods in <b>child-to-parent order</b>
608    *    <li>{@link #getAllMethodsTopDown()} - Returns all methods in <b>parent-to-child order</b> ← This method
609    *    <li>{@link #getPublicMethods()} - Returns public methods only on this class and parents
610    * </ul>
611    *
612    * <h5 class='section'>Notes:</h5>
613    * <ul class='spaced-list'>
614    *    <li>Methods from {@link Object} class are excluded from the results.
615    *    <li>Use {@link #getAllMethods()} if you need child methods to appear before parent methods.
616    * </ul>
617    *
618    * @return
619    *    All declared methods on this class and all parent classes (all visibility levels).
620    *    <br>Results are ordered parent-to-child, and then alphabetically per class.
621    *    <br>List is unmodifiable.
622    */
623   public List<MethodInfo> getAllMethodsTopDown() { return allMethodsTopDown.get(); }
624
625   /**
626    * Returns a list including this class and all parent classes and interfaces.
627    *
628    * <p>
629    * Results are classes-before-interfaces, then child-to-parent order.
630    *
631    * @return An unmodifiable list including this class and all parent classes.
632    *    <br>Results are ordered child-to-parent order with classes listed before interfaces.
633    */
634   public List<ClassInfo> getAllParents() { return allParents.get(); }
635
636   @Override /* Annotatable */
637   public AnnotatableType getAnnotatableType() { return AnnotatableType.CLASS_TYPE; }
638
639   /**
640    * Returns a list of {@link AnnotatedType} objects that represent the annotated interfaces
641    * implemented by this class.
642    *
643    * <p>
644    * Returns a cached, unmodifiable list.
645    * If this class represents a class or interface whose superinterfaces are annotated,
646    * the returned objects reflect the annotations used in the source code to declare the superinterfaces.
647    *
648    * @return
649    *    An unmodifiable list of {@link AnnotatedType} objects representing the annotated superinterfaces.
650    *    <br>Returns an empty list if this class implements no interfaces.
651    */
652   public List<AnnotatedType> getAnnotatedInterfaces() { return annotatedInterfaces.get(); }
653
654   /**
655    * Returns an {@link AnnotatedType} object that represents the annotated superclass of this class.
656    *
657    * <p>
658    * If this class represents a class type whose superclass is annotated, the returned object reflects
659    * the annotations used in the source code to declare the superclass.
660    *
661    * @return
662    *    An {@link AnnotatedType} object representing the annotated superclass,
663    *    or <jk>null</jk> if this class represents {@link Object}, an interface, a primitive type, or void.
664    */
665   public AnnotatedType getAnnotatedSuperclass() { return inner == null ? null : inner.getAnnotatedSuperclass(); }
666
667   /**
668    * Returns all annotations on this class and parent classes/interfaces in child-to-parent order.
669    *
670    * <p>
671    * This returns all declared annotations from:
672    * <ol>
673    *    <li>This class
674    *    <li>Parent classes in child-to-parent order
675    *    <li>For each class, interfaces declared on that class and their parent interfaces
676    *    <li>The package of this class
677    * </ol>
678    *
679    * <p>
680    * This does NOT include runtime annotations. For runtime annotation support, use
681    * {@link org.apache.juneau.commons.reflect.AnnotationProvider}.
682    *
683    * @return An unmodifiable list of all annotation infos.
684    */
685   public List<AnnotationInfo<? extends Annotation>> getAnnotations() { return annotationInfos.get(); }
686
687   /**
688    * Returns all annotations of the specified type on this class and parent classes/interfaces in child-to-parent order.
689    *
690    * <p>
691    * This returns all declared annotations from:
692    * <ol>
693    *    <li>This class
694    *    <li>Parent classes in child-to-parent order
695    *    <li>For each class, interfaces declared on that class and their parent interfaces
696    *    <li>The package of this class
697    * </ol>
698    *
699    * <p>
700    * This does NOT include runtime annotations. For runtime annotation support, use
701    * {@link org.apache.juneau.commons.reflect.AnnotationProvider}.
702    *
703    * @param <A> The annotation type.
704    * @param type The annotation type to filter by.
705    * @return A stream of annotation infos of the specified type.
706    */
707   public <A extends Annotation> Stream<AnnotationInfo<A>> getAnnotations(Class<A> type) {
708      assertArgNotNull("type", type);
709      return getAnnotations().stream().filter(a -> a.isType(type)).map(a -> (AnnotationInfo<A>)a);
710   }
711
712   /**
713    * Returns the {@link ClassLoader} for this class.
714    *
715    * <p>
716    * If this class represents a primitive type or void, <jk>null</jk> is returned.
717    *
718    * @return The class loader for this class, or <jk>null</jk> if it doesn't have one.
719    */
720   public ClassLoader getClassLoader() { return inner == null ? null : inner.getClassLoader(); }
721
722   /**
723    * Returns the base component type of this class.
724    *
725    * <p>
726    * For array types (e.g., <c>String[][]</c>), returns the deepest component type (e.g., <c>String</c>).
727    * <br>For non-array types, returns this class itself.
728    *
729    * <p>
730    * <b>Note:</b> Unlike {@link Class#getComponentType()}, this method also handles generic array types (e.g., <c>List&lt;String&gt;[]</c>)
731    * and returns the full parameterized type information (e.g., <c>List&lt;String&gt;</c>).
732    * Additionally, this method never returns <jk>null</jk> - non-array types return <jk>this</jk> instead.
733    *
734    * @return The base component type of an array, or this class if not an array.
735    */
736   public ClassInfo getComponentType() { return componentType.get(); }
737
738   /**
739    * Returns all annotations declared directly on this class, wrapped in {@link AnnotationInfo} objects.
740    *
741    * <p>
742    * This includes annotations explicitly applied to the class declaration, but excludes inherited annotations
743    * from parent classes. Each annotation is wrapped for additional functionality such as annotation member
744    * access and metadata inspection.
745    *
746    * <p>
747    * <b>Note on Repeatable Annotations:</b>
748    * Repeatable annotations (those marked with {@link java.lang.annotation.Repeatable @Repeatable}) are automatically
749    * expanded into their individual annotation instances. For example, if a class has multiple {@code @Bean} annotations,
750    * this method returns each {@code @Bean} annotation separately, rather than the container annotation.
751    *
752    * @return
753    *    An unmodifiable list of {@link AnnotationInfo} wrappers for annotations declared directly on this class.
754    *    <br>List is empty if no annotations are declared.
755    *    <br>Results are in declaration order.
756    *    <br>Repeatable annotations are expanded into individual instances.
757    */
758   public List<AnnotationInfo> getDeclaredAnnotations() { return declaredAnnotations.get(); }
759
760   /**
761    * Returns the first matching declared constructor on this class.
762    *
763    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
764    * @return The declared constructor that matches the specified predicate.
765    */
766   public Optional<ConstructorInfo> getDeclaredConstructor(Predicate<ConstructorInfo> filter) {
767      return declaredConstructors.get().stream().filter(x -> test(filter, x)).findFirst();
768   }
769
770   /**
771    * Returns all the constructors defined on this class.
772    *
773    * @return
774    *    All constructors defined on this class.
775    *    <br>List is unmodifiable.
776    */
777   public List<ConstructorInfo> getDeclaredConstructors() { return declaredConstructors.get(); }
778
779   /**
780    * Returns the first matching declared field on this class.
781    *
782    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
783    * @return The declared field, or <jk>null</jk> if not found.
784    */
785   public Optional<FieldInfo> getDeclaredField(Predicate<FieldInfo> filter) {
786      return declaredFields.get().stream().filter(x -> test(filter, x)).findFirst();
787   }
788
789   /**
790    * Returns all declared fields on this class.
791    *
792    * @return
793    *    All declared fields on this class.
794    *    <br>Results are in alphabetical order.
795    *    <br>List is unmodifiable.
796    */
797   public List<FieldInfo> getDeclaredFields() { return declaredFields.get(); }
798
799   /**
800    * Returns a list of interfaces declared on this class.
801    *
802    * <p>
803    * Does not include interfaces declared on parent classes.
804    *
805    * <p>
806    * Results are in the same order as Class.getInterfaces().
807    *
808    * @return
809    *    An unmodifiable list of interfaces declared on this class.
810    *    <br>Results are in the same order as {@link Class#getInterfaces()}.
811    */
812   public List<ClassInfo> getDeclaredInterfaces() { return declaredInterfaces.get(); }
813
814   /**
815    * Returns all classes and interfaces declared as members of this class.
816    *
817    * <p>
818    * This includes public, protected, default (package) access, and private classes and interfaces
819    * declared by the class, but excludes inherited classes and interfaces.
820    *
821    * @return
822    *    An unmodifiable list of all classes and interfaces declared as members of this class.
823    *    <br>Returns an empty list if this class declares no classes or interfaces as members.
824    */
825   public List<ClassInfo> getDeclaredMemberClasses() {
826      if (inner == null)
827         return u(l());
828      Class<?>[] classes = inner.getDeclaredClasses();
829      List<ClassInfo> l = listOfSize(classes.length);
830      for (Class<?> cc : classes)
831         l.add(of(cc));
832      return u(l);
833   }
834
835   /**
836    * Returns the first matching declared method on this class.
837    *
838    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
839    * @return The first matching method, or <jk>null</jk> if no methods matched.
840    */
841   public Optional<MethodInfo> getDeclaredMethod(Predicate<MethodInfo> filter) {
842      return declaredMethods.get().stream().filter(x -> test(filter, x)).findFirst();
843   }
844
845   /**
846    * Returns all methods declared on this class.
847    *
848    * <p>
849    * This method returns methods of <b>all visibility levels</b> (public, protected, package-private, and private)
850    * declared directly on this class only (does not include inherited methods).
851    *
852    * <h5 class='section'>Comparison with Similar Methods:</h5>
853    * <ul class='spaced-list'>
854    *    <li>{@link #getDeclaredMethods()} - Returns all declared methods on this class only (all visibility levels) ← This method
855    *    <li>{@link #getAllMethods()} - Returns all declared methods on this class and parents (all visibility levels)
856    *    <li>{@link #getPublicMethods()} - Returns public methods only on this class and parents
857    * </ul>
858    *
859    * <h5 class='section'>Notes:</h5>
860    * <ul class='spaced-list'>
861    *    <li>Unlike Java's {@link Class#getDeclaredMethods()}, results are filtered to exclude synthetic methods like <c>$jacocoInit</c>.
862    * </ul>
863    *
864    * @return
865    *    All methods declared on this class (all visibility levels).
866    *    <br>Results are ordered alphabetically.
867    *    <br>List is unmodifiable.
868    */
869   public List<MethodInfo> getDeclaredMethods() { return declaredMethods.get(); }
870
871   /**
872    * Returns the {@link Class} object representing the class or interface that declares the member class
873    * represented by this class.
874    *
875    * <p>
876    * This method returns the class in which this class is explicitly declared as a member.
877    * It only returns non-null for <b>member classes</b> (static and non-static nested classes).
878    *
879    * <p>
880    * <h5 class='section'>Examples:</h5>
881    * <ul class='spaced-list'>
882    *    <li><c>class Outer { class Inner {} }</c> - <c>Inner.getDeclaringClass()</c> returns <c>Outer</c>
883    *    <li><c>class Outer { static class Nested {} }</c> - <c>Nested.getDeclaringClass()</c> returns <c>Outer</c>
884    *    <li><c>class Outer { void method() { class Local {} } }</c> - <c>Local.getDeclaringClass()</c> returns <jk>null</jk>
885    *    <li>Top-level class - <c>getDeclaringClass()</c> returns <jk>null</jk>
886    *    <li>Anonymous class - <c>getDeclaringClass()</c> returns <jk>null</jk>
887    * </ul>
888    *
889    * <h5 class='section'>See Also:</h5><ul>
890    *    <li class='ja'>{@link #getEnclosingClass()} - Returns the immediately enclosing class (works for local and anonymous classes too)
891    * </ul>
892    *
893    * @return The declaring class, or <jk>null</jk> if this class is not a member of another class.
894    */
895   public ClassInfo getDeclaringClass() {
896      if (inner == null)
897         return null;
898      var dc = inner.getDeclaringClass();
899      return dc == null ? null : of(dc);
900   }
901
902   /**
903    * Returns the number of dimensions if this is an array type.
904    *
905    * @return The number of dimensions if this is an array type, or <c>0</c> if it is not.
906    */
907   public int getDimensions() { return dimensions.get(); }
908
909   /**
910    * Returns the immediately enclosing class of this class.
911    *
912    * <p>
913    * This method returns the lexically enclosing class, regardless of whether this class is a member,
914    * local, or anonymous class. Unlike {@link #getDeclaringClass()}, this method works for all types of nested classes.
915    *
916    * <p>
917    * <h5 class='section'>Examples:</h5>
918    * <ul class='spaced-list'>
919    *    <li><c>class Outer { class Inner {} }</c> - <c>Inner.getEnclosingClass()</c> returns <c>Outer</c>
920    *    <li><c>class Outer { static class Nested {} }</c> - <c>Nested.getEnclosingClass()</c> returns <c>Outer</c>
921    *    <li><c>class Outer { void method() { class Local {} } }</c> - <c>Local.getEnclosingClass()</c> returns <c>Outer</c>
922    *    <li><c>class Outer { void method() { new Runnable() {...} } }</c> - Anonymous class <c>getEnclosingClass()</c> returns <c>Outer</c>
923    *    <li>Top-level class - <c>getEnclosingClass()</c> returns <jk>null</jk>
924    * </ul>
925    *
926    * <h5 class='section'>Differences from {@link #getDeclaringClass()}:</h5>
927    * <ul class='spaced-list'>
928    *    <li><c>getDeclaringClass()</c> - Returns non-null only for member classes (static or non-static nested classes)
929    *    <li><c>getEnclosingClass()</c> - Returns non-null for all nested classes (member, local, and anonymous)
930    * </ul>
931    *
932    * <h5 class='section'>See Also:</h5><ul>
933    *    <li class='ja'>{@link #getDeclaringClass()} - Returns the declaring class (only for member classes)
934    *    <li class='ja'>{@link #getEnclosingConstructor()} - Returns the enclosing constructor (for classes defined in constructors)
935    *    <li class='ja'>{@link #getEnclosingMethod()} - Returns the enclosing method (for classes defined in methods)
936    * </ul>
937    *
938    * @return The enclosing class, or <jk>null</jk> if this is a top-level class.
939    */
940   public ClassInfo getEnclosingClass() {
941      if (inner == null)
942         return null;
943      var ec = inner.getEnclosingClass();
944      return ec == null ? null : of(ec);
945   }
946
947   /**
948    * Returns the {@link ConstructorInfo} object representing the constructor that declares this class if this is a
949    * local or anonymous class declared within a constructor.
950    *
951    * <p>
952    * Returns <jk>null</jk> if this class was not declared within a constructor.
953    *
954    * @return The enclosing constructor, or <jk>null</jk> if this class was not declared within a constructor.
955    */
956   public ConstructorInfo getEnclosingConstructor() {
957      if (inner == null)
958         return null;
959      Constructor<?> ec = inner.getEnclosingConstructor();
960      return ec == null ? null : getConstructor(ec);
961   }
962
963   /**
964    * Returns the {@link MethodInfo} object representing the method that declares this class if this is a
965    * local or anonymous class declared within a method.
966    *
967    * <p>
968    * Returns <jk>null</jk> if this class was not declared within a method.
969    *
970    * @return The enclosing method, or <jk>null</jk> if this class was not declared within a method.
971    */
972   public MethodInfo getEnclosingMethod() {
973      if (inner == null)
974         return null;
975      var em = inner.getEnclosingMethod();
976      return em == null ? null : getMethod(em);
977   }
978
979   /**
980    * Returns the {@link Type}s representing the interfaces directly implemented by this class.
981    *
982    * <p>
983    * Returns a cached, unmodifiable list.
984    * If a superinterface is a parameterized type, the {@link Type} returned for it reflects the actual
985    * type parameters used in the source code.
986    *
987    * @return
988    *    An unmodifiable list of {@link Type}s representing the interfaces directly implemented by this class.
989    *    <br>Returns an empty list if this class implements no interfaces.
990    */
991   public List<Type> getGenericInterfaces() { return genericInterfaces.get(); }
992
993   /**
994    * Returns the {@link Type} representing the direct superclass of this class.
995    *
996    * <p>
997    * If the superclass is a parameterized type, the {@link Type} returned reflects the actual
998    * type parameters used in the source code.
999    *
1000    * @return
1001    *    The superclass of this class as a {@link Type},
1002    *    or <jk>null</jk> if this class represents {@link Object}, an interface, a primitive type, or void.
1003    */
1004   public Type getGenericSuperclass() { return inner == null ? null : inner.getGenericSuperclass(); }
1005
1006   /**
1007    * Returns a list of interfaces defined on this class and superclasses.
1008    *
1009    * <p>
1010    * Results are in child-to-parent order.
1011    *
1012    * @return
1013    *    An unmodifiable list of interfaces defined on this class and superclasses.
1014    *    <br>Results are in child-to-parent order.
1015    */
1016   public List<ClassInfo> getInterfaces() { return interfaces.get(); }
1017
1018   @Override /* Annotatable */
1019   public String getLabel() { return getNameSimple(); }
1020
1021   /**
1022    * Returns all public member classes and interfaces declared by this class and its superclasses.
1023    *
1024    * <p>
1025    * This includes public class and interface members inherited from superclasses and
1026    * public class and interface members declared by the class.
1027    *
1028    * @return
1029    *    An unmodifiable list of all public member classes and interfaces declared by this class.
1030    *    <br>Returns an empty list if this class has no public member classes or interfaces.
1031    */
1032   public List<ClassInfo> getMemberClasses() {
1033      if (inner == null)
1034         return u(l());
1035      Class<?>[] classes = inner.getClasses();
1036      List<ClassInfo> l = listOfSize(classes.length);
1037      for (Class<?> cc : classes)
1038         l.add(of(cc));
1039      return u(l);
1040   }
1041
1042   /**
1043    * Returns the first matching method on this class.
1044    *
1045    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
1046    * @return The first matching method, or <jk>null</jk> if no methods matched.
1047    */
1048   public Optional<MethodInfo> getMethod(Predicate<MethodInfo> filter) {
1049      return allMethods.get().stream().filter(x -> test(filter, x)).findFirst();
1050   }
1051
1052   /**
1053    * Returns the module that this class is a member of.
1054    *
1055    * <p>
1056    * If this class is not in a named module, returns the unnamed module of the class loader for this class.
1057    *
1058    * @return The module that this class is a member of.
1059    */
1060   public Module getModule() { return inner == null ? null : inner.getModule(); }
1061
1062   /**
1063    * Returns the name of the underlying class.
1064    *
1065    * <p>
1066    * Equivalent to calling {@link Class#getName()} or {@link Type#getTypeName()} depending on whether
1067    * this is a class or type.
1068    *
1069    * <p>
1070    * This method returns the JVM internal format for class names:
1071    * <ul>
1072    *    <li>Uses fully qualified package names
1073    *    <li>Uses <js>'$'</js> separator for nested classes
1074    *    <li>Uses JVM notation for arrays (e.g., <js>"[Ljava.lang.String;"</js>)
1075    *    <li>Uses single letters for primitive arrays (e.g., <js>"[I"</js> for <c>int[]</c>)
1076    * </ul>
1077    *
1078    * <h5 class='section'>Examples:</h5>
1079    * <ul>
1080    *    <li><js>"java.lang.String"</js> - Normal class
1081    *    <li><js>"[Ljava.lang.String;"</js> - Array
1082    *    <li><js>"[[Ljava.lang.String;"</js> - Multi-dimensional array
1083    *    <li><js>"java.util.Map$Entry"</js> - Nested class
1084    *    <li><js>"int"</js> - Primitive class
1085    *    <li><js>"[I"</js> - Primitive array
1086    *    <li><js>"[[I"</js> - Multi-dimensional primitive array
1087    * </ul>
1088    *
1089    * <h5 class='section'>See Also:</h5>
1090    * <ul class='spaced-list'>
1091    *    <li><c>{@link #getNameCanonical()}</c> - Java source code format (uses <js>'.'</js> and <js>"[]"</js>)
1092    *    <li><c>{@link #getNameSimple()}</c> - Simple class name without package
1093    *    <li><c>{@link #getNameFull()}</c> - Full name with type parameters
1094    *    <li><c>{@link #getNameShort()}</c> - Short name with type parameters
1095    *    <li><c>{@link #getNameReadable()}</c> - Human-readable name (uses <js>"Array"</js> suffix)
1096    * </ul>
1097    *
1098    * @return The name of the underlying class in JVM format.
1099    */
1100   public String getName() { return nn(inner) ? inner.getName() : innerType.getTypeName(); }
1101
1102   /**
1103    * Returns the canonical name of the underlying class.
1104    *
1105    * <p>
1106    * The canonical name is the name that would be used in Java source code to refer to the class.
1107    * For example:
1108    * <ul>
1109    *    <li><js>"java.lang.String"</js> - Normal class
1110    *    <li><js>"java.lang.String[]"</js> - Array
1111    *    <li><js>"java.util.Map.Entry"</js> - Nested class
1112    *    <li><jk>null</jk> - Local or anonymous class
1113    * </ul>
1114    *
1115    * @return The canonical name of the underlying class, or <jk>null</jk> if this class doesn't have a canonical name.
1116    */
1117   public String getNameCanonical() {
1118      // For Class objects, delegate to Class.getCanonicalName() which handles local/anonymous classes
1119      // For Type objects (ParameterizedType, etc.), compute the canonical name
1120      if (inner != null && ! isParameterizedType) {
1121         return inner.getCanonicalName();
1122      }
1123      // For ParameterizedType, we can't have a true canonical name with type parameters
1124      // Return null to maintain consistency with Class.getCanonicalName() behavior
1125      return null;
1126   }
1127
1128   /**
1129    * Returns a formatted class name with configurable options.
1130    *
1131    * <p>
1132    * This is a unified method that can produce output equivalent to all other name methods
1133    * by varying the parameters.
1134    *
1135    * <h5 class='section'>Examples:</h5>
1136    * <p class='bjava'>
1137    *    <jc>// Given: java.util.HashMap&lt;String,Integer&gt;[]</jc>
1138    *
1139    *    <jc>// Full name with generics</jc>
1140    *    getFormattedName(ClassNameFormat.<jsf>FULL</jsf>, <jk>true</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
1141    *    <jc>// → "java.util.HashMap&lt;java.lang.String,java.lang.Integer&gt;[]"</jc>
1142    *
1143    *    <jc>// Short name with generics</jc>
1144    *    getFormattedName(ClassNameFormat.<jsf>SHORT</jsf>, <jk>true</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
1145    *    <jc>// → "HashMap&lt;String,Integer&gt;[]"</jc>
1146    *
1147    *    <jc>// Simple name</jc>
1148    *    getFormattedName(ClassNameFormat.<jsf>SIMPLE</jsf>, <jk>false</jk>, '$', ClassArrayFormat.<jsf>BRACKETS</jsf>)
1149    *    <jc>// → "HashMap[]"</jc>
1150    *
1151    *    <jc>// With dot separator</jc>
1152    *    getFormattedName(ClassNameFormat.<jsf>SHORT</jsf>, <jk>false</jk>, '.', ClassArrayFormat.<jsf>BRACKETS</jsf>)
1153    *    <jc>// → "Map.Entry"</jc>
1154    *
1155    *    <jc>// Word format for arrays</jc>
1156    *    getFormattedName(ClassNameFormat.<jsf>SIMPLE</jsf>, <jk>false</jk>, '$', ClassArrayFormat.<jsf>WORD</jsf>)
1157    *    <jc>// → "HashMapArray"</jc>
1158    * </p>
1159    *
1160    * <h5 class='section'>Equivalent Methods:</h5>
1161    * <ul class='spaced-list'>
1162    *    <li><c>getName()</c> = <c>getFormattedName(FULL, <jk>false</jk>, '$', JVM)</c>
1163    *    <li><c>getCanonicalName()</c> = <c>getFormattedName(FULL, <jk>false</jk>, '.', BRACKETS)</c>
1164    *    <li><c>getSimpleName()</c> = <c>getFormattedName(SIMPLE, <jk>false</jk>, '$', BRACKETS)</c>
1165    *    <li><c>getFullName()</c> = <c>getFormattedName(FULL, <jk>true</jk>, '$', BRACKETS)</c>
1166    *    <li><c>getShortName()</c> = <c>getFormattedName(SHORT, <jk>true</jk>, '$', BRACKETS)</c>
1167    *    <li><c>getReadableName()</c> = <c>getFormattedName(SIMPLE, <jk>false</jk>, '$', WORD)</c>
1168    * </ul>
1169    *
1170    * @param nameFormat
1171    *    Controls which parts of the class name to include (package, outer classes).
1172    * @param includeTypeParams
1173    *    If <jk>true</jk>, include generic type parameters recursively.
1174    *    <br>For example: <js>"HashMap&lt;String,Integer&gt;"</js> instead of <js>"HashMap"</js>
1175    * @param separator
1176    *    Character to use between outer and inner class names.
1177    *    <br>Typically <js>'$'</js> (JVM format) or <js>'.'</js> (canonical format).
1178    *    <br>Ignored when <c>nameFormat</c> is {@link ClassNameFormat#SIMPLE}.
1179    * @param arrayFormat
1180    *    How to format array dimensions.
1181    * @return
1182    *    The formatted class name.
1183    */
1184   public String getNameFormatted(ClassNameFormat nameFormat, boolean includeTypeParams, char separator, ClassArrayFormat arrayFormat) {
1185      var sb = new StringBuilder(128);
1186      appendNameFormatted(sb, nameFormat, includeTypeParams, separator, arrayFormat);
1187      return sb.toString();
1188   }
1189
1190   /**
1191    * Returns the full name of this class.
1192    *
1193    * <h5 class='section'>Examples:</h5>
1194    * <ul>
1195    *    <li><js>"com.foo.MyClass"</js> - Normal class
1196    *    <li><js>"com.foo.MyClass[][]"</js> - Array.
1197    *    <li><js>"com.foo.MyClass$InnerClass"</js> - Inner class.
1198    *    <li><js>"com.foo.MyClass$InnerClass[][]"</js> - Inner class array.
1199    *    <li><js>"int"</js> - Primitive class.
1200    *    <li><js>"int[][]"</js> - Primitive class class.
1201    *    <li><js>"java.util.Map&lt;java.lang.String,java.lang.Object&gt;"</js> - Parameterized type.
1202    *    <li><js>"java.util.AbstractMap&lt;K,V&gt;"</js> - Parameterized generic type.
1203    *    <li><js>"V"</js> - Parameterized generic type argument.
1204    * </ul>
1205    *
1206    * @return The underlying class name.
1207    */
1208   public String getNameFull() { return fullName.get(); }
1209
1210   /**
1211    * Same as {@link #getNameSimple()} but uses <js>"Array"</js> instead of <js>"[]"</js>.
1212    *
1213    * @return The readable name for this class.
1214    */
1215   public String getNameReadable() { return readableName.get(); }
1216
1217   /**
1218    * Returns all possible names for this class.
1219    *
1220    * @return
1221    *    An array consisting of:
1222    *    <ul>
1223    *       <li>{@link #getNameFull()}
1224    *       <li>{@link Class#getName()} - Note that this might be a dup.
1225    *       <li>{@link #getNameShort()}
1226    *       <li>{@link #getNameSimple()}
1227    *    </ul>
1228    */
1229   public String[] getNames() { return a(getNameFull(), inner.getName(), getNameShort(), getNameSimple()); }
1230
1231   /**
1232    * Returns the short name of the underlying class.
1233    *
1234    * <p>
1235    * Similar to {@link #getNameSimple()} but also renders local or member class name prefixes.
1236    *
1237    * @return The short name of the underlying class.
1238    */
1239   public String getNameShort() { return shortName.get(); }
1240
1241   /**
1242    * Returns the simple name of the underlying class.
1243    *
1244    * <p>
1245    * Returns either {@link Class#getSimpleName()} or {@link Type#getTypeName()} depending on whether
1246    * this is a class or type.
1247    *
1248    * @return The simple name of the underlying class;
1249    */
1250   public String getNameSimple() { return nn(inner) ? inner.getSimpleName() : innerType.getTypeName(); }
1251
1252   /**
1253    * Returns the nest host of this class.
1254    *
1255    * <p>
1256    * Every class belongs to exactly one nest. A class that is not a member of a nest
1257    * is its own nest host.
1258    *
1259    * @return The nest host of this class.
1260    */
1261   public ClassInfo getNestHost() {
1262      if (inner == null)
1263         return null;
1264      return of(inner.getNestHost());
1265   }
1266
1267   /**
1268    * Returns an array containing all the classes and interfaces that are members of the nest
1269    * to which this class belongs.
1270    *
1271    * @return
1272    *    An unmodifiable list of all classes and interfaces in the same nest as this class.
1273    *    <br>Returns an empty list if this object does not represent a class.
1274    */
1275   public List<ClassInfo> getNestMembers() {
1276      if (inner == null)
1277         return u(l());
1278      var members = inner.getNestMembers();
1279      List<ClassInfo> l = listOfSize(members.length);
1280      for (Class<?> cc : members)
1281         l.add(of(cc));
1282      return u(l);
1283   }
1284
1285   /**
1286    * Locates the no-arg constructor for this class.
1287    *
1288    * <p>
1289    * Constructor must match the visibility requirements specified by parameter 'v'.
1290    * If class is abstract, always returns <jk>null</jk>.
1291    * Note that this also returns the 1-arg constructor for non-static member classes.
1292    *
1293    * @param v The minimum visibility.
1294    * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility.
1295    */
1296   public Optional<ConstructorInfo> getNoArgConstructor(Visibility v) {
1297      if (isAbstract())
1298         return opte();
1299      int expectedParams = isNonStaticMemberClass() ? 1 : 0;
1300      // @formatter:off
1301      return getDeclaredConstructors().stream()
1302         .filter(cc -> cc.hasNumParameters(expectedParams))
1303         .filter(cc -> cc.isVisible(v))
1304         .map(cc -> cc.accessible())
1305         .findFirst();
1306      // @formatter:on
1307   }
1308
1309   /**
1310    * Returns the package of this class.
1311    *
1312    * <p>
1313    * Returns <jk>null</jk> in the following cases:
1314    * <ul>
1315    *    <li>Primitive types (e.g., <c>int.class</c>, <c>boolean.class</c>)
1316    *    <li>Array types of primitives (e.g., <c>int[].class</c>)
1317    *    <li>Classes in the default (unnamed) package - while the default package is technically a package,
1318    *       Java's <c>Class.getPackage()</c> returns <jk>null</jk> for classes in the default package because
1319    *       it has no name or associated <c>Package</c> object
1320    * </ul>
1321    *
1322    * @return The package of this class wrapped in a {@link PackageInfo}, or <jk>null</jk> if this class has no package
1323    *    or is in the default (unnamed) package.
1324    */
1325   public PackageInfo getPackage() { return packageInfo.get(); }
1326
1327   /**
1328    * Returns the specified annotation only if it's been declared on the package of this class.
1329    *
1330    * @param <A> The annotation type to look for.
1331    * @param type The annotation class.
1332    * @return The annotation, or <jk>null</jk> if not found.
1333    */
1334   public <A extends Annotation> A getPackageAnnotation(Class<A> type) {
1335      PackageInfo pi = getPackage();
1336      if (pi == null)
1337         return null;
1338      var ai = pi.getAnnotations(type).findFirst().orElse(null);
1339      return ai == null ? null : ai.inner();
1340   }
1341
1342   /**
1343    * Finds the real parameter type of this class.
1344    *
1345    * @param index The zero-based index of the parameter to resolve.
1346    * @param pt The parameterized type class containing the parameterized type to resolve (e.g. <c>HashMap</c>).
1347    * @return The resolved real class.
1348    */
1349   public Class<?> getParameterType(int index, Class<?> pt) {
1350      assertArgNotNull("pt", pt);
1351
1352      // We need to make up a mapping of type names.
1353      var typeMap = new HashMap<Type,Type>();
1354      var cc = inner;
1355      while (pt != cc.getSuperclass()) {
1356         extractTypes(typeMap, cc);
1357         cc = cc.getSuperclass();
1358         assertArg(nn(cc), "Class ''{0}'' is not a subclass of parameterized type ''{1}''", inner.getSimpleName(), pt.getSimpleName());
1359      }
1360
1361      Type gsc = cc.getGenericSuperclass();
1362
1363      assertArg(gsc instanceof ParameterizedType, "Class ''{0}'' is not a parameterized type", pt.getSimpleName());
1364
1365      var cpt = (ParameterizedType)gsc;
1366      Type[] atArgs = cpt.getActualTypeArguments();
1367      assertArg(index < atArgs.length, "Invalid type index. index={0}, argsLength={1}", index, atArgs.length);
1368      Type actualType = cpt.getActualTypeArguments()[index];
1369
1370      if (typeMap.containsKey(actualType))
1371         actualType = typeMap.get(actualType);
1372
1373      if (actualType instanceof Class actualType2) {
1374         return actualType2;
1375
1376      } else if (actualType instanceof GenericArrayType actualType2) {
1377         Type gct = actualType2.getGenericComponentType();
1378         if (gct instanceof ParameterizedType gct2)
1379            return Array.newInstance((Class<?>)gct2.getRawType(), 0).getClass();
1380      } else if (actualType instanceof TypeVariable<?> actualType3) {
1381         var nestedOuterTypes = new LinkedList<Class<?>>();
1382         for (Class<?> ec = cc.getEnclosingClass(); nn(ec); ec = ec.getEnclosingClass()) {
1383            var outerClass = ec;
1384            nestedOuterTypes.add(outerClass);
1385            var outerTypeMap = new HashMap<Type,Type>();
1386            extractTypes(outerTypeMap, outerClass);
1387            for (var entry : outerTypeMap.entrySet()) {
1388               // HTT - Hard to test - requires nested generics where a type variable in an inner class
1389               // maps to another TypeVariable (not a Class) in the outer class's type map, which then
1390               // needs to be resolved further up the enclosing class chain.
1391               var key = entry.getKey();
1392               var value = entry.getValue();
1393               if (key instanceof TypeVariable<?> key2) {
1394                  if (key2.getName().equals(actualType3.getName()) && isInnerClass(key2.getGenericDeclaration(), actualType3.getGenericDeclaration())) {
1395                     if (value instanceof Class<?> value2)
1396                        return value2;
1397                     actualType3 = (TypeVariable<?>)entry.getValue();
1398                  }
1399               }
1400            }
1401         }
1402      } else if (actualType instanceof ParameterizedType actualType2) {
1403         return (Class<?>)actualType2.getRawType();
1404      }
1405      throw illegalArg("Could not resolve variable ''{0}'' to a type.", actualType.getTypeName());
1406   }
1407
1408   /**
1409    * Extracts generic type parameter mappings from a class's superclass declaration and adds them to a type map.
1410    *
1411    * <p>
1412    * This method builds a mapping between type variables (e.g., <c>T</c>, <c>K</c>, <c>V</c>) and their actual
1413    * type arguments in the inheritance hierarchy. It's used to resolve generic type parameters when navigating
1414    * through class hierarchies.
1415    *
1416    * <p>
1417    * The method handles transitive type mappings by checking if an actual type argument is itself a type variable
1418    * that's already been mapped, and resolving it to its final type.
1419    *
1420    * @param typeMap
1421    *    The map to populate with type variable to actual type mappings.
1422    *    <br>Existing entries are used to resolve transitive mappings.
1423    * @param c
1424    *    The class whose generic superclass should be examined for type parameters.
1425    */
1426   private static void extractTypes(Map<Type,Type> typeMap, Class<?> c) {
1427      var gs = c.getGenericSuperclass();
1428      if (gs instanceof ParameterizedType gs2) {
1429         var typeParameters = ((Class<?>)gs2.getRawType()).getTypeParameters();
1430         var actualTypeArguments = gs2.getActualTypeArguments();
1431         for (var i = 0; i < typeParameters.length; i++) {
1432            if (typeMap.containsKey(actualTypeArguments[i]))
1433               actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]);
1434            typeMap.put(typeParameters[i], actualTypeArguments[i]);
1435         }
1436      }
1437   }
1438
1439   /**
1440    * Checks if one generic declaration (typically a class) is an inner class of another.
1441    *
1442    * <p>
1443    * This method walks up the enclosing class hierarchy of the potential inner class to determine if it's
1444    * nested within the outer class at any level. It's used during generic type resolution to determine if
1445    * a type variable belongs to an inner class that's nested within an outer class.
1446    *
1447    * @param od
1448    *    The potential outer declaration (typically an outer class).
1449    * @param id
1450    *    The potential inner declaration (typically an inner class).
1451    * @return
1452    *    <jk>true</jk> if <c>id</c> is nested within <c>od</c> at any level, <jk>false</jk> otherwise.
1453    *    <br>Returns <jk>false</jk> if either parameter is not a <c>Class</c>.
1454    */
1455   private static boolean isInnerClass(GenericDeclaration od, GenericDeclaration id) {
1456      if (od instanceof Class<?> oc && id instanceof Class<?> ic) {
1457         while (nn(ic = ic.getEnclosingClass()))
1458            if (ic == oc)
1459               return true;
1460      }
1461      return false;
1462   }
1463
1464   /**
1465    * Returns a list including this class and all parent classes.
1466    *
1467    * <p>
1468    * Does not include interfaces.
1469    *
1470    * <p>
1471    * Results are in child-to-parent order.
1472    *
1473    * @return An unmodifiable list including this class and all parent classes.
1474    *    <br>Results are in child-to-parent order.
1475    */
1476   public List<ClassInfo> getParents() { return parents.get(); }
1477
1478   /**
1479    * Returns all parent classes and interfaces in proper traversal order.
1480    *
1481    * <p>
1482    * This method returns a unique list of all parent classes (including this class) and all interfaces
1483    * (including interface hierarchies) with proper handling of duplicates. The order is:
1484    * <ol>
1485    *    <li>This class
1486    *    <li>Parent classes in child-to-parent order
1487    *    <li>For each class, interfaces declared on that class and their parent interfaces
1488    * </ol>
1489    *
1490    * <p>
1491    * This is useful for annotation processing where you need to traverse the complete type hierarchy
1492    * without duplicates.
1493    *
1494    * <h5 class='section'>Example:</h5>
1495    * <p class='bjava'>
1496    *    <jc>// Interface hierarchy:</jc>
1497    *    <jk>interface</jk> ISuperGrandParent {}
1498    *    <jk>interface</jk> IGrandParent <jk>extends</jk> ISuperGrandParent {}
1499    *    <jk>interface</jk> ISuperParent {}
1500    *    <jk>interface</jk> IParent <jk>extends</jk> ISuperParent {}
1501    *    <jk>interface</jk> IChild {}
1502    *
1503    *    <jc>// Class hierarchy:</jc>
1504    *    <jk>class</jk> GrandParent <jk>implements</jk> IGrandParent {}
1505    *    <jk>class</jk> Parent <jk>extends</jk> GrandParent <jk>implements</jk> IParent {}
1506    *    <jk>class</jk> Child <jk>extends</jk> Parent <jk>implements</jk> IChild {}
1507    *
1508    *    <jc>// For Child, returns (in this order):</jc>
1509    *    ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(Child.<jk>class</jk>);
1510    *    List&lt;ClassInfo&gt; <jv>result</jv> = <jv>ci</jv>.getParentsAndInterfaces();
1511    *    <jc>// Result: [</jc>
1512    *    <jc>//   Child,                  // 1. This class</jc>
1513    *    <jc>//   IChild,                 // 2. Interface on Child</jc>
1514    *    <jc>//   Parent,                 // 3. Parent class</jc>
1515    *    <jc>//   IParent,                // 4. Interface on Parent</jc>
1516    *    <jc>//   ISuperParent,           // 5. Parent interface of IParent</jc>
1517    *    <jc>//   GrandParent,            // 6. Grandparent class</jc>
1518    *    <jc>//   IGrandParent,           // 7. Interface on GrandParent</jc>
1519    *    <jc>//   ISuperGrandParent       // 8. Parent interface of IGrandParent</jc>
1520    *    <jc>// ]</jc>
1521    * </p>
1522    *
1523    * @return An unmodifiable list of all parent classes and interfaces, properly ordered without duplicates.
1524    */
1525   public List<ClassInfo> getParentsAndInterfaces() { return parentsAndInterfaces.get(); }
1526
1527   /**
1528    * Returns the permitted subclasses of this sealed class.
1529    *
1530    * <p>
1531    * If this class is not sealed, returns an empty list.
1532    *
1533    * @return
1534    *    An unmodifiable list of permitted subclasses if this is a sealed class.
1535    *    <br>Returns an empty list if this class is not sealed.
1536    */
1537   public List<ClassInfo> getPermittedSubclasses() {
1538      if (inner == null || ! inner.isSealed())
1539         return u(l());
1540      return u(stream(inner.getPermittedSubclasses()).map(ClassInfo::of).toList());
1541   }
1542
1543   /**
1544    * Returns the default value for this primitive class.
1545    *
1546    * @return The default value, or <jk>null</jk> if this is not a primitive class.
1547    */
1548   public Object getPrimitiveDefault() { return primitiveDefaultMap.get(inner); }
1549
1550   /**
1551    * If this class is a primitive wrapper (e.g. <code><jk>Integer</jk>.<jk>class</jk></code>) returns it's
1552    * primitive class (e.g. <code>int.<jk>class</jk></code>).
1553    *
1554    * @return The primitive class, or <jk>null</jk> if class is not a primitive wrapper.
1555    */
1556   public Class<?> getPrimitiveForWrapper() { return pmap2.get(inner); }
1557
1558   /**
1559    * If this class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class
1560    * (e.g. <code>Integer.<jk>class</jk></code>).
1561    *
1562    * @return The wrapper class, or <jk>null</jk> if class is not a primitive.
1563    */
1564   public Class<?> getPrimitiveWrapper() { return pmap1.get(inner); }
1565
1566   /**
1567    * Returns the {@link ProtectionDomain} of this class.
1568    *
1569    * <p>
1570    * If a security manager is installed, this method requires <c>RuntimePermission("getProtectionDomain")</c>.
1571    *
1572    * @return The {@link ProtectionDomain} of this class, or <jk>null</jk> if the class does not have a protection domain.
1573    */
1574   public java.security.ProtectionDomain getProtectionDomain() { return inner == null ? null : inner.getProtectionDomain(); }
1575
1576   /**
1577    * Returns the first matching public constructor on this class.
1578    *
1579    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
1580    * @return The public constructor that matches the specified predicate.
1581    */
1582   public Optional<ConstructorInfo> getPublicConstructor(Predicate<ConstructorInfo> filter) {
1583      return publicConstructors.get().stream().filter(x -> test(filter, x)).findFirst();
1584   }
1585
1586   /**
1587    * Returns all the public constructors defined on this class.
1588    *
1589    * @return All public constructors defined on this class.
1590    */
1591   public List<ConstructorInfo> getPublicConstructors() { return publicConstructors.get(); }
1592
1593   /**
1594    * Returns the first matching public field on this class.
1595    *
1596    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
1597    * @return The public field, or <jk>null</jk> if not found.
1598    */
1599   public Optional<FieldInfo> getPublicField(Predicate<FieldInfo> filter) {
1600      return publicFields.get().stream().filter(x -> test(filter, x)).findFirst();
1601   }
1602
1603   /**
1604    * Returns all public fields on this class.
1605    *
1606    * <p>
1607    * Hidden fields are excluded from the results.
1608    *
1609    * @return
1610    *    All public fields on this class.
1611    *    <br>Results are in alphabetical order.
1612    *    <br>List is unmodifiable.
1613    */
1614   public List<FieldInfo> getPublicFields() { return publicFields.get(); }
1615
1616   /**
1617    * Returns the first matching public method on this class.
1618    *
1619    * @param filter A predicate to apply to the entries to determine if value should be used.  Can be <jk>null</jk>.
1620    * @return The first matching method, or <jk>null</jk> if no methods matched.
1621    */
1622   public Optional<MethodInfo> getPublicMethod(Predicate<MethodInfo> filter) {
1623      return publicMethods.get().stream().filter(x -> test(filter, x)).findFirst();
1624   }
1625
1626   /**
1627    * Returns all public methods on this class and parent classes.
1628    *
1629    * <p>
1630    * This method returns <b>public methods only</b>, from this class and all parent classes and interfaces.
1631    *
1632    * <h5 class='section'>Comparison with Similar Methods:</h5>
1633    * <ul class='spaced-list'>
1634    *    <li>{@link #getDeclaredMethods()} - Returns all declared methods on this class only (all visibility levels)
1635    *    <li>{@link #getAllMethods()} - Returns all declared methods on this class and parents (all visibility levels)
1636    *    <li>{@link #getPublicMethods()} - Returns public methods only on this class and parents ← This method
1637    * </ul>
1638    *
1639    * <h5 class='section'>Notes:</h5>
1640    * <ul class='spaced-list'>
1641    *    <li>This method behaves similarly to Java's {@link Class#getMethods()}, returning only public methods.
1642    *    <li>Methods defined on the {@link Object} class are excluded from the results.
1643    * </ul>
1644    *
1645    * @return
1646    *    All public methods on this class and parent classes.
1647    *    <br>Results are ordered alphabetically.
1648    *    <br>List is unmodifiable.
1649    */
1650   public List<MethodInfo> getPublicMethods() { return publicMethods.get(); }
1651
1652   /**
1653    * Returns the record components of this record class.
1654    *
1655    * <p>
1656    * Returns a cached, unmodifiable list of record components.
1657    * The components are returned in the same order as they appear in the record declaration.
1658    * If this class is not a record, returns an empty list.
1659    *
1660    * @return An unmodifiable list of record components, or an empty list if this class is not a record.
1661    */
1662   public List<RecordComponent> getRecordComponents() { return recordComponents.get(); }
1663
1664   /**
1665    * Returns the repeated annotation method on this class.
1666    *
1667    * <p>
1668    * The repeated annotation method is the <code>value()</code> method that returns an array
1669    * of annotations who themselves are marked with the {@link Repeatable @Repeatable} annotation
1670    * of this class.
1671    *
1672    * @return The repeated annotation method on this class, or <jk>null</jk> if it doesn't exist.
1673    */
1674   public MethodInfo getRepeatedAnnotationMethod() { return repeatedAnnotationMethod.get(); }
1675
1676   /**
1677    * Finds a resource with a given name.
1678    *
1679    * <p>
1680    * The rules for searching resources associated with a given class are implemented by the defining class loader.
1681    *
1682    * @param name The resource name.
1683    * @return A URL object for reading the resource, or <jk>null</jk> if the resource could not be found.
1684    */
1685   public java.net.URL getResource(String name) {
1686      return inner == null ? null : inner.getResource(name);
1687   }
1688
1689   /**
1690    * Finds a resource with a given name and returns an input stream for reading the resource.
1691    *
1692    * <p>
1693    * The rules for searching resources associated with a given class are implemented by the defining class loader.
1694    *
1695    * @param name The resource name.
1696    * @return An input stream for reading the resource, or <jk>null</jk> if the resource could not be found.
1697    */
1698   public java.io.InputStream getResourceAsStream(String name) {
1699      return inner == null ? null : inner.getResourceAsStream(name);
1700   }
1701
1702   /**
1703    * Returns the signers of this class.
1704    *
1705    * <p>
1706    * Returns a cached, unmodifiable list.
1707    *
1708    * @return An unmodifiable list of signers, or an empty list if there are no signers.
1709    */
1710   public List<Object> getSigners() { return signers.get(); }
1711
1712   /**
1713    * Returns the parent class.
1714    *
1715    * @return
1716    *    The parent class, or <jk>null</jk> if the class has no parent.
1717    */
1718   public ClassInfo getSuperclass() {
1719      if (inner == null)
1720         return null;
1721      var sc = inner.getSuperclass();
1722      return sc == null ? null : of(sc);
1723   }
1724
1725   /**
1726    * Returns a list of {@link TypeVariable} objects that represent the type variables declared by this class.
1727    *
1728    * <p>
1729    * Returns a cached, unmodifiable list.
1730    * The type variables are returned in the same order as they appear in the class declaration.
1731    *
1732    * @return
1733    *    An unmodifiable list of {@link TypeVariable} objects representing the type parameters of this class.
1734    *    <br>Returns an empty list if this class declares no type parameters.
1735    */
1736   public List<TypeVariable<?>> getTypeParameters() { return typeParameters.get(); }
1737
1738   /**
1739    * Returns the wrapper class if this is a primitive, otherwise returns this class.
1740    *
1741    * <p>
1742    * If this class is a primitive (e.g. {@code int.class}), returns its wrapper class
1743    * (e.g. {@code Integer.class}) wrapped in a {@link ClassInfo}.
1744    * Otherwise, returns this {@link ClassInfo} instance.
1745    *
1746    * <h5 class='section'>Example:</h5>
1747    * <p class='bjava'>
1748    *    ClassInfo <jv>intClass</jv> = ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>);
1749    *    ClassInfo <jv>wrapper</jv> = <jv>intClass</jv>.getWrapperIfPrimitive();  <jc>// Returns ClassInfo for Integer.class</jc>
1750    *
1751    *    ClassInfo <jv>stringClass</jv> = ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>);
1752    *    ClassInfo <jv>same</jv> = <jv>stringClass</jv>.getWrapperIfPrimitive();  <jc>// Returns same ClassInfo</jc>
1753    * </p>
1754    *
1755    * @return The wrapper {@link ClassInfo} if this is a primitive, or this {@link ClassInfo} if not a primitive.
1756    */
1757   public ClassInfo getWrapperIfPrimitive() {
1758      if (inner == null || ! inner.isPrimitive())
1759         return this;
1760      return of(pmap1.get(inner));
1761   }
1762
1763   /**
1764    * Returns <jk>true</jk> if this class has the specified annotation.
1765    *
1766    * @param <A> The annotation type to look for.
1767    * @param type The annotation to look for.
1768    * @return The <jk>true</jk> if annotation if found.
1769    */
1770   public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
1771      return getAnnotations(type).findFirst().isPresent();
1772   }
1773
1774   @Override
1775   public int hashCode() {
1776      return innerType.hashCode();
1777   }
1778
1779   /**
1780    * Returns <jk>true</jk> if this class is not in the root package.
1781    *
1782    * @return <jk>true</jk> if this class is not in the root package.
1783    */
1784   public boolean hasPackage() {
1785      return nn(getPackage());
1786   }
1787
1788   /**
1789    * Returns <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value.
1790    *
1791    * @return <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value.
1792    */
1793   public boolean hasPrimitiveWrapper() {
1794      return pmap1.containsKey(inner);
1795   }
1796
1797   /**
1798    * Returns the wrapped class as a {@link Class}.
1799    *
1800    * @param <T> The inner class type.
1801    * @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}).
1802    */
1803   public <T> Class<T> inner() {
1804      return (Class<T>)inner;
1805   }
1806
1807   /**
1808    * Returns the wrapped class as a {@link Type}.
1809    *
1810    * @return The wrapped class as a {@link Type}.
1811    */
1812   public Type innerType() {
1813      return innerType;
1814   }
1815
1816   /**
1817    * Checks for equality with the specified class.
1818    *
1819    * @param c The class to check equality with.
1820    * @return <jk>true</jk> if the specified class is the same as this one.
1821    */
1822   public boolean is(Class<?> c) {
1823      return nn(this.inner) && this.inner.equals(c);
1824   }
1825
1826   /**
1827    * Checks for equality with the specified class.
1828    *
1829    * @param c The class to check equality with.
1830    * @return <jk>true</jk> if the specified class is the same as this one.
1831    */
1832   public boolean is(ClassInfo c) {
1833      if (nn(this.inner))
1834         return this.inner.equals(c.inner());
1835      return innerType.equals(c.innerType);
1836   }
1837
1838   /**
1839    * Returns <jk>true</jk> if all specified flags are applicable to this class.
1840    *
1841    * @param flag The flag to test for.
1842    * @return <jk>true</jk> if all specified flags are applicable to this class.
1843    */
1844   @Override
1845   public boolean is(ElementFlag flag) {
1846      return switch (flag) {
1847         case ANNOTATION -> isAnnotation();
1848         case NOT_ANNOTATION -> ! isAnnotation();
1849         case ANONYMOUS -> isAnonymousClass();
1850         case NOT_ANONYMOUS -> ! isAnonymousClass();
1851         case ARRAY -> isArray();
1852         case NOT_ARRAY -> ! isArray();
1853         case CLASS -> ! isInterface();
1854         case DEPRECATED -> isDeprecated();
1855         case NOT_DEPRECATED -> isNotDeprecated();
1856         case ENUM -> isEnum();
1857         case NOT_ENUM -> ! isEnum();
1858         case LOCAL -> isLocalClass();
1859         case NOT_LOCAL -> ! isLocalClass();
1860         case MEMBER -> isMemberClass();
1861         case NOT_MEMBER -> isNotMemberClass();
1862         case NON_STATIC_MEMBER -> isNonStaticMemberClass();
1863         case NOT_NON_STATIC_MEMBER -> ! isNonStaticMemberClass();
1864         case PRIMITIVE -> isPrimitive();
1865         case NOT_PRIMITIVE -> ! isPrimitive();
1866         case RECORD -> isRecord();
1867         case NOT_RECORD -> ! isRecord();
1868         case SEALED -> isSealed();
1869         case NOT_SEALED -> ! isSealed();
1870         case SYNTHETIC -> isSynthetic();
1871         case NOT_SYNTHETIC -> ! isSynthetic(); // HTT - synthetic classes are compiler-generated and difficult to create in tests
1872         default -> super.is(flag);
1873      };
1874   }
1875
1876   /**
1877    * Returns <jk>true</jk> if this class is an annotation.
1878    *
1879    * @return <jk>true</jk> if this class is an annotation.
1880    */
1881   public boolean isAnnotation() { return nn(inner) && inner.isAnnotation(); }
1882
1883   /**
1884    * Returns <jk>true</jk> if this class is an anonymous class.
1885    *
1886    * <p>
1887    * An anonymous class is a local class declared within a method or constructor that has no name.
1888    *
1889    * @return <jk>true</jk> if this class is an anonymous class.
1890    */
1891   public boolean isAnonymousClass() { return nn(inner) && inner.isAnonymousClass(); }
1892
1893   /**
1894    * Returns <jk>true</jk> if this class is any of the specified types.
1895    *
1896    * @param types The types to check against.
1897    * @return <jk>true</jk> if this class is any of the specified types.
1898    */
1899   public boolean isAny(Class<?>...types) {
1900      for (var cc : types)
1901         if (is(cc))
1902            return true;
1903      return false;
1904   }
1905
1906   /**
1907    * Returns <jk>true</jk> if this class is any of the specified types.
1908    *
1909    * <p>
1910    * This is a high-performance overload that avoids array creation and iteration.
1911    * Use this method instead of {@link #isAny(Class...)} when checking against exactly 2 types.
1912    *
1913    * @param t1 The first type to check against.
1914    * @param t2 The second type to check against.
1915    * @return <jk>true</jk> if this class is any of the specified types.
1916    */
1917   public boolean isAny(Class<?> t1, Class<?> t2) {
1918      return inner == t1 || inner == t2;
1919   }
1920
1921   /**
1922    * Returns <jk>true</jk> if this class is any of the specified types.
1923    *
1924    * <p>
1925    * This is a high-performance overload that avoids array creation and iteration.
1926    * Use this method instead of {@link #isAny(Class...)} when checking against exactly 3 types.
1927    *
1928    * @param t1 The first type to check against.
1929    * @param t2 The second type to check against.
1930    * @param t3 The third type to check against.
1931    * @return <jk>true</jk> if this class is any of the specified types.
1932    */
1933   public boolean isAny(Class<?> t1, Class<?> t2, Class<?> t3) {
1934      return inner == t1 || inner == t2 || inner == t3;
1935   }
1936
1937   /**
1938    * Returns <jk>true</jk> if this class is any of the specified types.
1939    *
1940    * <p>
1941    * This is a high-performance overload that avoids array creation and iteration.
1942    * Use this method instead of {@link #isAny(Class...)} when checking against exactly 4 types.
1943    *
1944    * @param t1 The first type to check against.
1945    * @param t2 The second type to check against.
1946    * @param t3 The third type to check against.
1947    * @param t4 The fourth type to check against.
1948    * @return <jk>true</jk> if this class is any of the specified types.
1949    */
1950   public boolean isAny(Class<?> t1, Class<?> t2, Class<?> t3, Class<?> t4) {
1951      return inner == t1 || inner == t2 || inner == t3 || inner == t4;
1952   }
1953
1954   /**
1955    * Returns <jk>true</jk> if this class is any of the specified types.
1956    *
1957    * <p>
1958    * This is a high-performance overload that avoids array creation and iteration.
1959    * Use this method instead of {@link #isAny(Class...)} when checking against exactly 5 types.
1960    *
1961    * @param t1 The first type to check against.
1962    * @param t2 The second type to check against.
1963    * @param t3 The third type to check against.
1964    * @param t4 The fourth type to check against.
1965    * @param t5 The fifth type to check against.
1966    * @return <jk>true</jk> if this class is any of the specified types.
1967    */
1968   public boolean isAny(Class<?> t1, Class<?> t2, Class<?> t3, Class<?> t4, Class<?> t5) {
1969      return inner == t1 || inner == t2 || inner == t3 || inner == t4 || inner == t5;
1970   }
1971
1972   /**
1973    * Returns <jk>true</jk> if this class is an array.
1974    *
1975    * @return <jk>true</jk> if this class is an array.
1976    */
1977   public boolean isArray() { return nn(inner) && inner.isArray(); }
1978
1979   /**
1980    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1981    *
1982    * @param parent The parent class.
1983    * @return <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1984    */
1985   public boolean isChildOf(Class<?> parent) {
1986      return and(nn(inner), nn(parent)) && parent.isAssignableFrom(inner);
1987   }
1988
1989   /**
1990    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1991    *
1992    * @param parent The parent class.
1993    * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>.
1994    */
1995   public boolean isChildOf(ClassInfo parent) {
1996      return isChildOf(parent.inner());
1997   }
1998
1999   /**
2000    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
2001    *
2002    * @param parent The parent class.
2003    * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>.
2004    */
2005   public boolean isChildOf(Type parent) {
2006      if (parent instanceof Class<?> c)
2007         return isChildOf(c);
2008      return false;
2009   }
2010
2011   /**
2012    * Returns <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>.
2013    *
2014    * @param parents The parents class.
2015    * @return <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>.
2016    */
2017   public boolean isChildOfAny(Class<?>...parents) {
2018      for (var p : parents)
2019         if (isChildOf(p))
2020            return true;
2021      return false;
2022   }
2023
2024   /**
2025    * Returns <jk>true</jk> if this class is not an interface.
2026    *
2027    * @return <jk>true</jk> if this class is not an interface.
2028    */
2029   public boolean isClass() { return nn(inner) && ! inner.isInterface(); }
2030
2031   /**
2032    * Returns <jk>true</jk> if this class is a {@link Collection} or an array.
2033    *
2034    * @return <jk>true</jk> if this class is a {@link Collection} or an array.
2035    */
2036   public boolean isCollectionOrArray() { return nn(inner) && (Collection.class.isAssignableFrom(inner) || inner.isArray()); }
2037
2038   /**
2039    * Returns <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it.
2040    *
2041    * @return <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it.
2042    */
2043   public boolean isDeprecated() { return nn(inner) && inner.isAnnotationPresent(Deprecated.class); }
2044
2045   /**
2046    * Returns <jk>true</jk> if this class is an enum.
2047    *
2048    * @return <jk>true</jk> if this class is an enum.
2049    */
2050   public boolean isEnum() { return nn(inner) && inner.isEnum(); }
2051
2052   /**
2053    * Returns <jk>true</jk> if the specified value is an instance of this class.
2054    *
2055    * @param value The value to check.
2056    * @return <jk>true</jk> if the specified value is an instance of this class.
2057    */
2058   public boolean isInstance(Object value) {
2059      if (nn(this.inner))
2060         return inner.isInstance(value);
2061      return false;
2062   }
2063
2064   /**
2065    * Returns <jk>true</jk> if this class is a local class.
2066    *
2067    * @return <jk>true</jk> if this class is a local class.
2068    */
2069   public boolean isLocalClass() { return nn(inner) && inner.isLocalClass(); }
2070
2071   /**
2072    * Returns <jk>true</jk> if this class is a member class.
2073    *
2074    * @return <jk>true</jk> if this class is a member class.
2075    */
2076   public boolean isMemberClass() { return nn(inner) && inner.isMemberClass(); }
2077
2078   /**
2079    * Determines if this class and the specified class are nestmates.
2080    *
2081    * <p>
2082    * Two classes are nestmates if they belong to the same nest.
2083    *
2084    * @param c The class to check.
2085    * @return <jk>true</jk> if this class and the specified class are nestmates.
2086    */
2087   public boolean isNestmateOf(Class<?> c) {
2088      return nn(this.inner) && nn(c) && this.inner.isNestmateOf(c);
2089   }
2090
2091   /**
2092    * Returns <jk>true</jk> if this class is a member class and not static.
2093    *
2094    * @return <jk>true</jk> if this class is a member class and not static.
2095    */
2096   public boolean isNonStaticMemberClass() { return nn(inner) && inner.isMemberClass() && ! isStatic(); }
2097
2098   /**
2099    * Returns <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it.
2100    *
2101    * @return <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it.
2102    */
2103   public boolean isNotDeprecated() { return inner == null || ! inner.isAnnotationPresent(Deprecated.class); }
2104
2105   /**
2106    * Returns <jk>true</jk> if this class is a local class.
2107    *
2108    * @return <jk>true</jk> if this class is a local class.
2109    */
2110   public boolean isNotLocalClass() { return inner == null || ! inner.isLocalClass(); }
2111
2112   /**
2113    * Returns <jk>true</jk> if this class is a member class.
2114    *
2115    * @return <jk>true</jk> if this class is a member class.
2116    */
2117   public boolean isNotMemberClass() { return inner == null || ! inner.isMemberClass(); }
2118
2119   /**
2120    * Returns <jk>false</jk> if this class is a member class and not static.
2121    *
2122    * @return <jk>false</jk> if this class is a member class and not static.
2123    */
2124   public boolean isNotNonStaticMemberClass() { return ! isNonStaticMemberClass(); }
2125
2126   /**
2127    * Returns <jk>true</jk> if this is not a primitive class.
2128    *
2129    * @return <jk>true</jk> if this is not a primitive class.
2130    */
2131   public boolean isNotPrimitive() { return inner == null || ! inner.isPrimitive(); }
2132
2133   /**
2134    * Returns <jk>true</jk> if this class is <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>.
2135    *
2136    * @return <jk>true</jk> if this class is <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>.
2137    */
2138   public boolean isVoid() {
2139      return inner != null && (inner == void.class || inner == Void.class || inner.getSimpleName().equalsIgnoreCase("void"));
2140   }
2141
2142   /**
2143    * Returns <jk>true</jk> if this class is not <c><jk>void</jk>.<jk>class</jk></c> and not {@link Void} and does not have the simple name <js>"Void</js>.
2144    *
2145    * @return <jk>true</jk> if this class is not <c><jk>void</jk>.<jk>class</jk></c> and not {@link Void} and does not have the simple name <js>"Void</js>.
2146    */
2147   public boolean isNotVoid() { return ! isVoid(); }
2148
2149   /**
2150    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2151    *
2152    * @param child The child class.
2153    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2154    */
2155   public boolean isParentOf(Class<?> child) {
2156      return nn(inner) && nn(child) && inner.isAssignableFrom(child);
2157   }
2158
2159   /**
2160    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2161    *
2162    * <p>
2163    * Same as {@link #isParentOf(Class)} but takes in a {@link ClassInfo}.
2164    *
2165    * <h5 class='section'>Example:</h5>
2166    * <p class='bjava'>
2167    *    ClassInfo <jv>parent</jv> = ClassInfo.<jsm>of</jsm>(List.<jk>class</jk>);
2168    *    ClassInfo <jv>child</jv> = ClassInfo.<jsm>of</jsm>(ArrayList.<jk>class</jk>);
2169    *    <jk>boolean</jk> <jv>b</jv> = <jv>parent</jv>.isParentOf(<jv>child</jv>);  <jc>// true</jc>
2170    * </p>
2171    *
2172    * @param child The child class.
2173    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2174    */
2175   public boolean isParentOf(ClassInfo child) {
2176      return nn(inner) && nn(child) && inner.isAssignableFrom(child.inner());
2177   }
2178
2179   /**
2180    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2181    *
2182    * @param child The child class.
2183    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2184    */
2185   public boolean isParentOf(Type child) {
2186      if (child instanceof Class<?> c)
2187         return isParentOf(c);
2188      return false;
2189   }
2190
2191   /**
2192    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2193    *
2194    * <p>
2195    * Primitive classes are converted to wrapper classes and compared.
2196    *
2197    * <h5 class='section'>Examples:</h5>
2198    * <p class='bjava'>
2199    *       ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfLenient(String.<jk>class</jk>);  <jc>// true</jc>
2200    *       ClassInfo.<jsm>of</jsm>(CharSequence.<jk>class</jk>).isParentOfLenient(String.<jk>class</jk>);  <jc>// true</jc>
2201    *       ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfLenient(CharSequence.<jk>class</jk>);  <jc>// false</jc>
2202    *       ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(Integer.<jk>class</jk>);  <jc>// true</jc>
2203    *       ClassInfo.<jsm>of</jsm>(Integer.<jk>class</jk>).isParentOfLenient(<jk>int</jk>.<jk>class</jk>);  <jc>// true</jc>
2204    *       ClassInfo.<jsm>of</jsm>(Number.<jk>class</jk>).isParentOfLenient(<jk>int</jk>.<jk>class</jk>);  <jc>// true</jc>
2205    *       ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(Number.<jk>class</jk>);  <jc>// false</jc>
2206    *       ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfLenient(<jk>long</jk>.<jk>class</jk>);  <jc>// false</jc>
2207    * </p>
2208    *
2209    * @param child The child class.
2210    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2211    */
2212   public boolean isParentOfLenient(Class<?> child) {
2213      if (inner == null || child == null)
2214         return false;
2215      if (inner.isAssignableFrom(child))
2216         return true;
2217      if (this.isPrimitive() || child.isPrimitive()) {
2218         return this.getWrapperIfPrimitive().isParentOf(of(child).getWrapperIfPrimitive());
2219      }
2220      return false;
2221   }
2222
2223   /**
2224    * Same as {@link #isParentOfLenient(Class)} but takes in a {@link ClassInfo}.
2225    *
2226    * @param child The child class.
2227    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2228    */
2229   public boolean isParentOfLenient(ClassInfo child) {
2230      if (inner == null || child == null)
2231         return false;
2232      if (inner.isAssignableFrom(child.inner()))
2233         return true;
2234      // If at least one is a primitive, convert both to wrappers and compare
2235      // Note: If both are primitives and the same type, we would have returned true above
2236      // So this branch is only reached when at least one is a primitive and they're not directly assignable
2237      if (this.isPrimitive() || child.isPrimitive()) {
2238         return this.getWrapperIfPrimitive().isParentOf(child.getWrapperIfPrimitive());
2239      }
2240      return false;
2241   }
2242
2243   /**
2244    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2245    *
2246    * @param child The child class.
2247    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
2248    */
2249   public boolean isParentOfLenient(Type child) {
2250      if (child instanceof Class<?> c)
2251         return isParentOfLenient(c);
2252      return false;
2253   }
2254
2255   /**
2256    * Returns <jk>true</jk> if this is a primitive class.
2257    *
2258    * @return <jk>true</jk> if this is a primitive class.
2259    */
2260   public boolean isPrimitive() { return nn(inner) && inner.isPrimitive(); }
2261
2262   /**
2263    * Returns <jk>true</jk> if this class is a record class.
2264    *
2265    * <p>
2266    * A record class is a final class that extends {@link java.lang.Record}.
2267    *
2268    * @return <jk>true</jk> if this class is a record class.
2269    */
2270   public boolean isRecord() { return nn(inner) && inner.isRecord(); }
2271
2272   /**
2273    * Returns <jk>true</jk> if this is a repeated annotation class.
2274    *
2275    * <p>
2276    * A repeated annotation has a single <code>value()</code> method that returns an array
2277    * of annotations who themselves are marked with the {@link Repeatable @Repeatable} annotation
2278    * of this class.
2279    *
2280    * @return <jk>true</jk> if this is a repeated annotation class.
2281    */
2282   public boolean isRepeatedAnnotation() { return getRepeatedAnnotationMethod() != null; }
2283
2284   /**
2285    * Returns <jk>true</jk> if this class is a {@link RuntimeException}.
2286    *
2287    * @return <jk>true</jk> if this class is a {@link RuntimeException}.
2288    */
2289   public boolean isRuntimeException() { return isChildOf(RuntimeException.class); }
2290
2291   /**
2292    * Returns <jk>true</jk> if this class is a sealed class.
2293    *
2294    * <p>
2295    * A sealed class is a class that can only be extended by a permitted set of subclasses.
2296    *
2297    * @return <jk>true</jk> if this class is a sealed class.
2298    */
2299   public boolean isSealed() { return nn(inner) && inner.isSealed(); }
2300
2301   /**
2302    * Returns <jk>true</jk> if this class is a child of <c>parent</c>.
2303    *
2304    * @param parent The parent class.
2305    * @return <jk>true</jk> if this class is a parent of <c>child</c>.
2306    */
2307   public boolean isStrictChildOf(Class<?> parent) {
2308      return nn(inner) && nn(parent) && parent.isAssignableFrom(inner) && ! inner.equals(parent);
2309   }
2310
2311   /**
2312    * Returns <jk>true</jk> if this class is a synthetic class.
2313    *
2314    * <p>
2315    * A synthetic class is one that is generated by the compiler and does not appear in source code.
2316    *
2317    * @return <jk>true</jk> if this class is synthetic.
2318    */
2319   public boolean isSynthetic() { return nn(inner) && inner.isSynthetic(); }  // HTT
2320
2321   /**
2322    * Identifies if the specified visibility matches this constructor.
2323    *
2324    * @param v The visibility to validate against.
2325    * @return <jk>true</jk> if this visibility matches the modifier attribute of this constructor.
2326    */
2327   public boolean isVisible(Visibility v) {
2328      return nn(inner) && v.isVisible(inner);
2329   }
2330
2331   /**
2332    * Shortcut for calling <c>Class.getDeclaredConstructor().newInstance()</c> on the underlying class.
2333    *
2334    * @return A new instance of the underlying class
2335    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
2336    */
2337   public Object newInstance() throws ExecutableException {
2338      if (inner == null)
2339         throw exex("Type ''{0}'' cannot be instantiated", getNameFull());
2340      try {
2341         return inner.getDeclaredConstructor().newInstance();
2342      } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
2343         throw exex(e);
2344      }
2345   }
2346
2347   @Override
2348   public String toString() {
2349      return innerType.toString();
2350   }
2351
2352   /**
2353    * Unwrap this class if it's a parameterized type of the specified type such as {@link Value} or {@link Optional}.
2354    *
2355    * @param wrapperTypes The parameterized types to unwrap if this class is one of those types.
2356    * @return The class info on the unwrapped type, or just this type if this isn't one of the specified types.
2357    */
2358   public ClassInfo unwrap(Class<?>...wrapperTypes) {
2359      for (var wt : wrapperTypes) {
2360         if (isParameterizedTypeOf(wt)) {
2361            Type t = getFirstParameterType(wt);
2362            if (nn(t))
2363               return of(t).unwrap(wrapperTypes); // Recursively do it again.
2364         }
2365      }
2366      return this;
2367   }
2368
2369   /**
2370    * Helper method to recursively add an interface and its parent interfaces to the set.
2371    *
2372    * @param set The set to add to.
2373    * @param iface The interface to add.
2374    */
2375   private void addInterfaceHierarchy(LinkedHashSet<ClassInfo> set, ClassInfo iface) {
2376      if (! set.add(iface))
2377         return;
2378
2379      // Process parent interfaces recursively
2380      var parentInterfaces = iface.getDeclaredInterfaces();
2381      for (var i = 0; i < parentInterfaces.size(); i++)
2382         addInterfaceHierarchy(set, parentInterfaces.get(i));
2383   }
2384
2385   /**
2386    * Finds all annotations on this class and parent classes/interfaces in child-to-parent order.
2387    *
2388    * <p>
2389    * This is similar to {@link org.apache.juneau.commons.reflect.AnnotationProvider#xfind(Class)} but without runtime annotations.
2390    *
2391    * <p>
2392    * Order of traversal:
2393    * <ol>
2394    *    <li>Annotations declared on this class
2395    *    <li>Annotations declared on parent classes (child-to-parent order)
2396    *    <li>For each parent class, annotations on interfaces declared on that class (child-to-parent interface hierarchy)
2397    *    <li>Annotations on the package of this class
2398    * </ol>
2399    *
2400    * @return A list of all annotation infos in child-to-parent order.
2401    */
2402   private List<AnnotationInfo<? extends Annotation>> findAnnotations() {
2403      var list = new ArrayList<AnnotationInfo<Annotation>>();
2404
2405      // On all parent classes and interfaces (properly traversed to avoid duplicates)
2406      var parentsAndInterfaces = getParentsAndInterfaces();
2407      for (var i = 0; i < parentsAndInterfaces.size(); i++) {
2408         var ci = parentsAndInterfaces.get(i);
2409         // Add declared annotations from this class/interface
2410         for (var a : ci.inner().getDeclaredAnnotations())
2411            streamRepeated(a).forEach(a2 -> list.add(ai(ci, a2)));
2412      }
2413
2414      // On the package of this class
2415      var pkg = getPackage();
2416      if (nn(pkg)) {
2417         var pi = PackageInfo.of(pkg.inner());
2418         for (var a : pkg.inner().getAnnotations())
2419            streamRepeated(a).forEach(a2 -> list.add(ai(pi, a2)));
2420      }
2421
2422      return u(list);
2423   }
2424
2425   private ClassInfo findComponentType() {
2426      Type ct = innerType;
2427      var cc = inner;
2428
2429      // Handle GenericArrayType (e.g., List<String>[])
2430      while (ct instanceof GenericArrayType ct2) {
2431         ct = ct2.getGenericComponentType();
2432      }
2433
2434      // Handle regular arrays
2435      while (nn(cc) && cc.isArray()) {
2436         cc = cc.getComponentType();
2437      }
2438
2439      // Return the deepest component type found
2440      if (ct != innerType) {
2441         return of(ct);
2442      } else if (cc != inner) {
2443         return of(cc);
2444      } else {
2445         return this;
2446      }
2447   }
2448
2449   private int findDimensions() {
2450      int d = 0;
2451      Type ct = innerType;
2452
2453      // Handle GenericArrayType (e.g., List<String>[])
2454      while (ct instanceof GenericArrayType ct2) {
2455         d++;
2456         ct = ct2.getGenericComponentType();
2457      }
2458
2459      // Handle regular arrays
2460      var cc = inner;
2461      while (nn(cc) && cc.isArray()) {
2462         d++;
2463         cc = cc.getComponentType();
2464      }
2465
2466      return d;
2467   }
2468
2469   private List<ClassInfo> findParents() {
2470      List<ClassInfo> l = list();
2471      var pc = inner;
2472      while (nn(pc) && pc != Object.class) {
2473         l.add(of(pc));
2474         pc = pc.getSuperclass();
2475      }
2476      return u(l);
2477   }
2478
2479   /**
2480    * Finds all parent classes and interfaces with proper traversal of interface hierarchy.
2481    *
2482    * @return A list of all parent classes and interfaces without duplicates.
2483    */
2484   private List<ClassInfo> findParentsAndInterfaces() {
2485      var set = new LinkedHashSet<ClassInfo>();
2486
2487      // Process all parent classes (includes this class)
2488      var parents = getParents();
2489      for (var i = 0; i < parents.size(); i++) {
2490         var parent = parents.get(i);
2491         set.add(parent);
2492
2493         // Process interfaces declared on this parent (and their parent interfaces)
2494         var declaredInterfaces = parent.getDeclaredInterfaces();
2495         for (var j = 0; j < declaredInterfaces.size(); j++)
2496            addInterfaceHierarchy(set, declaredInterfaces.get(j));
2497      }
2498
2499      return u(toList(set));
2500   }
2501
2502   private MethodInfo findRepeatedAnnotationMethod() {
2503      // @formatter:off
2504      return getPublicMethods().stream()
2505         .filter(m -> m.hasName("value"))
2506         .filter(m -> m.getReturnType().isArray())
2507         .filter(m -> {
2508            var rct = m.getReturnType().getComponentType();
2509            if (rct.hasAnnotation(Repeatable.class)) {
2510               return rct.getAnnotations(Repeatable.class).findFirst().map(AnnotationInfo::inner).orElse(null).value().equals(inner);
2511            }
2512            return false;
2513         })
2514         .findFirst()
2515         .orElse(null);
2516      // @formatter:on
2517   }
2518
2519   //-----------------------------------------------------------------------------------------------------------------
2520   // Find methods
2521   //-----------------------------------------------------------------------------------------------------------------
2522
2523   /**
2524    * Returns the first type parameter of this type if it's parameterized (e.g., T in Optional&lt;T&gt;).
2525    *
2526    * @param parameterizedType The expected parameterized class (e.g., Optional.class).
2527    * @return The first type parameter, or <jk>null</jk> if not parameterized or no parameters exist.
2528    */
2529   private Type getFirstParameterType(Class<?> parameterizedType) {
2530      if (innerType instanceof ParameterizedType innerType2) {
2531         var ta = innerType2.getActualTypeArguments();
2532         if (ta.length > 0)
2533            return ta[0];
2534      } else if (innerType instanceof Class<?> innerType3) /* Class that extends Optional<T> */ {
2535         if (innerType3 != parameterizedType && parameterizedType.isAssignableFrom(innerType3))
2536            return ClassInfo.of(innerType3).getParameterType(0, parameterizedType);
2537      }
2538      return null;
2539   }
2540
2541   /**
2542    * Returns <jk>true</jk> if this type is a parameterized type of the specified class or a subclass of it.
2543    *
2544    * @param c The class to check against (e.g., Optional.class, List.class).
2545    * @return <jk>true</jk> if this is Optional&lt;T&gt; or a subclass like MyOptional extends Optional&lt;String&gt;.
2546    */
2547   private boolean isParameterizedTypeOf(Class<?> c) {
2548      return (innerType instanceof ParameterizedType t2 && t2.getRawType() == c) || (innerType instanceof Class innerType2 && c.isAssignableFrom(innerType2));
2549   }
2550
2551   /**
2552    * Returns a {@link ConstructorInfo} wrapper for the specified raw {@link Constructor} object.
2553    *
2554    * <p>
2555    * This is an internal method used to wrap raw reflection objects into their cached Info wrappers.
2556    * The wrappers provide additional functionality and are cached to avoid creating duplicate objects.
2557    *
2558    * <p>
2559    * This method is called internally when building the lists of constructors (public, declared, etc.)
2560    * and ensures that the same {@link Constructor} object always maps to the same {@link ConstructorInfo} instance.
2561    *
2562    * @param x The raw constructor to wrap.
2563    * @return The cached {@link ConstructorInfo} wrapper for this constructor.
2564    */
2565   ConstructorInfo getConstructor(Constructor<?> x) {
2566      return constructorCache.get(x, () -> new ConstructorInfo(this, x));
2567   }
2568
2569   //-----------------------------------------------------------------------------------------------------------------
2570   // Annotatable interface methods
2571   //-----------------------------------------------------------------------------------------------------------------
2572
2573   /**
2574    * Returns a {@link FieldInfo} wrapper for the specified raw {@link Field} object.
2575    *
2576    * <p>
2577    * This is an internal method used to wrap raw reflection objects into their cached Info wrappers.
2578    * The wrappers provide additional functionality and are cached to avoid creating duplicate objects.
2579    *
2580    * <p>
2581    * This method is called internally when building the lists of fields (public, declared, all, etc.)
2582    * and ensures that the same {@link Field} object always maps to the same {@link FieldInfo} instance.
2583    *
2584    * @param x The raw field to wrap.
2585    * @return The cached {@link FieldInfo} wrapper for this field.
2586    */
2587   FieldInfo getField(Field x) {
2588      return fieldCache.get(x, () -> new FieldInfo(this, x));
2589   }
2590
2591   /**
2592    * Returns a {@link MethodInfo} wrapper for the specified raw {@link Method} object.
2593    *
2594    * <p>
2595    * This is an internal method used to wrap raw reflection objects into their cached Info wrappers.
2596    * The wrappers provide additional functionality and are cached to avoid creating duplicate objects.
2597    *
2598    * <p>
2599    * This method is called internally when building the lists of methods (public, declared, all, etc.)
2600    * and ensures that the same {@link Method} object always maps to the same {@link MethodInfo} instance.
2601    *
2602    * @param x The raw method to wrap.
2603    * @return The cached {@link MethodInfo} wrapper for this method.
2604    */
2605   MethodInfo getMethod(Method x) {
2606      return methodCache.get(x, () -> new MethodInfo(this, x));
2607   }
2608}