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