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