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