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&lt;java.lang.String,java.lang.Object&gt;"<js> - Parameterized type.
1734    *    <li><js>"java.util.AbstractMap&lt;K,V&gt;"<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}