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    * Same as {@link #getFullName()} but appends to an existing string builder.
1752    *
1753    * @param sb The string builder to append to.
1754    * @return The same string builder.
1755    */
1756   public StringBuilder appendFullName(StringBuilder sb) {
1757      Class<?> ct = getComponentType().inner();
1758      int dim = getDimensions();
1759      if (ct != null && dim == 0 && ! isParameterizedType)
1760         return sb.append(ct.getName());
1761      sb.append(ct != null ? ct.getName() : t.getTypeName());
1762      if (isParameterizedType) {
1763         ParameterizedType pt = (ParameterizedType)t;
1764         sb.append('<');
1765         boolean first = true;
1766         for (Type t2 : pt.getActualTypeArguments()) {
1767            if (! first)
1768               sb.append(',');
1769            first = false;
1770            of(t2).appendFullName(sb);
1771         }
1772         sb.append('>');
1773      }
1774      for (int i = 0; i < dim; i++)
1775         sb.append('[').append(']');
1776      return sb;
1777   }
1778
1779   /**
1780    * Returns the short name of the underlying class.
1781    *
1782    * <p>
1783    * Similar to {@link #getSimpleName()} but also renders local or member class name prefixes.
1784    *
1785    * @return The short name of the underlying class.
1786    */
1787   public String getShortName() {
1788      Class<?> ct = getComponentType().inner();
1789      int dim = getDimensions();
1790      if (ct != null && dim == 0 && ! (isParameterizedType || isMemberClass() || c.isLocalClass()))
1791         return ct.getSimpleName();
1792      StringBuilder sb = new StringBuilder(32);
1793      appendShortName(sb);
1794      return sb.toString();
1795   }
1796
1797   /**
1798    * Same as {@link #getShortName()} but appends to an existing string builder.
1799    *
1800    * @param sb The string builder to append to.
1801    * @return The same string builder.
1802    */
1803   public StringBuilder appendShortName(StringBuilder sb) {
1804      Class<?> ct = getComponentType().inner();
1805      int dim = getDimensions();
1806      if (ct != null) {
1807         if (ct.isLocalClass())
1808            sb.append(of(ct.getEnclosingClass()).getSimpleName()).append('$').append(ct.getSimpleName());
1809         else if (ct.isMemberClass())
1810            sb.append(of(ct.getDeclaringClass()).getSimpleName()).append('$').append(ct.getSimpleName());
1811         else
1812            sb.append(ct.getSimpleName());
1813      } else {
1814         sb.append(t.getTypeName());
1815      }
1816      if (isParameterizedType) {
1817         ParameterizedType pt = (ParameterizedType)t;
1818         sb.append('<');
1819         boolean first = true;
1820         for (Type t2 : pt.getActualTypeArguments()) {
1821            if (! first)
1822               sb.append(',');
1823            first = false;
1824            of(t2).appendShortName(sb);
1825         }
1826         sb.append('>');
1827      }
1828      for (int i = 0; i < dim; i++)
1829         sb.append('[').append(']');
1830      return sb;
1831   }
1832
1833   /**
1834    * Returns the simple name of the underlying class.
1835    *
1836    * <p>
1837    * Returns either {@link Class#getSimpleName()} or {@link Type#getTypeName()} depending on whether
1838    * this is a class or type.
1839    *
1840    * @return The simple name of the underlying class;
1841    */
1842   public String getSimpleName() {
1843      return c != null ? c.getSimpleName() : t.getTypeName();
1844   }
1845
1846   /**
1847    * Returns the name of the underlying class.
1848    *
1849    * @return The name of the underlying class.
1850    */
1851   public String getName() {
1852      return c != null ? c.getName() : t.getTypeName();
1853   }
1854
1855   /**
1856    * Same as {@link #getSimpleName()} but uses <js>"Array"</js> instead of <js>"[]"</js>.
1857    *
1858    * @return The readable name for this class.
1859    */
1860   public String getReadableName() {
1861      if (c == null)
1862         return t.getTypeName();
1863      if (! c.isArray())
1864         return c.getSimpleName();
1865      Class<?> c = this.c;
1866      StringBuilder sb = new StringBuilder();
1867      while (c.isArray()) {
1868         sb.append("Array");
1869         c = c.getComponentType();
1870      }
1871      return c.getSimpleName() + sb;
1872   }
1873
1874   //-----------------------------------------------------------------------------------------------------------------
1875   // Hierarchy
1876   //-----------------------------------------------------------------------------------------------------------------
1877
1878   /**
1879    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
1880    *
1881    * @param child The child class.
1882    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
1883    */
1884   public boolean isParentOf(Class<?> child) {
1885      return c != null && child != null && c.isAssignableFrom(child);
1886   }
1887
1888   /**
1889    * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
1890    *
1891    * @param child The child class.
1892    * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
1893    */
1894   public boolean isParentOf(Type child) {
1895      if (child instanceof Class)
1896         return isParentOf((Class<?>)child);
1897      return false;
1898   }
1899
1900   /**
1901    * Returns <jk>true</jk> if this class is a child of <c>parent</c>.
1902    *
1903    * @param parent The parent class.
1904    * @return <jk>true</jk> if this class is a parent of <c>child</c>.
1905    */
1906   public boolean isStrictChildOf(Class<?> parent) {
1907      return c != null && parent != null && parent.isAssignableFrom(c) && ! c.equals(parent);
1908   }
1909
1910   /**
1911    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1912    *
1913    * @param parent The parent class.
1914    * @return <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1915    */
1916   public boolean isChildOf(Class<?> parent) {
1917      return c != null && parent != null && parent.isAssignableFrom(c);
1918   }
1919
1920   /**
1921    * Returns <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>.
1922    *
1923    * @param parents The parents class.
1924    * @return <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>.
1925    */
1926   public boolean isChildOfAny(Class<?>...parents) {
1927      for (Class<?> p : parents)
1928         if (isChildOf(p))
1929            return true;
1930      return false;
1931   }
1932
1933   /**
1934    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1935    *
1936    * @param parent The parent class.
1937    * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>.
1938    */
1939   public boolean isChildOf(Type parent) {
1940      if (parent instanceof Class)
1941         return isChildOf((Class<?>)parent);
1942      return false;
1943   }
1944
1945   /**
1946    * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>.
1947    *
1948    * @param parent The parent class.
1949    * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>.
1950    */
1951   public boolean isChildOf(ClassInfo parent) {
1952      return isChildOf(parent.inner());
1953   }
1954
1955   /**
1956    * Checks for equality with the specified class.
1957    *
1958    * @param c The class to check equality with.
1959    * @return <jk>true</jk> if the specified class is the same as this one.
1960    */
1961   public boolean is(Class<?> c) {
1962      return this.c != null && this.c.equals(c);
1963   }
1964
1965   /**
1966    * Checks for equality with the specified class.
1967    *
1968    * @param c The class to check equality with.
1969    * @return <jk>true</jk> if the specified class is the same as this one.
1970    */
1971   public boolean is(ClassInfo c) {
1972      if (this.c != null)
1973         return this.c.equals(c.inner());
1974      return t.equals(c.t);
1975   }
1976
1977   /**
1978    * Returns <jk>true</jk> if this class is any of the specified types.
1979    *
1980    * @param types The types to check against.
1981    * @return <jk>true</jk> if this class is any of the specified types.
1982    */
1983   public boolean isAny(Class<?>...types) {
1984      for (Class<?> cc : types)
1985         if (is(cc))
1986            return true;
1987      return false;
1988   }
1989
1990   /**
1991    * Returns the package of this class.
1992    *
1993    * @return The package of this class.
1994    */
1995   public Package getPackage() {
1996      return c == null ? null : c.getPackage();
1997   }
1998
1999   /**
2000    * Returns <jk>true</jk> if this class is not in the root package.
2001    *
2002    * @return <jk>true</jk> if this class is not in the root package.
2003    */
2004   public boolean hasPackage() {
2005      return getPackage() != null;
2006   }
2007
2008   /**
2009    * Returns the number of dimensions if this is an array type.
2010    *
2011    * @return The number of dimensions if this is an array type, or <c>0</c> if it is not.
2012    */
2013   public int getDimensions() {
2014      if (dim == -1) {
2015         int d = 0;
2016         Class<?> ct = c;
2017         while (ct != null && ct.isArray()) {
2018            d++;
2019            ct = ct.getComponentType();
2020         }
2021         this.dim = d;
2022         this.componentType = ct == c ? this : of(ct);
2023      }
2024      return dim;
2025   }
2026
2027   /**
2028    * Returns the base component type of this class if it's an array.
2029    *
2030    * @return The base component type of this class if it's an array, or this object if it's not.
2031    */
2032   public ClassInfo getComponentType() {
2033      if (componentType == null) {
2034         if (c == null)
2035            componentType = this;
2036         else
2037            getDimensions();
2038      }
2039      return componentType;
2040   }
2041
2042   /**
2043    * Returns <jk>true</jk> if this class is an enum.
2044    *
2045    * @return <jk>true</jk> if this class is an enum.
2046    */
2047   public boolean isEnum() {
2048      return c != null && c.isEnum();
2049   }
2050
2051   //-----------------------------------------------------------------------------------------------------------------
2052   // Instantiation
2053   //-----------------------------------------------------------------------------------------------------------------
2054
2055   /**
2056    * Shortcut for calling {@link Class#newInstance()} on the underlying class.
2057    *
2058    * @return A new instance of the underlying class
2059    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
2060    */
2061   public Object newInstance() throws ExecutableException {
2062      if (c == null)
2063         throw new ExecutableException("Type ''{0}'' cannot be instantiated", getFullName());
2064      try {
2065         return c.newInstance();
2066      } catch (InstantiationException | IllegalAccessException e) {
2067         throw new ExecutableException(e);
2068      }
2069   }
2070
2071   //-----------------------------------------------------------------------------------------------------------------
2072   // Parameter types
2073   //-----------------------------------------------------------------------------------------------------------------
2074
2075   /**
2076    * Finds the real parameter type of this class.
2077    *
2078    * @param index The zero-based index of the parameter to resolve.
2079    * @param pt The parameterized type class containing the parameterized type to resolve (e.g. <c>HashMap</c>).
2080    * @return The resolved real class.
2081    */
2082   public Class<?> getParameterType(int index, Class<?> pt) {
2083      if (pt == null)
2084         throw new FormattedIllegalArgumentException("Parameterized type cannot be null");
2085
2086      // We need to make up a mapping of type names.
2087      Map<Type,Type> typeMap = new HashMap<>();
2088      Class<?> cc = c;
2089      while (pt != cc.getSuperclass()) {
2090         extractTypes(typeMap, cc);
2091         cc = cc.getSuperclass();
2092         if (cc == null)
2093            throw new FormattedIllegalArgumentException("Class ''{0}'' is not a subclass of parameterized type ''{1}''", c.getSimpleName(), pt.getSimpleName());
2094      }
2095
2096      Type gsc = cc.getGenericSuperclass();
2097
2098      if (! (gsc instanceof ParameterizedType))
2099         throw new FormattedIllegalArgumentException("Class ''{0}'' is not a parameterized type", pt.getSimpleName());
2100
2101      ParameterizedType cpt = (ParameterizedType)gsc;
2102      Type[] atArgs = cpt.getActualTypeArguments();
2103      if (index >= atArgs.length)
2104         throw new FormattedIllegalArgumentException("Invalid type index. index={0}, argsLength={1}", index, atArgs.length);
2105      Type actualType = cpt.getActualTypeArguments()[index];
2106
2107      if (typeMap.containsKey(actualType))
2108         actualType = typeMap.get(actualType);
2109
2110      if (actualType instanceof Class) {
2111         return (Class<?>)actualType;
2112
2113      } else if (actualType instanceof GenericArrayType) {
2114         Type gct = ((GenericArrayType)actualType).getGenericComponentType();
2115         if (gct instanceof ParameterizedType)
2116            return Array.newInstance((Class<?>)((ParameterizedType)gct).getRawType(), 0).getClass();
2117      } else if (actualType instanceof TypeVariable) {
2118         TypeVariable<?> typeVariable = (TypeVariable<?>)actualType;
2119         List<Class<?>> nestedOuterTypes = new LinkedList<>();
2120         for (Class<?> ec = cc.getEnclosingClass(); ec != null; ec = ec.getEnclosingClass()) {
2121            Class<?> outerClass = cc.getClass();
2122            nestedOuterTypes.add(outerClass);
2123            Map<Type,Type> outerTypeMap = new HashMap<>();
2124            extractTypes(outerTypeMap, outerClass);
2125            for (Map.Entry<Type,Type> entry : outerTypeMap.entrySet()) {
2126               Type key = entry.getKey(), value = entry.getValue();
2127               if (key instanceof TypeVariable) {
2128                  TypeVariable<?> keyType = (TypeVariable<?>)key;
2129                  if (keyType.getName().equals(typeVariable.getName()) && isInnerClass(keyType.getGenericDeclaration(), typeVariable.getGenericDeclaration())) {
2130                     if (value instanceof Class)
2131                        return (Class<?>)value;
2132                     typeVariable = (TypeVariable<?>)entry.getValue();
2133                  }
2134               }
2135            }
2136         }
2137      } else if (actualType instanceof ParameterizedType) {
2138         return (Class<?>)((ParameterizedType)actualType).getRawType();
2139      }
2140      throw new FormattedIllegalArgumentException("Could not resolve variable ''{0}'' to a type.", actualType.getTypeName());
2141   }
2142
2143   private static boolean isInnerClass(GenericDeclaration od, GenericDeclaration id) {
2144      if (od instanceof Class && id instanceof Class) {
2145         Class<?> oc = (Class<?>)od;
2146         Class<?> ic = (Class<?>)id;
2147         while ((ic = ic.getEnclosingClass()) != null)
2148            if (ic == oc)
2149               return true;
2150      }
2151      return false;
2152   }
2153
2154   private static void extractTypes(Map<Type,Type> typeMap, Class<?> c) {
2155      Type gs = c.getGenericSuperclass();
2156      if (gs instanceof ParameterizedType) {
2157         ParameterizedType pt = (ParameterizedType)gs;
2158         Type[] typeParameters = ((Class<?>)pt.getRawType()).getTypeParameters();
2159         Type[] actualTypeArguments = pt.getActualTypeArguments();
2160         for (int i = 0; i < typeParameters.length; i++) {
2161            if (typeMap.containsKey(actualTypeArguments[i]))
2162               actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]);
2163            typeMap.put(typeParameters[i], actualTypeArguments[i]);
2164         }
2165      }
2166   }
2167
2168   //-----------------------------------------------------------------------------------------------------------------
2169   // Other
2170   //-----------------------------------------------------------------------------------------------------------------
2171
2172   @Override
2173   public String toString() {
2174      return t.toString();
2175   }
2176
2177   @Override
2178   public int hashCode() {
2179      return t.hashCode();
2180   }
2181
2182   @Override
2183   public boolean equals(Object o) {
2184      return o == null ? false : ((ClassInfo)o).t.equals(t);
2185   }
2186}