001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.reflect; 014 015import static org.apache.juneau.internal.StringUtils.*; 016import static org.apache.juneau.reflect.ReflectFlags.*; 017import static org.apache.juneau.internal.CollectionUtils.*; 018 019import java.lang.annotation.*; 020import java.lang.reflect.*; 021import java.util.*; 022import java.util.concurrent.*; 023import java.util.function.*; 024 025import org.apache.juneau.*; 026import org.apache.juneau.annotation.*; 027import org.apache.juneau.internal.*; 028import org.apache.juneau.utils.*; 029 030/** 031 * Lightweight utility class for introspecting information about a class. 032 * 033 * <p> 034 * Provides various convenience methods for introspecting fields/methods/annotations 035 * that aren't provided by the standard Java reflection APIs. 036 * 037 * <p> 038 * Objects are designed to be lightweight to create and threadsafe. 039 * 040 * <h5 class='figure'>Example:</h5> 041 * <p class='bpcode w800'> 042 * <jc>// Wrap our class inside a ClassInfo.</jc> 043 * ClassInfo ci = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>); 044 * 045 * <jc>// Get all methods in parent-to-child order, sorted alphabetically per class.</jc> 046 * <jk>for</jk> (MethodInfo mi : ci.getAllMethodInfos(<jk>true</jk>, <jk>true</jk>)) { 047 * <jc>// Do something with it.</jc> 048 * } 049 * 050 * <jc>// Get all class-level annotations in parent-to-child order.</jc> 051 * <jk>for</jk> (MyAnnotation a : ci.getAnnotations(MyAnnotation.<jk>class</jk>, <jk>true</jk>)) { 052 * // Do something with it. 053 * } 054 * </p> 055 */ 056@BeanIgnore 057public final class ClassInfo { 058 059 private final Type t; 060 final Class<?> c; 061 private ClassInfo proxyFor; 062 private final boolean isParameterizedType; 063 private List<ClassInfo> interfaces, declaredInterfaces, parents, allParents; 064 private List<MethodInfo> publicMethods, declaredMethods, allMethods, allMethodsParentFirst; 065 private List<ConstructorInfo> publicConstructors, declaredConstructors; 066 private List<FieldInfo> publicFields, declaredFields, allFields, allFieldsParentFirst; 067 private Map<Class<?>,Optional<Annotation>> annotationMap, declaredAnnotationMap; 068 private int dim = -1; 069 private ClassInfo componentType; 070 071 private static final Map<Class<?>,ClassInfo> CACHE = new ConcurrentHashMap<>(); 072 073 //----------------------------------------------------------------------------------------------------------------- 074 // Instantiation. 075 //----------------------------------------------------------------------------------------------------------------- 076 077 /** 078 * Constructor. 079 * 080 * @param c The class type. 081 * @param t The generic type (if parameterized type). 082 * @param proxyFor If the class is a CGLIB proxy, this is the underlying wrapped class. 083 */ 084 protected ClassInfo(Class<?> c, Type t, Class<?> proxyFor) { 085 this.t = t; 086 this.c = c; 087 this.proxyFor = proxyFor == null ? null : ClassInfo.of(proxyFor); 088 this.isParameterizedType = t == null ? false : (t instanceof ParameterizedType); 089 } 090 091 /** 092 * Returns a class info wrapper around the specified class type. 093 * 094 * @param t The class type. 095 * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. 096 */ 097 public static ClassInfo of(Type t) { 098 if (t == null) 099 return null; 100 return new ClassInfo(ClassUtils.toClass(t), t, null); 101 } 102 103 /** 104 * Returns a class info wrapper around the specified class type. 105 * 106 * @param c The class type. 107 * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. 108 */ 109 public static ClassInfo of(Class<?> c) { 110 if (c == null) 111 return null; 112 return new ClassInfo(c, c, null); 113 } 114 115 /** 116 * Same as {@link #of(Class)}} but caches the result for faster future lookup. 117 * 118 * @param c The class type. 119 * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. 120 */ 121 public static ClassInfo ofc(Class<?> c) { 122 if (c == null) 123 return null; 124 ClassInfo ci = CACHE.get(c); 125 if (ci == null) { 126 ci = ClassInfo.of(c); 127 CACHE.put(c, ci); 128 } 129 return ci; 130 } 131 132 /** 133 * Returns a class info wrapper around the specified class type. 134 * 135 * @param c The class type. 136 * @param t The generic type (if parameterized type). 137 * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. 138 */ 139 public static ClassInfo of(Class<?> c, Type t) { 140 return new ClassInfo(c, t, null); 141 } 142 143 /** 144 * Same as using the constructor, but operates on an object instance. 145 * 146 * @param o The class instance. 147 * @return The constructed class info, or <jk>null</jk> if the object was <jk>null</jk>. 148 */ 149 public static ClassInfo of(Object o) { 150 if (o == null) 151 return null; 152 return new ClassInfo(o.getClass(), o.getClass(), getProxyFor(o)); 153 } 154 155 /** 156 * Same as {@link #of(Object)}} but caches the result for faster future lookup. 157 * 158 * @param o The class instance. 159 * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. 160 */ 161 public static ClassInfo ofc(Object o) { 162 if (o == null) 163 return null; 164 Class<?> c = o.getClass(); 165 ClassInfo ci = CACHE.get(c); 166 if (ci == null) { 167 ci = ClassInfo.of(o); 168 CACHE.put(c, ci); 169 } 170 return ci; 171 } 172 173 /** 174 * When this metadata is against a CGLIB proxy, this method finds the underlying "real" class. 175 * 176 * @param o The class instance. 177 * @return The non-proxy class, or <jk>null</jk> if it's not a CGLIB proxy. 178 */ 179 private static Class<?> getProxyFor(Object o) { 180 Class<?> c = o.getClass(); 181 String s = c.getName(); 182 if (s.indexOf('$') == -1 || ! s.contains("$$EnhancerBySpringCGLIB$$")) 183 return null; 184 for (Method m : c.getMethods()) { 185 if (m.getName().equals("getTargetClass") && m.getParameterCount() == 0 && m.getReturnType().equals(Class.class)) { 186 try { 187 return (Class<?>) m.invoke(o); 188 } catch (Exception e) {} 189 } 190 } 191 return null; 192 } 193 194 /** 195 * Returns the wrapped class as a {@link Type}. 196 * 197 * @return The wrapped class as a {@link Type}. 198 */ 199 public Type innerType() { 200 return t; 201 } 202 203 /** 204 * Returns the wrapped class as a {@link Class}. 205 * 206 * @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}). 207 */ 208 @SuppressWarnings("unchecked") 209 public <T> Class<T> inner() { 210 return (Class<T>)c; 211 } 212 213 /** 214 * If this class is a parameterized {@link Value} type, returns the parameterized type. 215 * 216 * @return The parameterized type, or this object if this class is not a parameterized {@link Value} type. 217 */ 218 public ClassInfo resolved() { 219 if (Value.isType(t)) 220 return of(Value.getParameterType(t)); 221 return this; 222 } 223 224 /** 225 * Identifies the inner target class when this class info represents a CGLIB proxy class. 226 * 227 * @param proxyFor The inner non-proxied class. 228 * @return This object (for method chaining). 229 */ 230 public ClassInfo proxyFor(Class<?> proxyFor) { 231 this.proxyFor = ClassInfo.of(proxyFor); 232 return this; 233 } 234 235 /** 236 * Returns the non-proxied inner class of a CGLIB proxy class. 237 * 238 * @return The non-proxied inner class of a CGLIB proxy class, or the inner class if it's not a proxy. 239 */ 240 public Class<?> getProxiedClass() { 241 return proxyFor == null ? c : proxyFor.inner(); 242 } 243 244 //----------------------------------------------------------------------------------------------------------------- 245 // Parent classes and interfaces. 246 //----------------------------------------------------------------------------------------------------------------- 247 248 /** 249 * Returns the parent class. 250 * 251 * @return 252 * The parent class, or <jk>null</jk> if the class has no parent. 253 */ 254 public ClassInfo getParent() { 255 return c == null ? null : of(c.getSuperclass()); 256 } 257 258 /** 259 * Returns a list of interfaces declared on this class. 260 * 261 * <p> 262 * Does not include interfaces declared on parent classes. 263 * 264 * @return 265 * An unmodifiable list of interfaces declared on this class. 266 * <br>Results are in the same order as {@link Class#getInterfaces()}. 267 */ 268 public List<ClassInfo> getDeclaredInterfaces() { 269 if (declaredInterfaces == null) { 270 Class<?>[] ii = c == null ? new Class[0] : c.getInterfaces(); 271 List<ClassInfo> l = new ArrayList<>(ii.length); 272 for (Class<?> i : ii) 273 l.add(of(i)); 274 declaredInterfaces = unmodifiableList(l); 275 } 276 return declaredInterfaces; 277 } 278 279 /** 280 * Returns a list of interfaces defined on this class and superclasses. 281 * 282 * @return 283 * An unmodifiable list of interfaces defined on this class and superclasses. 284 * <br>Results are in child-to-parent order. 285 */ 286 public List<ClassInfo> getInterfaces() { 287 if (interfaces == null) { 288 Set<ClassInfo> s = new LinkedHashSet<>(); 289 for (ClassInfo ci : getParents()) 290 for (ClassInfo ci2 : ci.getDeclaredInterfaces()) { 291 s.add(ci2); 292 for (ClassInfo ci3 : ci2.getInterfaces()) 293 s.add(ci3); 294 } 295 interfaces = unmodifiableList(new ArrayList<>(s)); 296 } 297 return interfaces; 298 } 299 300 /** 301 * Returns a list of interfaces defined on this class and superclasses. 302 * 303 * @return 304 * An unmodifiable list of interfaces defined on this class and superclasses. 305 * <br>Results are in parent-to-child order. 306 */ 307 public Iterable<ClassInfo> getInterfacesParentFirst() { 308 return iterable(getInterfaces(), true); 309 } 310 311 /** 312 * Returns a list including this class and all parent classes. 313 * 314 * <p> 315 * Does not include interfaces. 316 * 317 * @return An unmodifiable list including this class and all parent classes. 318 * <br>Results are in child-to-parent order. 319 */ 320 public List<ClassInfo> getParents() { 321 if (parents == null) { 322 List<ClassInfo> l = new ArrayList<>(); 323 Class<?> pc = c; 324 while (pc != null && pc != Object.class) { 325 l.add(of(pc)); 326 pc = pc.getSuperclass(); 327 } 328 parents = Collections.unmodifiableList(l); 329 } 330 return parents; 331 } 332 333 /** 334 * Returns a list including this class and all parent classes. 335 * 336 * <p> 337 * Does not include interfaces. 338 * 339 * @return An unmodifiable list including this class and all parent classes. 340 * <br>Results are in parent-to-child order. 341 */ 342 public Iterable<ClassInfo> getParentsParentFirst() { 343 return iterable(getParents(), true); 344 } 345 346 /** 347 * Returns a list including this class and all parent classes and interfaces. 348 * 349 * @return An unmodifiable list including this class and all parent classes. 350 * <br>Results are ordered child-to-parent order with classes listed before interfaces. 351 */ 352 public List<ClassInfo> getAllParents() { 353 if (allParents == null) { 354 List<ClassInfo> l = new ArrayList<>(); 355 l.addAll(getParents()); 356 l.addAll(getInterfaces()); 357 allParents = Collections.unmodifiableList(l); 358 } 359 return allParents; 360 } 361 362 /** 363 * Returns a list including this class and all parent classes and interfaces. 364 * 365 * @return An unmodifiable list including this class and all parent classes. 366 * <br>Results are ordered parent-to-child order with interfaces listed before classes. 367 */ 368 public Iterable<ClassInfo> getAllParentsParentFirst() { 369 return iterable(getAllParents(), true); 370 } 371 372 //----------------------------------------------------------------------------------------------------------------- 373 // Methods 374 //----------------------------------------------------------------------------------------------------------------- 375 376 /** 377 * Returns all public methods on this class. 378 * 379 * <p> 380 * Methods defined on the {@link Object} class are excluded from the results. 381 * 382 * @return 383 * All public methods on this class. 384 * <br>Results are ordered alphabetically. 385 */ 386 public List<MethodInfo> getPublicMethods() { 387 if (publicMethods == null) { 388 Method[] mm = c == null ? new Method[0] : c.getMethods(); 389 List<MethodInfo> l = new ArrayList<>(mm.length); 390 for (Method m : mm) 391 if (m.getDeclaringClass() != Object.class) 392 l.add(MethodInfo.of(this, m, getProxyTarget(m))); 393 l.sort(null); 394 publicMethods = Collections.unmodifiableList(l); 395 } 396 return publicMethods; 397 } 398 399 private Method getProxyTarget(Method m) { 400 if (proxyFor != null) { 401 MethodInfo m2 = proxyFor.getMethod(m.getName(), m.getParameterTypes()); 402 if (m2 != null) 403 return m2.inner(); 404 } 405 return m; 406 } 407 408 private Constructor<?> getProxyTarget(Constructor<?> c) { 409 if (proxyFor != null) { 410 ConstructorInfo c2 = proxyFor.getConstructor(Visibility.PRIVATE, c.getParameterTypes()); 411 if (c2 != null) 412 return c2.inner(); 413 } 414 return c; 415 } 416 417 /** 418 * Returns the public method with the specified method name and argument types. 419 * 420 * @param name The method name (e.g. <js>"toString"</js>). 421 * @param args The exact argument types. 422 * @return 423 * The public method with the specified method name and argument types, or <jk>null</jk> if not found. 424 */ 425 public MethodInfo getPublicMethod(String name, Class<?>...args) { 426 for (MethodInfo mi : getPublicMethods()) 427 if (mi.hasName(name) && mi.hasParamTypes(args)) 428 return mi; 429 return null; 430 } 431 432 /** 433 * Returns the method with the specified method name and argument types. 434 * 435 * @param name The method name (e.g. <js>"toString"</js>). 436 * @param args The exact argument types. 437 * @return 438 * The method with the specified method name and argument types, or <jk>null</jk> if not found. 439 */ 440 public MethodInfo getMethod(String name, Class<?>...args) { 441 for (MethodInfo mi : getAllMethods()) 442 if (mi.hasName(name) && mi.hasParamTypes(args)) 443 return mi; 444 return null; 445 } 446 447 /** 448 * Returns all methods declared on this class. 449 * 450 * @return 451 * All methods declared on this class. 452 * <br>Results are ordered alphabetically. 453 */ 454 public List<MethodInfo> getDeclaredMethods() { 455 if (declaredMethods == null) { 456 Method[] mm = c == null ? new Method[0] : c.getDeclaredMethods(); 457 List<MethodInfo> l = new ArrayList<>(mm.length); 458 for (Method m : mm) 459 if (! "$jacocoInit".equals(m.getName())) // Jacoco adds its own simulated methods. 460 l.add(MethodInfo.of(this, m, getProxyTarget(m))); 461 l.sort(null); 462 declaredMethods = Collections.unmodifiableList(l); 463 } 464 return declaredMethods; 465 } 466 467 /** 468 * Returns all declared methods on this class and all parent classes. 469 * 470 * @return 471 * All declared methods on this class and all parent classes. 472 * <br>Results are ordered child-to-parent, and then alphabetically per class. 473 */ 474 public List<MethodInfo> getAllMethods() { 475 if (allMethods == null) { 476 List<MethodInfo> l = new ArrayList<>(); 477 for (ClassInfo c : getAllParents()) 478 c.appendDeclaredMethods(l); 479 allMethods = Collections.unmodifiableList(l); 480 } 481 return allMethods; 482 } 483 484 /** 485 * Returns all declared methods on this class and all parent classes. 486 * 487 * 488 * @return 489 * All declared methods on this class and all parent classes. 490 * <br>Results are ordered parent-to-child, and then alphabetically per class. 491 */ 492 public List<MethodInfo> getAllMethodsParentFirst() { 493 if (allMethodsParentFirst == null) { 494 List<MethodInfo> l = new ArrayList<>(); 495 for (ClassInfo c : getAllParentsParentFirst()) 496 c.appendDeclaredMethods(l); 497 allMethodsParentFirst = Collections.unmodifiableList(l); 498 } 499 return allMethodsParentFirst; 500 } 501 502 private List<MethodInfo> appendDeclaredMethods(List<MethodInfo> l) { 503 l.addAll(getDeclaredMethods()); 504 return l; 505 } 506 507 //----------------------------------------------------------------------------------------------------------------- 508 // Special methods 509 //----------------------------------------------------------------------------------------------------------------- 510 511 /** 512 * Find the public static creator method on this class. 513 * 514 * <p> 515 * Looks for the following method names: 516 * <ul> 517 * <li><c>create</c> 518 * <li><c>from</c> 519 * <li><c>fromValue</c> 520 * <li><c>parse</c> 521 * <li><c>valueOf</c> 522 * <li><c>fromX</c> 523 * <li><c>forX</c> 524 * <li><c>parseX</c> 525 * </ul> 526 * 527 * @param ic The argument type. 528 * @param additionalNames Additional method names to check for. 529 * @return The static method, or <jk>null</jk> if it couldn't be found. 530 */ 531 public MethodInfo getStaticCreateMethod(Class<?> ic, String...additionalNames) { 532 if (c != null) { 533 for (MethodInfo m : getPublicMethods()) { 534 if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && m.hasReturnType(c) && m.hasParamTypes(ic)) { 535 String n = m.getSimpleName(), cn = ic.getSimpleName(); 536 if ( 537 isOneOf(n, "create","from","fromValue","parse","valueOf") 538 || isOneOf(n, additionalNames) 539 || (n.startsWith("from") && n.substring(4).equals(cn)) 540 || (n.startsWith("for") && n.substring(3).equals(cn)) 541 || (n.startsWith("parse") && n.substring(5).equals(cn)) 542 ) { 543 return m; 544 } 545 } 546 } 547 } 548 return null; 549 } 550 551 /** 552 * Find the public static method with the specified name and args. 553 * 554 * @param name The method name. 555 * @param rt The method return type. 556 * @param args The method arguments 557 * @return The method, or <jk>null</jk> if it couldn't be found. 558 */ 559 public MethodInfo getStaticPublicMethod(String name, Class<?> rt, Class<?>...args) { 560 if (c != null) 561 for (MethodInfo m : getPublicMethods()) 562 if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && name.equals(m.getSimpleName()) && m.hasReturnType(rt) && m.hasParamTypes(args)) 563 return m; 564 return null; 565 } 566 567 /** 568 * Find the public static method with the specified name and args. 569 * 570 * @param name The method name. 571 * @param rt The method return type. 572 * @param args The method arguments 573 * @return The method, or <jk>null</jk> if it couldn't be found. 574 */ 575 public Method getStaticPublicMethodInner(String name, Class<?> rt, Class<?>...args) { 576 MethodInfo mi = getStaticPublicMethod(name, rt, args); 577 return mi == null ? null : mi.inner(); 578 } 579 580 /** 581 * Returns the <c>public static Builder create()</c> method on this class. 582 * 583 * @return The <c>public static Builder create()</c> method on this class, or <jk>null</jk> if it doesn't exist. 584 */ 585 public MethodInfo getBuilderCreateMethod() { 586 for (MethodInfo m : getDeclaredMethods()) 587 if (m.isAll(PUBLIC, STATIC) && m.hasName("create") && (!m.hasReturnType(void.class))) 588 return m; 589 return null; 590 } 591 592 /** 593 * Returns the <c>T build()</c> method on this class. 594 * 595 * @return The <c>T build()</c> method on this class, or <jk>null</jk> if it doesn't exist. 596 */ 597 public MethodInfo getBuilderBuildMethod() { 598 for (MethodInfo m : getDeclaredMethods()) 599 if (m.isAll(NOT_STATIC) && m.hasName("build") && (!m.hasParams()) && (!m.hasReturnType(void.class))) 600 return m; 601 return null; 602 } 603 604 //----------------------------------------------------------------------------------------------------------------- 605 // Constructors 606 //----------------------------------------------------------------------------------------------------------------- 607 608 /** 609 * Returns all the public constructors defined on this class. 610 * 611 * @return All public constructors defined on this class. 612 */ 613 public List<ConstructorInfo> getPublicConstructors() { 614 if (publicConstructors == null) { 615 Constructor<?>[] cc = c == null ? new Constructor[0] : c.getConstructors(); 616 List<ConstructorInfo> l = new ArrayList<>(cc.length); 617 for (Constructor<?> ccc : cc) 618 l.add(ConstructorInfo.of(this, ccc, getProxyTarget(ccc))); 619 l.sort(null); 620 publicConstructors = Collections.unmodifiableList(l); 621 } 622 return publicConstructors; 623 } 624 625 /** 626 * Returns the public constructor with the specified argument types. 627 * 628 * @param args The exact argument types. 629 * @return 630 * The public constructor with the specified argument types, or <jk>null</jk> if not found. 631 */ 632 public ConstructorInfo getPublicConstructor(Class<?>...args) { 633 for (ConstructorInfo ci : getPublicConstructors()) 634 if (ci.hasParamTypes(args)) 635 return ci; 636 return null; 637 } 638 639 /** 640 * Returns the declared constructor with the specified argument types. 641 * 642 * @param args The exact argument types. 643 * @return 644 * The declared constructor with the specified argument types, or <jk>null</jk> if not found. 645 */ 646 public ConstructorInfo getDeclaredConstructor(Class<?>...args) { 647 for (ConstructorInfo ci : getDeclaredConstructors()) 648 if (ci.hasParamTypes(args)) 649 return ci; 650 return null; 651 } 652 653 /** 654 * Same as {@link #getPublicConstructor(Class...)} but allows for inexact arg type matching. 655 * 656 * <p> 657 * For example, the method <c>foo(CharSequence)</c> will be matched by <code>getAvailablePublicConstructor(String.<jk>class</jk>)</code> 658 * 659 * @param args The exact argument types. 660 * @return 661 * The public constructor with the specified argument types, or <jk>null</jk> if not found. 662 */ 663 public ConstructorInfo getAvailablePublicConstructor(Class<?>...args) { 664 return getConstructor(Visibility.PUBLIC, false, args); 665 } 666 667 /** 668 * Returns all the constructors defined on this class. 669 * 670 * @return All constructors defined on this class. 671 */ 672 public List<ConstructorInfo> getDeclaredConstructors() { 673 if (declaredConstructors == null) { 674 Constructor<?>[] cc = c == null ? new Constructor[0] : c.getDeclaredConstructors(); 675 List<ConstructorInfo> l = new ArrayList<>(cc.length); 676 for (Constructor<?> ccc : cc) 677 l.add(ConstructorInfo.of(this, ccc, getProxyTarget(ccc))); 678 l.sort(null); 679 declaredConstructors = Collections.unmodifiableList(l); 680 } 681 return declaredConstructors; 682 } 683 684 /** 685 * Finds the public constructor that can take in the specified arguments. 686 * 687 * @param args The arguments we want to pass into the constructor. 688 * @return 689 * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified 690 * arguments. 691 */ 692 public ConstructorInfo getPublicConstructor(Object...args) { 693 return getPublicConstructor(ClassUtils.getClasses(args)); 694 } 695 696 /** 697 * Finds the public constructor that can take in the specified arguments using fuzzy-arg matching. 698 * 699 * @param args The arguments we want to pass into the constructor. 700 * @return 701 * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified 702 * arguments. 703 */ 704 public ConstructorInfo getPublicConstructorFuzzy(Object...args) { 705 return getConstructor(Visibility.PUBLIC, true, ClassUtils.getClasses(args)); 706 } 707 708 /** 709 * Finds a constructor with the specified parameters without throwing an exception. 710 * 711 * @param vis The minimum visibility. 712 * @param argTypes 713 * The argument types in the constructor. 714 * Can be subtypes of the actual constructor argument types. 715 * @return The matching constructor, or <jk>null</jk> if constructor could not be found. 716 */ 717 public ConstructorInfo getConstructor(Visibility vis, Class<?>...argTypes) { 718 return getConstructor(vis, false, argTypes); 719 } 720 721 private ConstructorInfo getConstructor(Visibility vis, boolean fuzzyArgs, Class<?>...argTypes) { 722 if (fuzzyArgs) { 723 int bestCount = -1; 724 ConstructorInfo bestMatch = null; 725 for (ConstructorInfo n : getDeclaredConstructors()) { 726 if (vis.isVisible(n.inner())) { 727 int m = ClassUtils.fuzzyArgsMatch(n.getParamTypes(), argTypes); 728 if (m > bestCount) { 729 bestCount = m; 730 bestMatch = n; 731 } 732 } 733 } 734 return bestMatch; 735 } 736 737 boolean isMemberClass = isNonStaticMemberClass(); 738 for (ConstructorInfo n : getDeclaredConstructors()) { 739 List<ClassInfo> paramTypes = n.getParamTypes(); 740 if (isMemberClass) 741 paramTypes = paramTypes.subList(1, paramTypes.size()); 742 if (ClassUtils.argsMatch(paramTypes, argTypes) && vis.isVisible(n.inner())) 743 return n; 744 } 745 746 return null; 747 } 748 749 //----------------------------------------------------------------------------------------------------------------- 750 // Special constructors 751 //----------------------------------------------------------------------------------------------------------------- 752 753 /** 754 * Locates the no-arg constructor for this class. 755 * 756 * <p> 757 * Constructor must match the visibility requirements specified by parameter 'v'. 758 * If class is abstract, always returns <jk>null</jk>. 759 * Note that this also returns the 1-arg constructor for non-static member classes. 760 * 761 * @param v The minimum visibility. 762 * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility. 763 */ 764 public ConstructorInfo getNoArgConstructor(Visibility v) { 765 if (isAbstract()) 766 return null; 767 boolean isMemberClass = isNonStaticMemberClass(); 768 for (ConstructorInfo cc : getDeclaredConstructors()) 769 if (cc.hasNumParams(isMemberClass ? 1 : 0) && cc.isVisible(v)) 770 return cc.makeAccessible(v); 771 return null; 772 } 773 774 //----------------------------------------------------------------------------------------------------------------- 775 // Fields 776 //----------------------------------------------------------------------------------------------------------------- 777 778 /** 779 * Returns all public fields on this class. 780 * 781 * <p> 782 * Hidden fields are excluded from the results. 783 * 784 * @return 785 * All public fields on this class. 786 * <br>Results are in alphabetical order. 787 */ 788 public List<FieldInfo> getPublicFields() { 789 if (publicFields == null) { 790 Map<String,FieldInfo> m = new LinkedHashMap<>(); 791 for (ClassInfo c : getParents()) 792 c.appendDeclaredPublicFields(m); 793 List<FieldInfo> l = new ArrayList<>(m.values()); 794 l.sort(null); 795 publicFields = Collections.unmodifiableList(l); 796 } 797 return publicFields; 798 } 799 800 /** 801 * Returns all declared fields on this class. 802 * 803 * @return 804 * All declared fields on this class. 805 * <br>Results are in alphabetical order. 806 */ 807 public List<FieldInfo> getDeclaredFields() { 808 if (declaredFields == null) { 809 Field[] ff = c == null ? new Field[0] : c.getDeclaredFields(); 810 List<FieldInfo> l = new ArrayList<>(ff.length); 811 for (Field f : ff) 812 if (! "$jacocoData".equals(f.getName())) 813 l.add(FieldInfo.of(this, f)); 814 l.sort(null); 815 declaredFields = Collections.unmodifiableList(l); 816 } 817 return declaredFields; 818 } 819 820 /** 821 * Returns all declared fields on this class and all parent classes. 822 * 823 * @return 824 * All declared fields on this class. 825 * <br>Results are ordered child-to-parent, and then alphabetical per class. 826 */ 827 public List<FieldInfo> getAllFields() { 828 if (allFields == null) { 829 List<FieldInfo> l = new ArrayList<>(); 830 for (ClassInfo c : getAllParents()) 831 c.appendDeclaredFields(l); 832 allFields = Collections.unmodifiableList(l); 833 } 834 return allFields; 835 } 836 837 /** 838 * Returns all declared fields on this class and all parent classes. 839 * 840 * @return 841 * All declared fields on this class. 842 * <br>Results are ordered parent-to-child, and then alphabetical per class. 843 */ 844 public List<FieldInfo> getAllFieldsParentFirst() { 845 if (allFieldsParentFirst == null) { 846 List<FieldInfo> l = new ArrayList<>(); 847 for (ClassInfo c : getAllParentsParentFirst()) 848 c.appendDeclaredFields(l); 849 allFieldsParentFirst = Collections.unmodifiableList(l); 850 } 851 return allFieldsParentFirst; 852 } 853 854 private List<FieldInfo> appendDeclaredFields(List<FieldInfo> l) { 855 l.addAll(getDeclaredFields()); 856 return l; 857 } 858 859 private Map<String,FieldInfo> appendDeclaredPublicFields(Map<String,FieldInfo> m) { 860 for (FieldInfo f : getDeclaredFields()) { 861 String fn = f.getName(); 862 if (f.isPublic() && ! (m.containsKey(fn) || "$jacocoData".equals(fn))) 863 m.put(f.getName(), f); 864 } 865 return m; 866 } 867 868 /** 869 * Returns the public field with the specified name. 870 * 871 * @param name The field name. 872 * @return The public field, or <jk>null</jk> if not found. 873 */ 874 public FieldInfo getPublicField(String name) { 875 for (FieldInfo f : getPublicFields()) 876 if (f.getName().equals(name)) 877 return f; 878 return null; 879 } 880 881 /** 882 * Returns the declared field with the specified name. 883 * 884 * @param name The field name. 885 * @return The declared field, or <jk>null</jk> if not found. 886 */ 887 public FieldInfo getDeclaredField(String name) { 888 for (FieldInfo f : getDeclaredFields()) 889 if (f.getName().equals(name)) 890 return f; 891 return null; 892 } 893 894 /** 895 * Returns the static public field with the specified name. 896 * 897 * @param name The field name. 898 * @return The public field, or <jk>null</jk> if not found. 899 */ 900 public FieldInfo getStaticPublicField(String name) { 901 for (FieldInfo f : getPublicFields()) 902 if (f.isStatic() && f.getName().equals(name)) 903 return f; 904 return null; 905 } 906 907 /** 908 * Returns the static public field with the specified name. 909 * 910 * @param name The field name. 911 * @return The public field, or <jk>null</jk> if not found. 912 */ 913 public Field getStaticPublicFieldInner(String name) { 914 for (FieldInfo f : getPublicFields()) 915 if (f.isStatic() && f.getName().equals(name)) 916 return f.inner(); 917 return null; 918 } 919 920 //----------------------------------------------------------------------------------------------------------------- 921 // Annotations 922 //----------------------------------------------------------------------------------------------------------------- 923 924 /** 925 * Finds the annotation of the specified type defined on this class or parent class/interface. 926 * 927 * <p> 928 * If the annotation cannot be found on the immediate class, searches methods with the same 929 * signature on the parent classes or interfaces. 930 * <br>The search is performed in child-to-parent order. 931 * 932 * @param a 933 * The annotation to search for. 934 * @return 935 * The annotation if found, or <jk>null</jk> if not. 936 */ 937 @SuppressWarnings("unchecked") 938 public <T extends Annotation> T getAnnotation(Class<T> a) { 939 if (a == null) 940 return null; 941 Optional<Annotation> o = annotationMap().get(a); 942 if (o == null) { 943 o = Optional.ofNullable(findAnnotation(a)); 944 annotationMap().put(a, o); 945 } 946 return o.isPresent() ? (T)o.get() : null; 947 } 948 949 /** 950 * Returns <jk>true</jk> if this class has the specified annotation. 951 * 952 * @param a 953 * The annotation to search for. 954 * @return 955 * The <jk>true</jk> if annotation if found. 956 */ 957 public boolean hasAnnotation(Class<? extends Annotation> a) { 958 return getAnnotation(a) != null; 959 } 960 961 /** 962 * Returns the specified annotation only if it's been declared on this class. 963 * 964 * <p> 965 * More efficient than calling {@link Class#getAnnotation(Class)} since it doesn't recursively look for the class 966 * up the parent chain. 967 * 968 * @param <T> The annotation class type. 969 * @param a The annotation class. 970 * @return The annotation, or <jk>null</jk> if not found. 971 */ 972 @SuppressWarnings("unchecked") 973 public <T extends Annotation> T getDeclaredAnnotation(Class<T> a) { 974 if (a == null) 975 return null; 976 Optional<Annotation> o = declaredAnnotationMap().get(a); 977 if (o == null) { 978 o = Optional.ofNullable(findDeclaredAnnotation(a)); 979 declaredAnnotationMap().put(a, o); 980 } 981 return o.isPresent() ? (T)o.get() : null; 982 } 983 984 /** 985 * Returns the specified annotation only if it's been declared on the package of this class. 986 * 987 * @param <T> The annotation class type. 988 * @param a The annotation class. 989 * @return The annotation, or <jk>null</jk> if not found. 990 */ 991 public <T extends Annotation> T getPackageAnnotation(Class<T> a) { 992 Package p = c == null ? null : c.getPackage(); 993 return (p == null ? null : p.getAnnotation(a)); 994 } 995 996 /** 997 * Same as {@link #getDeclaredAnnotation(Class)} but returns the annotation wrapped in a {@link AnnotationInfo}. 998 * 999 * @param a The annotation to search for. 1000 * @return The annotation if found, or <jk>null</jk> if not. 1001 */ 1002 public <T extends Annotation> AnnotationInfo<T> getDeclaredAnnotationInfo(Class<T> a) { 1003 T ca = getDeclaredAnnotation(a); 1004 return ca == null ? null : AnnotationInfo.of(this, ca); 1005 } 1006 1007 /** 1008 * Same as {@link #getPackageAnnotation(Class)} but returns the annotation wrapped in a {@link AnnotationInfo}. 1009 * 1010 * @param a The annotation to search for. 1011 * @return The annotation if found, or <jk>null</jk> if not. 1012 */ 1013 public <T extends Annotation> AnnotationInfo<T> getPackageAnnotationInfo(Class<T> a) { 1014 T ca = getPackageAnnotation(a); 1015 return ca == null ? null : AnnotationInfo.of(getPackage(), ca); 1016 } 1017 1018 /** 1019 * Returns all annotations of the specified type defined on the specified class or parent classes/interfaces. 1020 * 1021 * @param a 1022 * The annotation to search for. 1023 * @return 1024 * A list of all matching annotations found in child-to-parent order, or an empty list if none found. 1025 */ 1026 public <T extends Annotation> List<T> getAnnotations(Class<T> a) { 1027 return appendAnnotations(new ArrayList<>(), a); 1028 } 1029 1030 /** 1031 * Identical to {@link #getAnnotations(Class)} but optionally returns the list in reverse (parent-to-child) order. 1032 * 1033 * @param a 1034 * The annotation to search for. 1035 * @return 1036 * A list of all matching annotations found or an empty list if none found. 1037 */ 1038 public <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a) { 1039 return appendAnnotationsParentFirst(new ArrayList<>(), a); 1040 } 1041 1042 /** 1043 * Same as getAnnotations(Class) except returns the annotations with the accompanying class. 1044 * 1045 * <p> 1046 * Results are ordered child-to-parent. 1047 * 1048 * @param <T> The annotation class type. 1049 * @param a The annotation class type. 1050 * @return The found matches, or an empty list if annotation was not found. 1051 */ 1052 public <T extends Annotation> List<AnnotationInfo<T>> getAnnotationInfos(Class<T> a) { 1053 return appendAnnotationInfos(new ArrayList<>(), a); 1054 } 1055 1056 /** 1057 * Same as getAnnotations(Class) except returns the annotations with the accompanying class. 1058 * 1059 * <p> 1060 * Results are ordered parent-to-child. 1061 * 1062 * @param <T> The annotation class type. 1063 * @param a The annotation class type. 1064 * @return The found matches, or an empty list if annotation was not found. 1065 */ 1066 public <T extends Annotation> List<AnnotationInfo<T>> getAnnotationInfosParentFirst(Class<T> a) { 1067 return appendAnnotationInfosParentFirst(new ArrayList<>(), a); 1068 } 1069 1070 /** 1071 * Constructs an {@link AnnotationList} of all annotations found on this class. 1072 * 1073 * <p> 1074 * Annotations are appended in the following orders: 1075 * <ol> 1076 * <li>On this class. 1077 * <li>On parent classes ordered child-to-parent. 1078 * <li>On interfaces ordered child-to-parent. 1079 * <li>On the package of this class. 1080 * </ol> 1081 * 1082 * @param filter 1083 * Optional filter to apply to limit which annotations are added to the list. 1084 * <br>Can be <jk>null</jk> for no filtering. 1085 * @return A new {@link AnnotationList} object on every call. 1086 */ 1087 public AnnotationList getAnnotationList(Predicate<AnnotationInfo<?>> filter) { 1088 return appendAnnotationList(new AnnotationList(filter)); 1089 } 1090 1091 /** 1092 * Constructs an {@link AnnotationList} of all annotations found on this class. 1093 * 1094 * <p> 1095 * Annotations are appended in the following orders: 1096 * <ol> 1097 * <li>On the package of this class. 1098 * <li>On interfaces ordered parent-to-child. 1099 * <li>On parent classes ordered parent-to-child. 1100 * <li>On this class. 1101 * </ol> 1102 * 1103 * @param filter 1104 * Optional filter to apply to limit which annotations are added to the list. 1105 * <br>Can be <jk>null</jk> for no filtering. 1106 * @return A new {@link AnnotationList} object on every call. 1107 */ 1108 public AnnotationList getAnnotationListParentFirst(Predicate<AnnotationInfo<?>> filter) { 1109 return appendAnnotationListParentFirst(new AnnotationList(filter)); 1110 } 1111 1112 /** 1113 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified 1114 * list. 1115 * 1116 * <p> 1117 * Annotations are appended in the following orders: 1118 * <ol> 1119 * <li>On this class. 1120 * <li>On parent classes ordered child-to-parent. 1121 * <li>On interfaces ordered child-to-parent. 1122 * <li>On the package of this class. 1123 * </ol> 1124 * 1125 * @param l The list of annotations. 1126 * @param a The annotation to search for. 1127 * @return The same list. 1128 */ 1129 public <T extends Annotation> List<T> appendAnnotations(List<T> l, Class<T> a) { 1130 for (ClassInfo ci : getParents()) 1131 addIfNotNull(l, ci.getDeclaredAnnotation(a)); 1132 for (ClassInfo ci : getInterfaces()) 1133 addIfNotNull(l, ci.getDeclaredAnnotation(a)); 1134 addIfNotNull(l, getPackageAnnotation(a)); 1135 return l; 1136 } 1137 1138 /** 1139 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified 1140 * list. 1141 * 1142 * <p> 1143 * Annotations are appended in the following orders: 1144 * <ol> 1145 * <li>On the package of this class. 1146 * <li>On interfaces ordered child-to-parent. 1147 * <li>On parent classes ordered child-to-parent. 1148 * <li>On this class. 1149 * </ol> 1150 * 1151 * @param l The list of annotations. 1152 * @param a The annotation to search for. 1153 * @return The same list. 1154 */ 1155 public <T extends Annotation> List<T> appendAnnotationsParentFirst(List<T> l, Class<T> a) { 1156 addIfNotNull(l, getPackageAnnotation(a)); 1157 for (ClassInfo ci : getInterfacesParentFirst()) 1158 addIfNotNull(l, ci.getDeclaredAnnotation(a)); 1159 for (ClassInfo ci : getParentsParentFirst()) 1160 addIfNotNull(l, ci.getDeclaredAnnotation(a)); 1161 return l; 1162 } 1163 1164 /** 1165 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified 1166 * list. 1167 * 1168 * <p> 1169 * Annotations are appended in the following orders: 1170 * <ol> 1171 * <li>On this class. 1172 * <li>On parent classes ordered child-to-parent. 1173 * <li>On interfaces ordered child-to-parent. 1174 * <li>On the package of this class. 1175 * </ol> 1176 * 1177 * @param l The list of annotations. 1178 * @param a The annotation to search for. 1179 * @return The same list. 1180 */ 1181 public <T extends Annotation> List<AnnotationInfo<T>> appendAnnotationInfos(List<AnnotationInfo<T>> l, Class<T> a) { 1182 for (ClassInfo ci : getParents()) 1183 addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); 1184 for (ClassInfo ci : getInterfaces()) 1185 addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); 1186 addIfNotNull(l, getPackageAnnotationInfo(a)); 1187 return l; 1188 } 1189 1190 /** 1191 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified 1192 * list. 1193 * 1194 * <p> 1195 * Annotations are appended in the following orders: 1196 * <ol> 1197 * <li>On the package of this class. 1198 * <li>On interfaces ordered child-to-parent. 1199 * <li>On parent classes ordered child-to-parent. 1200 * <li>On this class. 1201 * </ol> 1202 * 1203 * @param l The list of annotations. 1204 * @param a The annotation to search for. 1205 * @return The same list. 1206 */ 1207 public <T extends Annotation> List<AnnotationInfo<T>> appendAnnotationInfosParentFirst(List<AnnotationInfo<T>> l, Class<T> a) { 1208 addIfNotNull(l, getPackageAnnotationInfo(a)); 1209 for (ClassInfo ci : getInterfacesParentFirst()) 1210 addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); 1211 for (ClassInfo ci : getParentsParentFirst()) 1212 addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); 1213 return l; 1214 } 1215 1216 AnnotationList appendAnnotationList(AnnotationList m) { 1217 for (ClassInfo ci : getParents()) 1218 for (Annotation a : ci.c.getDeclaredAnnotations()) 1219 m.add(AnnotationInfo.of(ci, a)); 1220 for (ClassInfo ci : getInterfaces()) 1221 for (Annotation a : ci.c.getDeclaredAnnotations()) 1222 m.add(AnnotationInfo.of(ci, a)); 1223 Package p = c.getPackage(); 1224 if (p != null) 1225 for (Annotation a : p.getDeclaredAnnotations()) 1226 m.add(AnnotationInfo.of(p, a)); 1227 return m; 1228 } 1229 1230 AnnotationList appendAnnotationListParentFirst(AnnotationList m) { 1231 Package p = c.getPackage(); 1232 if (p != null) 1233 for (Annotation a : p.getDeclaredAnnotations()) 1234 m.add(AnnotationInfo.of(p, a)); 1235 for (ClassInfo ci : getInterfacesParentFirst()) 1236 for (Annotation a : ci.c.getDeclaredAnnotations()) 1237 m.add(AnnotationInfo.of(ci, a)); 1238 for (ClassInfo ci : getParentsParentFirst()) 1239 for (Annotation a : ci.c.getDeclaredAnnotations()) 1240 m.add(AnnotationInfo.of(ci, a)); 1241 return m; 1242 } 1243 1244 <T extends Annotation> T findAnnotation(Class<T> a) { 1245 T t2 = getDeclaredAnnotation(a); 1246 if (t2 != null) 1247 return t2; 1248 1249 ClassInfo sci = getParent(); 1250 if (sci != null) { 1251 t2 = sci.getAnnotation(a); 1252 if (t2 != null) 1253 return t2; 1254 } 1255 1256 for (ClassInfo c2 : getInterfaces()) { 1257 t2 = c2.getAnnotation(a); 1258 if (t2 != null) 1259 return t2; 1260 } 1261 1262 return null; 1263 } 1264 1265 @SuppressWarnings("unchecked") 1266 private <T extends Annotation> T findDeclaredAnnotation(Class<T> a) { 1267 for (Annotation a2 : c.getDeclaredAnnotations()) 1268 if (a2.annotationType() == a) 1269 return (T)a2; 1270 return null; 1271 } 1272 1273 private synchronized Map<Class<?>,Optional<Annotation>> annotationMap() { 1274 if (annotationMap == null) 1275 annotationMap = new ConcurrentHashMap<>(); 1276 return annotationMap; 1277 } 1278 1279 private synchronized Map<Class<?>,Optional<Annotation>> declaredAnnotationMap() { 1280 if (declaredAnnotationMap == null) 1281 declaredAnnotationMap = new ConcurrentHashMap<>(); 1282 return declaredAnnotationMap; 1283 } 1284 1285 //----------------------------------------------------------------------------------------------------------------- 1286 // Characteristics 1287 //----------------------------------------------------------------------------------------------------------------- 1288 1289 /** 1290 * Returns <jk>true</jk> if all specified flags are applicable to this class. 1291 * 1292 * @param flags The flags to test for. 1293 * @return <jk>true</jk> if all specified flags are applicable to this class. 1294 */ 1295 public boolean isAll(ReflectFlags...flags) { 1296 for (ReflectFlags f : flags) { 1297 switch (f) { 1298 case DEPRECATED: 1299 if (isNotDeprecated()) 1300 return false; 1301 break; 1302 case NOT_DEPRECATED: 1303 if (isDeprecated()) 1304 return false; 1305 break; 1306 case PUBLIC: 1307 if (isNotPublic()) 1308 return false; 1309 break; 1310 case NOT_PUBLIC: 1311 if (isPublic()) 1312 return false; 1313 break; 1314 case STATIC: 1315 if (isNotStatic()) 1316 return false; 1317 break; 1318 case NOT_STATIC: 1319 if (isStatic()) 1320 return false; 1321 break; 1322 case MEMBER: 1323 if (isNotMemberClass()) 1324 return false; 1325 break; 1326 case NOT_MEMBER: 1327 if (isMemberClass()) 1328 return false; 1329 break; 1330 case ABSTRACT: 1331 if (isNotAbstract()) 1332 return false; 1333 break; 1334 case NOT_ABSTRACT: 1335 if (isAbstract()) 1336 return false; 1337 break; 1338 case INTERFACE: 1339 if (isClass()) 1340 return false; 1341 break; 1342 case CLASS: 1343 if (isInterface()) 1344 return false; 1345 break; 1346 default: 1347 throw new RuntimeException("Invalid flag for class: " + f); 1348 1349 } 1350 } 1351 return true; 1352 } 1353 1354 /** 1355 * Returns <jk>true</jk> if all specified flags are applicable to this class. 1356 * 1357 * @param flags The flags to test for. 1358 * @return <jk>true</jk> if all specified flags are applicable to this class. 1359 */ 1360 public boolean isAny(ReflectFlags...flags) { 1361 for (ReflectFlags f : flags) { 1362 switch (f) { 1363 case DEPRECATED: 1364 if (isDeprecated()) 1365 return true; 1366 break; 1367 case NOT_DEPRECATED: 1368 if (isNotDeprecated()) 1369 return true; 1370 break; 1371 case PUBLIC: 1372 if (isPublic()) 1373 return true; 1374 break; 1375 case NOT_PUBLIC: 1376 if (isNotPublic()) 1377 return true; 1378 break; 1379 case STATIC: 1380 if (isStatic()) 1381 return true; 1382 break; 1383 case NOT_STATIC: 1384 if (isNotStatic()) 1385 return true; 1386 break; 1387 case MEMBER: 1388 if (isMemberClass()) 1389 return true; 1390 break; 1391 case NOT_MEMBER: 1392 if (isNotMemberClass()) 1393 return true; 1394 break; 1395 case ABSTRACT: 1396 if (isAbstract()) 1397 return true; 1398 break; 1399 case NOT_ABSTRACT: 1400 if (isNotAbstract()) 1401 return true; 1402 break; 1403 case INTERFACE: 1404 if (isInterface()) 1405 return true; 1406 break; 1407 case CLASS: 1408 if (isClass()) 1409 return true; 1410 break; 1411 default: 1412 throw new RuntimeException("Invalid flag for class: " + f); 1413 } 1414 } 1415 return false; 1416 } 1417 1418 /** 1419 * Returns <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it. 1420 * 1421 * @return <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it. 1422 */ 1423 public boolean isDeprecated() { 1424 return c != null && c.isAnnotationPresent(Deprecated.class); 1425 } 1426 1427 /** 1428 * Returns <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it. 1429 * 1430 * @return <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it. 1431 */ 1432 public boolean isNotDeprecated() { 1433 return c == null || ! c.isAnnotationPresent(Deprecated.class); 1434 } 1435 1436 /** 1437 * Returns <jk>true</jk> if this class is public. 1438 * 1439 * @return <jk>true</jk> if this class is public. 1440 */ 1441 public boolean isPublic() { 1442 return c != null && Modifier.isPublic(c.getModifiers()); 1443 } 1444 1445 /** 1446 * Returns <jk>true</jk> if this class is not public. 1447 * 1448 * @return <jk>true</jk> if this class is not public. 1449 */ 1450 public boolean isNotPublic() { 1451 return c == null || ! Modifier.isPublic(c.getModifiers()); 1452 } 1453 1454 /** 1455 * Returns <jk>true</jk> if this class is public. 1456 * 1457 * <p> 1458 * Note that interfaces are always reported as static, and the static keyword on a member interface is meaningless. 1459 * 1460 * @return <jk>true</jk> if this class is public. 1461 */ 1462 public boolean isStatic() { 1463 return c != null && Modifier.isStatic(c.getModifiers()); 1464 } 1465 1466 /** 1467 * Returns <jk>true</jk> if this class is not static. 1468 * 1469 * <p> 1470 * Note that interfaces are always reported as static, and the static keyword on a member interface is meaningless. 1471 * 1472 * @return <jk>true</jk> if this class is not static. 1473 */ 1474 public boolean isNotStatic() { 1475 return c == null || ! Modifier.isStatic(c.getModifiers()); 1476 } 1477 1478 /** 1479 * Returns <jk>true</jk> if this class is abstract. 1480 * 1481 * <p> 1482 * Note that interfaces are always reported as abstract. 1483 * 1484 * @return <jk>true</jk> if this class is abstract. 1485 */ 1486 public boolean isAbstract() { 1487 return c != null && Modifier.isAbstract(c.getModifiers()); 1488 } 1489 1490 /** 1491 * Returns <jk>true</jk> if this class is not abstract. 1492 * 1493 * <p> 1494 * Note that interfaces are always reported as abstract. 1495 * 1496 * @return <jk>true</jk> if this class is not abstract. 1497 */ 1498 public boolean isNotAbstract() { 1499 return c == null || ! Modifier.isAbstract(c.getModifiers()); 1500 } 1501 1502 /** 1503 * Returns <jk>true</jk> if this class is a member class. 1504 * 1505 * @return <jk>true</jk> if this class is a member class. 1506 */ 1507 public boolean isMemberClass() { 1508 return c != null && c.isMemberClass(); 1509 } 1510 1511 /** 1512 * Returns <jk>true</jk> if this class is a member class. 1513 * 1514 * @return <jk>true</jk> if this class is a member class. 1515 */ 1516 public boolean isNotMemberClass() { 1517 return c == null || ! c.isMemberClass(); 1518 } 1519 1520 /** 1521 * Returns <jk>true</jk> if this class is a member class and not static. 1522 * 1523 * @return <jk>true</jk> if this class is a member class and not static. 1524 */ 1525 public boolean isNonStaticMemberClass() { 1526 return c != null && c.isMemberClass() && ! isStatic(); 1527 } 1528 1529 /** 1530 * Returns <jk>false</jk> if this class is a member class and not static. 1531 * 1532 * @return <jk>false</jk> if this class is a member class and not static. 1533 */ 1534 public boolean isNotNonStaticMemberClass() { 1535 return ! isNonStaticMemberClass(); 1536 } 1537 1538 /** 1539 * Returns <jk>true</jk> if this class is a local class. 1540 * 1541 * @return <jk>true</jk> if this class is a local class. 1542 */ 1543 public boolean isLocalClass() { 1544 return c != null && c.isLocalClass(); 1545 } 1546 1547 /** 1548 * Returns <jk>true</jk> if this class is a local class. 1549 * 1550 * @return <jk>true</jk> if this class is a local class. 1551 */ 1552 public boolean isNotLocalClass() { 1553 return c == null || ! c.isLocalClass(); 1554 } 1555 1556 /** 1557 * Identifies if the specified visibility matches this constructor. 1558 * 1559 * @param v The visibility to validate against. 1560 * @return <jk>true</jk> if this visibility matches the modifier attribute of this constructor. 1561 */ 1562 public boolean isVisible(Visibility v) { 1563 return c != null && v.isVisible(c); 1564 } 1565 1566 /** 1567 * Returns <jk>true</jk> if this is a primitive class. 1568 * 1569 * @return <jk>true</jk> if this is a primitive class. 1570 */ 1571 public boolean isPrimitive() { 1572 return c != null && c.isPrimitive(); 1573 } 1574 1575 /** 1576 * Returns <jk>true</jk> if this is not a primitive class. 1577 * 1578 * @return <jk>true</jk> if this is not a primitive class. 1579 */ 1580 public boolean isNotPrimitive() { 1581 return c == null || ! c.isPrimitive(); 1582 } 1583 1584 /** 1585 * Returns <jk>true</jk> if this class is an interface. 1586 * 1587 * @return <jk>true</jk> if this class is an interface. 1588 */ 1589 public boolean isInterface() { 1590 return c != null && c.isInterface(); 1591 } 1592 1593 /** 1594 * Returns <jk>true</jk> if this class is not an interface. 1595 * 1596 * @return <jk>true</jk> if this class is not an interface. 1597 */ 1598 public boolean isClass() { 1599 return c != null && ! c.isInterface(); 1600 } 1601 1602 /** 1603 * Returns <jk>true</jk> if this class is a {@link RuntimeException}. 1604 * 1605 * @return <jk>true</jk> if this class is a {@link RuntimeException}. 1606 */ 1607 public boolean isRuntimeException() { 1608 return isChildOf(RuntimeException.class); 1609 } 1610 1611 //----------------------------------------------------------------------------------------------------------------- 1612 // Primitive wrappers 1613 //----------------------------------------------------------------------------------------------------------------- 1614 1615 /** 1616 * Returns <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value. 1617 * 1618 * @return <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value. 1619 */ 1620 public boolean hasPrimitiveWrapper() { 1621 return pmap1.containsKey(c); 1622 } 1623 1624 /** 1625 * If this class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class 1626 * (e.g. <code>Integer.<jk>class</jk></code>). 1627 * 1628 * @return The wrapper class, or <jk>null</jk> if class is not a primitive. 1629 */ 1630 public Class<?> getPrimitiveWrapper() { 1631 return pmap1.get(c); 1632 } 1633 1634 /** 1635 * If this class is a primitive wrapper (e.g. <code><jk>Integer</jk>.<jk>class</jk></code>) returns it's 1636 * primitive class (e.g. <code>int.<jk>class</jk></code>). 1637 * 1638 * @return The primitive class, or <jk>null</jk> if class is not a primitive wrapper. 1639 */ 1640 public Class<?> getPrimitiveForWrapper() { 1641 return pmap2.get(c); 1642 } 1643 1644 /** 1645 * If this class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class 1646 * (e.g. <code>Integer.<jk>class</jk></code>). 1647 * 1648 * @return The wrapper class if it's primitive, or the same class if class is not a primitive. 1649 */ 1650 public Class<?> getWrapperIfPrimitive() { 1651 if (c != null && ! c.isPrimitive()) 1652 return c; 1653 return pmap1.get(c); 1654 } 1655 1656 /** 1657 * Same as {@link #getWrapperIfPrimitive()} but wraps it in a {@link ClassInfo}. 1658 * 1659 * @return The wrapper class if it's primitive, or the same class if class is not a primitive. 1660 */ 1661 public ClassInfo getWrapperInfoIfPrimitive() { 1662 if (c == null || ! c.isPrimitive()) 1663 return this; 1664 return of(pmap1.get(c)); 1665 } 1666 1667 /** 1668 * Returns the default value for this primitive class. 1669 * 1670 * @return The default value, or <jk>null</jk> if this is not a primitive class. 1671 */ 1672 public Object getPrimitiveDefault() { 1673 return primitiveDefaultMap.get(c); 1674 } 1675 1676 private static final Map<Class<?>, Class<?>> 1677 pmap1 = new HashMap<>(), 1678 pmap2 = new HashMap<>(); 1679 static { 1680 pmap1.put(boolean.class, Boolean.class); 1681 pmap1.put(byte.class, Byte.class); 1682 pmap1.put(short.class, Short.class); 1683 pmap1.put(char.class, Character.class); 1684 pmap1.put(int.class, Integer.class); 1685 pmap1.put(long.class, Long.class); 1686 pmap1.put(float.class, Float.class); 1687 pmap1.put(double.class, Double.class); 1688 pmap2.put(Boolean.class, boolean.class); 1689 pmap2.put(Byte.class, byte.class); 1690 pmap2.put(Short.class, short.class); 1691 pmap2.put(Character.class, char.class); 1692 pmap2.put(Integer.class, int.class); 1693 pmap2.put(Long.class, long.class); 1694 pmap2.put(Float.class, float.class); 1695 pmap2.put(Double.class, double.class); 1696 } 1697 1698 private static final Map<Class<?>,Object> primitiveDefaultMap = Collections.unmodifiableMap( 1699 new AMap<Class<?>,Object>() 1700 .append(Boolean.TYPE, false) 1701 .append(Character.TYPE, (char)0) 1702 .append(Short.TYPE, (short)0) 1703 .append(Integer.TYPE, 0) 1704 .append(Long.TYPE, 0l) 1705 .append(Float.TYPE, 0f) 1706 .append(Double.TYPE, 0d) 1707 .append(Byte.TYPE, (byte)0) 1708 .append(Boolean.class, false) 1709 .append(Character.class, (char)0) 1710 .append(Short.class, (short)0) 1711 .append(Integer.class, 0) 1712 .append(Long.class, 0l) 1713 .append(Float.class, 0f) 1714 .append(Double.class, 0d) 1715 .append(Byte.class, (byte)0) 1716 ); 1717 1718 //----------------------------------------------------------------------------------------------------------------- 1719 // Labels 1720 //----------------------------------------------------------------------------------------------------------------- 1721 1722 /** 1723 * Returns the full name of this class. 1724 * 1725 * <h5 class='section'>Examples:</h5> 1726 * <ul> 1727 * <li><js>"com.foo.MyClass"<js> - Normal class 1728 * <li><js>"com.foo.MyClass[][]"<js> - Array. 1729 * <li><js>"com.foo.MyClass$InnerClass"<js> - Inner class. 1730 * <li><js>"com.foo.MyClass$InnerClass[][]"<js> - Inner class array. 1731 * <li><js>"int"<js> - Primitive class. 1732 * <li><js>"int[][]"<js> - Primitive class class. 1733 * <li><js>"java.util.Map<java.lang.String,java.lang.Object>"<js> - Parameterized type. 1734 * <li><js>"java.util.AbstractMap<K,V>"<js> - Parameterized generic type. 1735 * <li><js>"V"<js> - Parameterized generic type argument. 1736 * </ul> 1737 * 1738 * @return The underlying class name. 1739 */ 1740 public String getFullName() { 1741 Class<?> ct = getComponentType().inner(); 1742 int dim = getDimensions(); 1743 if (ct != null && dim == 0 && ! isParameterizedType) 1744 return ct.getName(); 1745 StringBuilder sb = new StringBuilder(128); 1746 appendFullName(sb); 1747 return sb.toString(); 1748 } 1749 1750 /** 1751 * Returns all possible names for this class. 1752 * 1753 * @return 1754 * An array consisting of: 1755 * <ul> 1756 * <li>{@link #getFullName()} 1757 * <li>{@link Class#getName()} - Note that this might be a dup. 1758 * <li>{@link #getShortName()} 1759 * <li>{@link #getSimpleName()} 1760 * </ul> 1761 */ 1762 public String[] getNames() { 1763 return new String[]{ getFullName(), c.getName(), getShortName(), getSimpleName() }; 1764 } 1765 1766 /** 1767 * Same as {@link #getFullName()} but appends to an existing string builder. 1768 * 1769 * @param sb The string builder to append to. 1770 * @return The same string builder. 1771 */ 1772 public StringBuilder appendFullName(StringBuilder sb) { 1773 Class<?> ct = getComponentType().inner(); 1774 int dim = getDimensions(); 1775 if (ct != null && dim == 0 && ! isParameterizedType) 1776 return sb.append(ct.getName()); 1777 sb.append(ct != null ? ct.getName() : t.getTypeName()); 1778 if (isParameterizedType) { 1779 ParameterizedType pt = (ParameterizedType)t; 1780 sb.append('<'); 1781 boolean first = true; 1782 for (Type t2 : pt.getActualTypeArguments()) { 1783 if (! first) 1784 sb.append(','); 1785 first = false; 1786 of(t2).appendFullName(sb); 1787 } 1788 sb.append('>'); 1789 } 1790 for (int i = 0; i < dim; i++) 1791 sb.append('[').append(']'); 1792 return sb; 1793 } 1794 1795 /** 1796 * Returns the short name of the underlying class. 1797 * 1798 * <p> 1799 * Similar to {@link #getSimpleName()} but also renders local or member class name prefixes. 1800 * 1801 * @return The short name of the underlying class. 1802 */ 1803 public String getShortName() { 1804 Class<?> ct = getComponentType().inner(); 1805 int dim = getDimensions(); 1806 if (ct != null && dim == 0 && ! (isParameterizedType || isMemberClass() || c.isLocalClass())) 1807 return ct.getSimpleName(); 1808 StringBuilder sb = new StringBuilder(32); 1809 appendShortName(sb); 1810 return sb.toString(); 1811 } 1812 1813 /** 1814 * Same as {@link #getShortName()} but appends to an existing string builder. 1815 * 1816 * @param sb The string builder to append to. 1817 * @return The same string builder. 1818 */ 1819 public StringBuilder appendShortName(StringBuilder sb) { 1820 Class<?> ct = getComponentType().inner(); 1821 int dim = getDimensions(); 1822 if (ct != null) { 1823 if (ct.isLocalClass()) 1824 sb.append(of(ct.getEnclosingClass()).getSimpleName()).append('$').append(ct.getSimpleName()); 1825 else if (ct.isMemberClass()) 1826 sb.append(of(ct.getDeclaringClass()).getSimpleName()).append('$').append(ct.getSimpleName()); 1827 else 1828 sb.append(ct.getSimpleName()); 1829 } else { 1830 sb.append(t.getTypeName()); 1831 } 1832 if (isParameterizedType) { 1833 ParameterizedType pt = (ParameterizedType)t; 1834 sb.append('<'); 1835 boolean first = true; 1836 for (Type t2 : pt.getActualTypeArguments()) { 1837 if (! first) 1838 sb.append(','); 1839 first = false; 1840 of(t2).appendShortName(sb); 1841 } 1842 sb.append('>'); 1843 } 1844 for (int i = 0; i < dim; i++) 1845 sb.append('[').append(']'); 1846 return sb; 1847 } 1848 1849 /** 1850 * Returns the simple name of the underlying class. 1851 * 1852 * <p> 1853 * Returns either {@link Class#getSimpleName()} or {@link Type#getTypeName()} depending on whether 1854 * this is a class or type. 1855 * 1856 * @return The simple name of the underlying class; 1857 */ 1858 public String getSimpleName() { 1859 return c != null ? c.getSimpleName() : t.getTypeName(); 1860 } 1861 1862 /** 1863 * Returns the name of the underlying class. 1864 * 1865 * @return The name of the underlying class. 1866 */ 1867 public String getName() { 1868 return c != null ? c.getName() : t.getTypeName(); 1869 } 1870 1871 /** 1872 * Same as {@link #getSimpleName()} but uses <js>"Array"</js> instead of <js>"[]"</js>. 1873 * 1874 * @return The readable name for this class. 1875 */ 1876 public String getReadableName() { 1877 if (c == null) 1878 return t.getTypeName(); 1879 if (! c.isArray()) 1880 return c.getSimpleName(); 1881 Class<?> c = this.c; 1882 StringBuilder sb = new StringBuilder(); 1883 while (c.isArray()) { 1884 sb.append("Array"); 1885 c = c.getComponentType(); 1886 } 1887 return c.getSimpleName() + sb; 1888 } 1889 1890 //----------------------------------------------------------------------------------------------------------------- 1891 // Hierarchy 1892 //----------------------------------------------------------------------------------------------------------------- 1893 1894 /** 1895 * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>. 1896 * 1897 * @param child The child class. 1898 * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>. 1899 */ 1900 public boolean isParentOf(Class<?> child) { 1901 return c != null && child != null && c.isAssignableFrom(child); 1902 } 1903 1904 /** 1905 * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>. 1906 * 1907 * @param child The child class. 1908 * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>. 1909 */ 1910 public boolean isParentOf(Type child) { 1911 if (child instanceof Class) 1912 return isParentOf((Class<?>)child); 1913 return false; 1914 } 1915 1916 /** 1917 * Returns <jk>true</jk> if this class is a child of <c>parent</c>. 1918 * 1919 * @param parent The parent class. 1920 * @return <jk>true</jk> if this class is a parent of <c>child</c>. 1921 */ 1922 public boolean isStrictChildOf(Class<?> parent) { 1923 return c != null && parent != null && parent.isAssignableFrom(c) && ! c.equals(parent); 1924 } 1925 1926 /** 1927 * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. 1928 * 1929 * @param parent The parent class. 1930 * @return <jk>true</jk> if this class is a child or the same as <c>parent</c>. 1931 */ 1932 public boolean isChildOf(Class<?> parent) { 1933 return c != null && parent != null && parent.isAssignableFrom(c); 1934 } 1935 1936 /** 1937 * Returns <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>. 1938 * 1939 * @param parents The parents class. 1940 * @return <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>. 1941 */ 1942 public boolean isChildOfAny(Class<?>...parents) { 1943 for (Class<?> p : parents) 1944 if (isChildOf(p)) 1945 return true; 1946 return false; 1947 } 1948 1949 /** 1950 * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. 1951 * 1952 * @param parent The parent class. 1953 * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>. 1954 */ 1955 public boolean isChildOf(Type parent) { 1956 if (parent instanceof Class) 1957 return isChildOf((Class<?>)parent); 1958 return false; 1959 } 1960 1961 /** 1962 * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. 1963 * 1964 * @param parent The parent class. 1965 * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>. 1966 */ 1967 public boolean isChildOf(ClassInfo parent) { 1968 return isChildOf(parent.inner()); 1969 } 1970 1971 /** 1972 * Checks for equality with the specified class. 1973 * 1974 * @param c The class to check equality with. 1975 * @return <jk>true</jk> if the specified class is the same as this one. 1976 */ 1977 public boolean is(Class<?> c) { 1978 return this.c != null && this.c.equals(c); 1979 } 1980 1981 /** 1982 * Checks for equality with the specified class. 1983 * 1984 * @param c The class to check equality with. 1985 * @return <jk>true</jk> if the specified class is the same as this one. 1986 */ 1987 public boolean is(ClassInfo c) { 1988 if (this.c != null) 1989 return this.c.equals(c.inner()); 1990 return t.equals(c.t); 1991 } 1992 1993 /** 1994 * Returns <jk>true</jk> if this class is any of the specified types. 1995 * 1996 * @param types The types to check against. 1997 * @return <jk>true</jk> if this class is any of the specified types. 1998 */ 1999 public boolean isAny(Class<?>...types) { 2000 for (Class<?> cc : types) 2001 if (is(cc)) 2002 return true; 2003 return false; 2004 } 2005 2006 /** 2007 * Returns the package of this class. 2008 * 2009 * @return The package of this class. 2010 */ 2011 public Package getPackage() { 2012 return c == null ? null : c.getPackage(); 2013 } 2014 2015 /** 2016 * Returns <jk>true</jk> if this class is not in the root package. 2017 * 2018 * @return <jk>true</jk> if this class is not in the root package. 2019 */ 2020 public boolean hasPackage() { 2021 return getPackage() != null; 2022 } 2023 2024 /** 2025 * Returns the number of dimensions if this is an array type. 2026 * 2027 * @return The number of dimensions if this is an array type, or <c>0</c> if it is not. 2028 */ 2029 public int getDimensions() { 2030 if (dim == -1) { 2031 int d = 0; 2032 Class<?> ct = c; 2033 while (ct != null && ct.isArray()) { 2034 d++; 2035 ct = ct.getComponentType(); 2036 } 2037 this.dim = d; 2038 this.componentType = ct == c ? this : of(ct); 2039 } 2040 return dim; 2041 } 2042 2043 /** 2044 * Returns the base component type of this class if it's an array. 2045 * 2046 * @return The base component type of this class if it's an array, or this object if it's not. 2047 */ 2048 public ClassInfo getComponentType() { 2049 if (componentType == null) { 2050 if (c == null) 2051 componentType = this; 2052 else 2053 getDimensions(); 2054 } 2055 return componentType; 2056 } 2057 2058 /** 2059 * Returns <jk>true</jk> if this class is an enum. 2060 * 2061 * @return <jk>true</jk> if this class is an enum. 2062 */ 2063 public boolean isEnum() { 2064 return c != null && c.isEnum(); 2065 } 2066 2067 //----------------------------------------------------------------------------------------------------------------- 2068 // Instantiation 2069 //----------------------------------------------------------------------------------------------------------------- 2070 2071 /** 2072 * Shortcut for calling {@link Class#newInstance()} on the underlying class. 2073 * 2074 * @return A new instance of the underlying class 2075 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 2076 */ 2077 public Object newInstance() throws ExecutableException { 2078 if (c == null) 2079 throw new ExecutableException("Type ''{0}'' cannot be instantiated", getFullName()); 2080 try { 2081 return c.newInstance(); 2082 } catch (InstantiationException | IllegalAccessException e) { 2083 throw new ExecutableException(e); 2084 } 2085 } 2086 2087 //----------------------------------------------------------------------------------------------------------------- 2088 // Parameter types 2089 //----------------------------------------------------------------------------------------------------------------- 2090 2091 /** 2092 * Finds the real parameter type of this class. 2093 * 2094 * @param index The zero-based index of the parameter to resolve. 2095 * @param pt The parameterized type class containing the parameterized type to resolve (e.g. <c>HashMap</c>). 2096 * @return The resolved real class. 2097 */ 2098 public Class<?> getParameterType(int index, Class<?> pt) { 2099 if (pt == null) 2100 throw new FormattedIllegalArgumentException("Parameterized type cannot be null"); 2101 2102 // We need to make up a mapping of type names. 2103 Map<Type,Type> typeMap = new HashMap<>(); 2104 Class<?> cc = c; 2105 while (pt != cc.getSuperclass()) { 2106 extractTypes(typeMap, cc); 2107 cc = cc.getSuperclass(); 2108 if (cc == null) 2109 throw new FormattedIllegalArgumentException("Class ''{0}'' is not a subclass of parameterized type ''{1}''", c.getSimpleName(), pt.getSimpleName()); 2110 } 2111 2112 Type gsc = cc.getGenericSuperclass(); 2113 2114 if (! (gsc instanceof ParameterizedType)) 2115 throw new FormattedIllegalArgumentException("Class ''{0}'' is not a parameterized type", pt.getSimpleName()); 2116 2117 ParameterizedType cpt = (ParameterizedType)gsc; 2118 Type[] atArgs = cpt.getActualTypeArguments(); 2119 if (index >= atArgs.length) 2120 throw new FormattedIllegalArgumentException("Invalid type index. index={0}, argsLength={1}", index, atArgs.length); 2121 Type actualType = cpt.getActualTypeArguments()[index]; 2122 2123 if (typeMap.containsKey(actualType)) 2124 actualType = typeMap.get(actualType); 2125 2126 if (actualType instanceof Class) { 2127 return (Class<?>)actualType; 2128 2129 } else if (actualType instanceof GenericArrayType) { 2130 Type gct = ((GenericArrayType)actualType).getGenericComponentType(); 2131 if (gct instanceof ParameterizedType) 2132 return Array.newInstance((Class<?>)((ParameterizedType)gct).getRawType(), 0).getClass(); 2133 } else if (actualType instanceof TypeVariable) { 2134 TypeVariable<?> typeVariable = (TypeVariable<?>)actualType; 2135 List<Class<?>> nestedOuterTypes = new LinkedList<>(); 2136 for (Class<?> ec = cc.getEnclosingClass(); ec != null; ec = ec.getEnclosingClass()) { 2137 Class<?> outerClass = cc.getClass(); 2138 nestedOuterTypes.add(outerClass); 2139 Map<Type,Type> outerTypeMap = new HashMap<>(); 2140 extractTypes(outerTypeMap, outerClass); 2141 for (Map.Entry<Type,Type> entry : outerTypeMap.entrySet()) { 2142 Type key = entry.getKey(), value = entry.getValue(); 2143 if (key instanceof TypeVariable) { 2144 TypeVariable<?> keyType = (TypeVariable<?>)key; 2145 if (keyType.getName().equals(typeVariable.getName()) && isInnerClass(keyType.getGenericDeclaration(), typeVariable.getGenericDeclaration())) { 2146 if (value instanceof Class) 2147 return (Class<?>)value; 2148 typeVariable = (TypeVariable<?>)entry.getValue(); 2149 } 2150 } 2151 } 2152 } 2153 } else if (actualType instanceof ParameterizedType) { 2154 return (Class<?>)((ParameterizedType)actualType).getRawType(); 2155 } 2156 throw new FormattedIllegalArgumentException("Could not resolve variable ''{0}'' to a type.", actualType.getTypeName()); 2157 } 2158 2159 private static boolean isInnerClass(GenericDeclaration od, GenericDeclaration id) { 2160 if (od instanceof Class && id instanceof Class) { 2161 Class<?> oc = (Class<?>)od; 2162 Class<?> ic = (Class<?>)id; 2163 while ((ic = ic.getEnclosingClass()) != null) 2164 if (ic == oc) 2165 return true; 2166 } 2167 return false; 2168 } 2169 2170 private static void extractTypes(Map<Type,Type> typeMap, Class<?> c) { 2171 Type gs = c.getGenericSuperclass(); 2172 if (gs instanceof ParameterizedType) { 2173 ParameterizedType pt = (ParameterizedType)gs; 2174 Type[] typeParameters = ((Class<?>)pt.getRawType()).getTypeParameters(); 2175 Type[] actualTypeArguments = pt.getActualTypeArguments(); 2176 for (int i = 0; i < typeParameters.length; i++) { 2177 if (typeMap.containsKey(actualTypeArguments[i])) 2178 actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]); 2179 typeMap.put(typeParameters[i], actualTypeArguments[i]); 2180 } 2181 } 2182 } 2183 2184 //----------------------------------------------------------------------------------------------------------------- 2185 // Other 2186 //----------------------------------------------------------------------------------------------------------------- 2187 2188 @Override 2189 public String toString() { 2190 return t.toString(); 2191 } 2192 2193 @Override 2194 public int hashCode() { 2195 return t.hashCode(); 2196 } 2197 2198 @Override 2199 public boolean equals(Object o) { 2200 return o == null ? false : ((ClassInfo)o).t.equals(t); 2201 } 2202}