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