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