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