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