| // *************************************************************************************************************************** |
| // * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * |
| // * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * |
| // * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * |
| // * with the License. You may obtain a copy of the License at * |
| // * * |
| // * http://www.apache.org/licenses/LICENSE-2.0 * |
| // * * |
| // * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * |
| // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * |
| // * specific language governing permissions and limitations under the License. * |
| // *************************************************************************************************************************** |
| package org.apache.juneau.reflect; |
| |
| import static org.apache.juneau.internal.StringUtils.*; |
| import static org.apache.juneau.reflect.ReflectFlags.*; |
| import static org.apache.juneau.internal.CollectionUtils.*; |
| |
| import java.lang.annotation.*; |
| import java.lang.reflect.*; |
| import java.util.*; |
| import java.util.concurrent.*; |
| import java.util.function.*; |
| |
| import org.apache.juneau.*; |
| import org.apache.juneau.annotation.*; |
| import org.apache.juneau.internal.*; |
| import org.apache.juneau.utils.*; |
| |
| /** |
| * Lightweight utility class for introspecting information about a class. |
| * |
| * <p> |
| * Provides various convenience methods for introspecting fields/methods/annotations |
| * that aren't provided by the standard Java reflection APIs. |
| * |
| * <p> |
| * Objects are designed to be lightweight to create and threadsafe. |
| * |
| * <h5 class='figure'>Example:</h5> |
| * <p class='bpcode w800'> |
| * <jc>// Wrap our class inside a ClassInfo.</jc> |
| * ClassInfo ci = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>); |
| * |
| * <jc>// Get all methods in parent-to-child order, sorted alphabetically per class.</jc> |
| * <jk>for</jk> (MethodInfo mi : ci.getAllMethodInfos(<jk>true</jk>, <jk>true</jk>)) { |
| * <jc>// Do something with it.</jc> |
| * } |
| * |
| * <jc>// Get all class-level annotations in parent-to-child order.</jc> |
| * <jk>for</jk> (MyAnnotation a : ci.getAnnotations(MyAnnotation.<jk>class</jk>, <jk>true</jk>)) { |
| * // Do something with it. |
| * } |
| * </p> |
| */ |
| @BeanIgnore |
| public final class ClassInfo { |
| |
| private final Type t; |
| final Class<?> c; |
| private ClassInfo proxyFor; |
| private final boolean isParameterizedType; |
| private List<ClassInfo> interfaces, declaredInterfaces, parents, allParents; |
| private List<MethodInfo> publicMethods, declaredMethods, allMethods, allMethodsParentFirst; |
| private List<ConstructorInfo> publicConstructors, declaredConstructors; |
| private List<FieldInfo> publicFields, declaredFields, allFields, allFieldsParentFirst; |
| private Map<Class<?>,Optional<Annotation>> annotationMap, declaredAnnotationMap; |
| private int dim = -1; |
| private ClassInfo componentType; |
| |
| private static final Map<Class<?>,ClassInfo> CACHE = new ConcurrentHashMap<>(); |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Instantiation. |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @param c The class type. |
| * @param t The generic type (if parameterized type). |
| * @param proxyFor If the class is a CGLIB proxy, this is the underlying wrapped class. |
| */ |
| protected ClassInfo(Class<?> c, Type t, Class<?> proxyFor) { |
| this.t = t; |
| this.c = c; |
| this.proxyFor = proxyFor == null ? null : ClassInfo.of(proxyFor); |
| this.isParameterizedType = t == null ? false : (t instanceof ParameterizedType); |
| } |
| |
| /** |
| * Returns a class info wrapper around the specified class type. |
| * |
| * @param t The class type. |
| * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. |
| */ |
| public static ClassInfo of(Type t) { |
| if (t == null) |
| return null; |
| return new ClassInfo(ClassUtils.toClass(t), t, null); |
| } |
| |
| /** |
| * Returns a class info wrapper around the specified class type. |
| * |
| * @param c The class type. |
| * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. |
| */ |
| public static ClassInfo of(Class<?> c) { |
| if (c == null) |
| return null; |
| return new ClassInfo(c, c, null); |
| } |
| |
| /** |
| * Same as {@link #of(Class)}} but caches the result for faster future lookup. |
| * |
| * @param c The class type. |
| * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. |
| */ |
| public static ClassInfo ofc(Class<?> c) { |
| if (c == null) |
| return null; |
| ClassInfo ci = CACHE.get(c); |
| if (ci == null) { |
| ci = ClassInfo.of(c); |
| CACHE.put(c, ci); |
| } |
| return ci; |
| } |
| |
| /** |
| * Returns a class info wrapper around the specified class type. |
| * |
| * @param c The class type. |
| * @param t The generic type (if parameterized type). |
| * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. |
| */ |
| public static ClassInfo of(Class<?> c, Type t) { |
| return new ClassInfo(c, t, null); |
| } |
| |
| /** |
| * Same as using the constructor, but operates on an object instance. |
| * |
| * @param o The class instance. |
| * @return The constructed class info, or <jk>null</jk> if the object was <jk>null</jk>. |
| */ |
| public static ClassInfo of(Object o) { |
| if (o == null) |
| return null; |
| return new ClassInfo(o.getClass(), o.getClass(), getProxyFor(o)); |
| } |
| |
| /** |
| * Same as {@link #of(Object)}} but caches the result for faster future lookup. |
| * |
| * @param o The class instance. |
| * @return The constructed class info, or <jk>null</jk> if the type was <jk>null</jk>. |
| */ |
| public static ClassInfo ofc(Object o) { |
| if (o == null) |
| return null; |
| Class<?> c = o.getClass(); |
| ClassInfo ci = CACHE.get(c); |
| if (ci == null) { |
| ci = ClassInfo.of(o); |
| CACHE.put(c, ci); |
| } |
| return ci; |
| } |
| |
| /** |
| * When this metadata is against a CGLIB proxy, this method finds the underlying "real" class. |
| * |
| * @param o The class instance. |
| * @return The non-proxy class, or <jk>null</jk> if it's not a CGLIB proxy. |
| */ |
| private static Class<?> getProxyFor(Object o) { |
| Class<?> c = o.getClass(); |
| String s = c.getName(); |
| if (s.indexOf('$') == -1 || ! s.contains("$$EnhancerBySpringCGLIB$$")) |
| return null; |
| for (Method m : c.getMethods()) { |
| if (m.getName().equals("getTargetClass") && m.getParameterCount() == 0 && m.getReturnType().equals(Class.class)) { |
| try { |
| return (Class<?>) m.invoke(o); |
| } catch (Exception e) {} |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the wrapped class as a {@link Type}. |
| * |
| * @return The wrapped class as a {@link Type}. |
| */ |
| public Type innerType() { |
| return t; |
| } |
| |
| /** |
| * Returns the wrapped class as a {@link Class}. |
| * |
| * @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}). |
| */ |
| @SuppressWarnings("unchecked") |
| public <T> Class<T> inner() { |
| return (Class<T>)c; |
| } |
| |
| /** |
| * If this class is a parameterized {@link Value} type, returns the parameterized type. |
| * |
| * @return The parameterized type, or this object if this class is not a parameterized {@link Value} type. |
| */ |
| public ClassInfo resolved() { |
| if (Value.isType(t)) |
| return of(Value.getParameterType(t)); |
| return this; |
| } |
| |
| /** |
| * Identifies the inner target class when this class info represents a CGLIB proxy class. |
| * |
| * @param proxyFor The inner non-proxied class. |
| * @return This object (for method chaining). |
| */ |
| public ClassInfo proxyFor(Class<?> proxyFor) { |
| this.proxyFor = ClassInfo.of(proxyFor); |
| return this; |
| } |
| |
| /** |
| * Returns the non-proxied inner class of a CGLIB proxy class. |
| * |
| * @return The non-proxied inner class of a CGLIB proxy class, or the inner class if it's not a proxy. |
| */ |
| public Class<?> getProxiedClass() { |
| return proxyFor == null ? c : proxyFor.inner(); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Parent classes and interfaces. |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns the parent class. |
| * |
| * @return |
| * The parent class, or <jk>null</jk> if the class has no parent. |
| */ |
| public ClassInfo getParent() { |
| return c == null ? null : of(c.getSuperclass()); |
| } |
| |
| /** |
| * Returns a list of interfaces declared on this class. |
| * |
| * <p> |
| * Does not include interfaces declared on parent classes. |
| * |
| * @return |
| * An unmodifiable list of interfaces declared on this class. |
| * <br>Results are in the same order as {@link Class#getInterfaces()}. |
| */ |
| public List<ClassInfo> getDeclaredInterfaces() { |
| if (declaredInterfaces == null) { |
| Class<?>[] ii = c == null ? new Class[0] : c.getInterfaces(); |
| List<ClassInfo> l = new ArrayList<>(ii.length); |
| for (Class<?> i : ii) |
| l.add(of(i)); |
| declaredInterfaces = unmodifiableList(l); |
| } |
| return declaredInterfaces; |
| } |
| |
| /** |
| * Returns a list of interfaces defined on this class and superclasses. |
| * |
| * @return |
| * An unmodifiable list of interfaces defined on this class and superclasses. |
| * <br>Results are in child-to-parent order. |
| */ |
| public List<ClassInfo> getInterfaces() { |
| if (interfaces == null) { |
| Set<ClassInfo> s = new LinkedHashSet<>(); |
| for (ClassInfo ci : getParents()) |
| for (ClassInfo ci2 : ci.getDeclaredInterfaces()) { |
| s.add(ci2); |
| for (ClassInfo ci3 : ci2.getInterfaces()) |
| s.add(ci3); |
| } |
| interfaces = unmodifiableList(new ArrayList<>(s)); |
| } |
| return interfaces; |
| } |
| |
| /** |
| * Returns a list of interfaces defined on this class and superclasses. |
| * |
| * @return |
| * An unmodifiable list of interfaces defined on this class and superclasses. |
| * <br>Results are in parent-to-child order. |
| */ |
| public Iterable<ClassInfo> getInterfacesParentFirst() { |
| return iterable(getInterfaces(), true); |
| } |
| |
| /** |
| * Returns a list including this class and all parent classes. |
| * |
| * <p> |
| * Does not include interfaces. |
| * |
| * @return An unmodifiable list including this class and all parent classes. |
| * <br>Results are in child-to-parent order. |
| */ |
| public List<ClassInfo> getParents() { |
| if (parents == null) { |
| List<ClassInfo> l = new ArrayList<>(); |
| Class<?> pc = c; |
| while (pc != null && pc != Object.class) { |
| l.add(of(pc)); |
| pc = pc.getSuperclass(); |
| } |
| parents = Collections.unmodifiableList(l); |
| } |
| return parents; |
| } |
| |
| /** |
| * Returns a list including this class and all parent classes. |
| * |
| * <p> |
| * Does not include interfaces. |
| * |
| * @return An unmodifiable list including this class and all parent classes. |
| * <br>Results are in parent-to-child order. |
| */ |
| public Iterable<ClassInfo> getParentsParentFirst() { |
| return iterable(getParents(), true); |
| } |
| |
| /** |
| * Returns a list including this class and all parent classes and interfaces. |
| * |
| * @return An unmodifiable list including this class and all parent classes. |
| * <br>Results are ordered child-to-parent order with classes listed before interfaces. |
| */ |
| public List<ClassInfo> getAllParents() { |
| if (allParents == null) { |
| List<ClassInfo> l = new ArrayList<>(); |
| l.addAll(getParents()); |
| l.addAll(getInterfaces()); |
| allParents = Collections.unmodifiableList(l); |
| } |
| return allParents; |
| } |
| |
| /** |
| * Returns a list including this class and all parent classes and interfaces. |
| * |
| * @return An unmodifiable list including this class and all parent classes. |
| * <br>Results are ordered parent-to-child order with interfaces listed before classes. |
| */ |
| public Iterable<ClassInfo> getAllParentsParentFirst() { |
| return iterable(getAllParents(), true); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Methods |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns all public methods on this class. |
| * |
| * <p> |
| * Methods defined on the {@link Object} class are excluded from the results. |
| * |
| * @return |
| * All public methods on this class. |
| * <br>Results are ordered alphabetically. |
| */ |
| public List<MethodInfo> getPublicMethods() { |
| if (publicMethods == null) { |
| Method[] mm = c == null ? new Method[0] : c.getMethods(); |
| List<MethodInfo> l = new ArrayList<>(mm.length); |
| for (Method m : mm) |
| if (m.getDeclaringClass() != Object.class) |
| l.add(MethodInfo.of(this, m, getProxyTarget(m))); |
| l.sort(null); |
| publicMethods = Collections.unmodifiableList(l); |
| } |
| return publicMethods; |
| } |
| |
| private Method getProxyTarget(Method m) { |
| if (proxyFor != null) { |
| MethodInfo m2 = proxyFor.getMethod(m.getName(), m.getParameterTypes()); |
| if (m2 != null) |
| return m2.inner(); |
| } |
| return m; |
| } |
| |
| private Constructor<?> getProxyTarget(Constructor<?> c) { |
| if (proxyFor != null) { |
| ConstructorInfo c2 = proxyFor.getConstructor(Visibility.PRIVATE, c.getParameterTypes()); |
| if (c2 != null) |
| return c2.inner(); |
| } |
| return c; |
| } |
| |
| /** |
| * Returns the public method with the specified method name and argument types. |
| * |
| * @param name The method name (e.g. <js>"toString"</js>). |
| * @param args The exact argument types. |
| * @return |
| * The public method with the specified method name and argument types, or <jk>null</jk> if not found. |
| */ |
| public MethodInfo getPublicMethod(String name, Class<?>...args) { |
| for (MethodInfo mi : getPublicMethods()) |
| if (mi.hasName(name) && mi.hasParamTypes(args)) |
| return mi; |
| return null; |
| } |
| |
| /** |
| * Returns the method with the specified method name and argument types. |
| * |
| * @param name The method name (e.g. <js>"toString"</js>). |
| * @param args The exact argument types. |
| * @return |
| * The method with the specified method name and argument types, or <jk>null</jk> if not found. |
| */ |
| public MethodInfo getMethod(String name, Class<?>...args) { |
| for (MethodInfo mi : getAllMethods()) |
| if (mi.hasName(name) && mi.hasParamTypes(args)) |
| return mi; |
| return null; |
| } |
| |
| /** |
| * Returns all methods declared on this class. |
| * |
| * @return |
| * All methods declared on this class. |
| * <br>Results are ordered alphabetically. |
| */ |
| public List<MethodInfo> getDeclaredMethods() { |
| if (declaredMethods == null) { |
| Method[] mm = c == null ? new Method[0] : c.getDeclaredMethods(); |
| List<MethodInfo> l = new ArrayList<>(mm.length); |
| for (Method m : mm) |
| if (! "$jacocoInit".equals(m.getName())) // Jacoco adds its own simulated methods. |
| l.add(MethodInfo.of(this, m, getProxyTarget(m))); |
| l.sort(null); |
| declaredMethods = Collections.unmodifiableList(l); |
| } |
| return declaredMethods; |
| } |
| |
| /** |
| * Returns all declared methods on this class and all parent classes. |
| * |
| * @return |
| * All declared methods on this class and all parent classes. |
| * <br>Results are ordered child-to-parent, and then alphabetically per class. |
| */ |
| public List<MethodInfo> getAllMethods() { |
| if (allMethods == null) { |
| List<MethodInfo> l = new ArrayList<>(); |
| for (ClassInfo c : getAllParents()) |
| c.appendDeclaredMethods(l); |
| allMethods = Collections.unmodifiableList(l); |
| } |
| return allMethods; |
| } |
| |
| /** |
| * Returns all declared methods on this class and all parent classes. |
| * |
| * |
| * @return |
| * All declared methods on this class and all parent classes. |
| * <br>Results are ordered parent-to-child, and then alphabetically per class. |
| */ |
| public List<MethodInfo> getAllMethodsParentFirst() { |
| if (allMethodsParentFirst == null) { |
| List<MethodInfo> l = new ArrayList<>(); |
| for (ClassInfo c : getAllParentsParentFirst()) |
| c.appendDeclaredMethods(l); |
| allMethodsParentFirst = Collections.unmodifiableList(l); |
| } |
| return allMethodsParentFirst; |
| } |
| |
| private List<MethodInfo> appendDeclaredMethods(List<MethodInfo> l) { |
| l.addAll(getDeclaredMethods()); |
| return l; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Special methods |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Find the public static creator method on this class. |
| * |
| * <p> |
| * Looks for the following method names: |
| * <ul> |
| * <li><c>create</c> |
| * <li><c>from</c> |
| * <li><c>fromValue</c> |
| * <li><c>parse</c> |
| * <li><c>valueOf</c> |
| * <li><c>fromX</c> |
| * <li><c>forX</c> |
| * <li><c>parseX</c> |
| * </ul> |
| * |
| * @param ic The argument type. |
| * @param additionalNames Additional method names to check for. |
| * @return The static method, or <jk>null</jk> if it couldn't be found. |
| */ |
| public MethodInfo getStaticCreateMethod(Class<?> ic, String...additionalNames) { |
| if (c != null) { |
| for (MethodInfo m : getPublicMethods()) { |
| if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && m.hasReturnType(c) && m.hasParamTypes(ic)) { |
| String n = m.getSimpleName(), cn = ic.getSimpleName(); |
| if ( |
| isOneOf(n, "create","from","fromValue","parse","valueOf") |
| || isOneOf(n, additionalNames) |
| || (n.startsWith("from") && n.substring(4).equals(cn)) |
| || (n.startsWith("for") && n.substring(3).equals(cn)) |
| || (n.startsWith("parse") && n.substring(5).equals(cn)) |
| ) { |
| return m; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Find the public static method with the specified name and args. |
| * |
| * @param name The method name. |
| * @param rt The method return type. |
| * @param args The method arguments |
| * @return The method, or <jk>null</jk> if it couldn't be found. |
| */ |
| public MethodInfo getStaticPublicMethod(String name, Class<?> rt, Class<?>...args) { |
| if (c != null) |
| for (MethodInfo m : getPublicMethods()) |
| if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && name.equals(m.getSimpleName()) && m.hasReturnType(rt) && m.hasParamTypes(args)) |
| return m; |
| return null; |
| } |
| |
| /** |
| * Find the public static method with the specified name and args. |
| * |
| * @param name The method name. |
| * @param rt The method return type. |
| * @param args The method arguments |
| * @return The method, or <jk>null</jk> if it couldn't be found. |
| */ |
| public Method getStaticPublicMethodInner(String name, Class<?> rt, Class<?>...args) { |
| MethodInfo mi = getStaticPublicMethod(name, rt, args); |
| return mi == null ? null : mi.inner(); |
| } |
| |
| /** |
| * Returns the <c>public static Builder create()</c> method on this class. |
| * |
| * @return The <c>public static Builder create()</c> method on this class, or <jk>null</jk> if it doesn't exist. |
| */ |
| public MethodInfo getBuilderCreateMethod() { |
| for (MethodInfo m : getDeclaredMethods()) |
| if (m.isAll(PUBLIC, STATIC) && m.hasName("create") && (!m.hasReturnType(void.class))) |
| return m; |
| return null; |
| } |
| |
| /** |
| * Returns the <c>T build()</c> method on this class. |
| * |
| * @return The <c>T build()</c> method on this class, or <jk>null</jk> if it doesn't exist. |
| */ |
| public MethodInfo getBuilderBuildMethod() { |
| for (MethodInfo m : getDeclaredMethods()) |
| if (m.isAll(NOT_STATIC) && m.hasName("build") && (!m.hasParams()) && (!m.hasReturnType(void.class))) |
| return m; |
| return null; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Constructors |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns all the public constructors defined on this class. |
| * |
| * @return All public constructors defined on this class. |
| */ |
| public List<ConstructorInfo> getPublicConstructors() { |
| if (publicConstructors == null) { |
| Constructor<?>[] cc = c == null ? new Constructor[0] : c.getConstructors(); |
| List<ConstructorInfo> l = new ArrayList<>(cc.length); |
| for (Constructor<?> ccc : cc) |
| l.add(ConstructorInfo.of(this, ccc, getProxyTarget(ccc))); |
| l.sort(null); |
| publicConstructors = Collections.unmodifiableList(l); |
| } |
| return publicConstructors; |
| } |
| |
| /** |
| * Returns the public constructor with the specified argument types. |
| * |
| * @param args The exact argument types. |
| * @return |
| * The public constructor with the specified argument types, or <jk>null</jk> if not found. |
| */ |
| public ConstructorInfo getPublicConstructor(Class<?>...args) { |
| for (ConstructorInfo ci : getPublicConstructors()) |
| if (ci.hasParamTypes(args)) |
| return ci; |
| return null; |
| } |
| |
| /** |
| * Returns the declared constructor with the specified argument types. |
| * |
| * @param args The exact argument types. |
| * @return |
| * The declared constructor with the specified argument types, or <jk>null</jk> if not found. |
| */ |
| public ConstructorInfo getDeclaredConstructor(Class<?>...args) { |
| for (ConstructorInfo ci : getDeclaredConstructors()) |
| if (ci.hasParamTypes(args)) |
| return ci; |
| return null; |
| } |
| |
| /** |
| * Same as {@link #getPublicConstructor(Class...)} but allows for inexact arg type matching. |
| * |
| * <p> |
| * For example, the method <c>foo(CharSequence)</c> will be matched by <code>getAvailablePublicConstructor(String.<jk>class</jk>)</code> |
| * |
| * @param args The exact argument types. |
| * @return |
| * The public constructor with the specified argument types, or <jk>null</jk> if not found. |
| */ |
| public ConstructorInfo getAvailablePublicConstructor(Class<?>...args) { |
| return getConstructor(Visibility.PUBLIC, false, args); |
| } |
| |
| /** |
| * Returns all the constructors defined on this class. |
| * |
| * @return All constructors defined on this class. |
| */ |
| public List<ConstructorInfo> getDeclaredConstructors() { |
| if (declaredConstructors == null) { |
| Constructor<?>[] cc = c == null ? new Constructor[0] : c.getDeclaredConstructors(); |
| List<ConstructorInfo> l = new ArrayList<>(cc.length); |
| for (Constructor<?> ccc : cc) |
| l.add(ConstructorInfo.of(this, ccc, getProxyTarget(ccc))); |
| l.sort(null); |
| declaredConstructors = Collections.unmodifiableList(l); |
| } |
| return declaredConstructors; |
| } |
| |
| /** |
| * Finds the public constructor that can take in the specified arguments. |
| * |
| * @param args The arguments we want to pass into the constructor. |
| * @return |
| * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified |
| * arguments. |
| */ |
| public ConstructorInfo getPublicConstructor(Object...args) { |
| return getPublicConstructor(ClassUtils.getClasses(args)); |
| } |
| |
| /** |
| * Finds the public constructor that can take in the specified arguments using fuzzy-arg matching. |
| * |
| * @param args The arguments we want to pass into the constructor. |
| * @return |
| * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified |
| * arguments. |
| */ |
| public ConstructorInfo getPublicConstructorFuzzy(Object...args) { |
| return getConstructor(Visibility.PUBLIC, true, ClassUtils.getClasses(args)); |
| } |
| |
| /** |
| * Finds a constructor with the specified parameters without throwing an exception. |
| * |
| * @param vis The minimum visibility. |
| * @param argTypes |
| * The argument types in the constructor. |
| * Can be subtypes of the actual constructor argument types. |
| * @return The matching constructor, or <jk>null</jk> if constructor could not be found. |
| */ |
| public ConstructorInfo getConstructor(Visibility vis, Class<?>...argTypes) { |
| return getConstructor(vis, false, argTypes); |
| } |
| |
| private ConstructorInfo getConstructor(Visibility vis, boolean fuzzyArgs, Class<?>...argTypes) { |
| if (fuzzyArgs) { |
| int bestCount = -1; |
| ConstructorInfo bestMatch = null; |
| for (ConstructorInfo n : getDeclaredConstructors()) { |
| if (vis.isVisible(n.inner())) { |
| int m = ClassUtils.fuzzyArgsMatch(n.getParamTypes(), argTypes); |
| if (m > bestCount) { |
| bestCount = m; |
| bestMatch = n; |
| } |
| } |
| } |
| return bestMatch; |
| } |
| |
| boolean isMemberClass = isNonStaticMemberClass(); |
| for (ConstructorInfo n : getDeclaredConstructors()) { |
| List<ClassInfo> paramTypes = n.getParamTypes(); |
| if (isMemberClass) |
| paramTypes = paramTypes.subList(1, paramTypes.size()); |
| if (ClassUtils.argsMatch(paramTypes, argTypes) && vis.isVisible(n.inner())) |
| return n; |
| } |
| |
| return null; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Special constructors |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Locates the no-arg constructor for this class. |
| * |
| * <p> |
| * Constructor must match the visibility requirements specified by parameter 'v'. |
| * If class is abstract, always returns <jk>null</jk>. |
| * Note that this also returns the 1-arg constructor for non-static member classes. |
| * |
| * @param v The minimum visibility. |
| * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility. |
| */ |
| public ConstructorInfo getNoArgConstructor(Visibility v) { |
| if (isAbstract()) |
| return null; |
| boolean isMemberClass = isNonStaticMemberClass(); |
| for (ConstructorInfo cc : getDeclaredConstructors()) |
| if (cc.hasNumParams(isMemberClass ? 1 : 0) && cc.isVisible(v)) |
| return cc.makeAccessible(v); |
| return null; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Fields |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns all public fields on this class. |
| * |
| * <p> |
| * Hidden fields are excluded from the results. |
| * |
| * @return |
| * All public fields on this class. |
| * <br>Results are in alphabetical order. |
| */ |
| public List<FieldInfo> getPublicFields() { |
| if (publicFields == null) { |
| Map<String,FieldInfo> m = new LinkedHashMap<>(); |
| for (ClassInfo c : getParents()) |
| c.appendDeclaredPublicFields(m); |
| List<FieldInfo> l = new ArrayList<>(m.values()); |
| l.sort(null); |
| publicFields = Collections.unmodifiableList(l); |
| } |
| return publicFields; |
| } |
| |
| /** |
| * Returns all declared fields on this class. |
| * |
| * @return |
| * All declared fields on this class. |
| * <br>Results are in alphabetical order. |
| */ |
| public List<FieldInfo> getDeclaredFields() { |
| if (declaredFields == null) { |
| Field[] ff = c == null ? new Field[0] : c.getDeclaredFields(); |
| List<FieldInfo> l = new ArrayList<>(ff.length); |
| for (Field f : ff) |
| if (! "$jacocoData".equals(f.getName())) |
| l.add(FieldInfo.of(this, f)); |
| l.sort(null); |
| declaredFields = Collections.unmodifiableList(l); |
| } |
| return declaredFields; |
| } |
| |
| /** |
| * Returns all declared fields on this class and all parent classes. |
| * |
| * @return |
| * All declared fields on this class. |
| * <br>Results are ordered child-to-parent, and then alphabetical per class. |
| */ |
| public List<FieldInfo> getAllFields() { |
| if (allFields == null) { |
| List<FieldInfo> l = new ArrayList<>(); |
| for (ClassInfo c : getAllParents()) |
| c.appendDeclaredFields(l); |
| allFields = Collections.unmodifiableList(l); |
| } |
| return allFields; |
| } |
| |
| /** |
| * Returns all declared fields on this class and all parent classes. |
| * |
| * @return |
| * All declared fields on this class. |
| * <br>Results are ordered parent-to-child, and then alphabetical per class. |
| */ |
| public List<FieldInfo> getAllFieldsParentFirst() { |
| if (allFieldsParentFirst == null) { |
| List<FieldInfo> l = new ArrayList<>(); |
| for (ClassInfo c : getAllParentsParentFirst()) |
| c.appendDeclaredFields(l); |
| allFieldsParentFirst = Collections.unmodifiableList(l); |
| } |
| return allFieldsParentFirst; |
| } |
| |
| private List<FieldInfo> appendDeclaredFields(List<FieldInfo> l) { |
| l.addAll(getDeclaredFields()); |
| return l; |
| } |
| |
| private Map<String,FieldInfo> appendDeclaredPublicFields(Map<String,FieldInfo> m) { |
| for (FieldInfo f : getDeclaredFields()) { |
| String fn = f.getName(); |
| if (f.isPublic() && ! (m.containsKey(fn) || "$jacocoData".equals(fn))) |
| m.put(f.getName(), f); |
| } |
| return m; |
| } |
| |
| /** |
| * Returns the public field with the specified name. |
| * |
| * @param name The field name. |
| * @return The public field, or <jk>null</jk> if not found. |
| */ |
| public FieldInfo getPublicField(String name) { |
| for (FieldInfo f : getPublicFields()) |
| if (f.getName().equals(name)) |
| return f; |
| return null; |
| } |
| |
| /** |
| * Returns the declared field with the specified name. |
| * |
| * @param name The field name. |
| * @return The declared field, or <jk>null</jk> if not found. |
| */ |
| public FieldInfo getDeclaredField(String name) { |
| for (FieldInfo f : getDeclaredFields()) |
| if (f.getName().equals(name)) |
| return f; |
| return null; |
| } |
| |
| /** |
| * Returns the static public field with the specified name. |
| * |
| * @param name The field name. |
| * @return The public field, or <jk>null</jk> if not found. |
| */ |
| public FieldInfo getStaticPublicField(String name) { |
| for (FieldInfo f : getPublicFields()) |
| if (f.isStatic() && f.getName().equals(name)) |
| return f; |
| return null; |
| } |
| |
| /** |
| * Returns the static public field with the specified name. |
| * |
| * @param name The field name. |
| * @return The public field, or <jk>null</jk> if not found. |
| */ |
| public Field getStaticPublicFieldInner(String name) { |
| for (FieldInfo f : getPublicFields()) |
| if (f.isStatic() && f.getName().equals(name)) |
| return f.inner(); |
| return null; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Annotations |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Finds the annotation of the specified type defined on this class or parent class/interface. |
| * |
| * <p> |
| * If the annotation cannot be found on the immediate class, searches methods with the same |
| * signature on the parent classes or interfaces. |
| * <br>The search is performed in child-to-parent order. |
| * |
| * @param a |
| * The annotation to search for. |
| * @return |
| * The annotation if found, or <jk>null</jk> if not. |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends Annotation> T getAnnotation(Class<T> a) { |
| if (a == null) |
| return null; |
| Optional<Annotation> o = annotationMap().get(a); |
| if (o == null) { |
| o = Optional.ofNullable(findAnnotation(a)); |
| annotationMap().put(a, o); |
| } |
| return o.isPresent() ? (T)o.get() : null; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class has the specified annotation. |
| * |
| * @param a |
| * The annotation to search for. |
| * @return |
| * The <jk>true</jk> if annotation if found. |
| */ |
| public boolean hasAnnotation(Class<? extends Annotation> a) { |
| return getAnnotation(a) != null; |
| } |
| |
| /** |
| * Returns the specified annotation only if it's been declared on this class. |
| * |
| * <p> |
| * More efficient than calling {@link Class#getAnnotation(Class)} since it doesn't recursively look for the class |
| * up the parent chain. |
| * |
| * @param <T> The annotation class type. |
| * @param a The annotation class. |
| * @return The annotation, or <jk>null</jk> if not found. |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends Annotation> T getDeclaredAnnotation(Class<T> a) { |
| if (a == null) |
| return null; |
| Optional<Annotation> o = declaredAnnotationMap().get(a); |
| if (o == null) { |
| o = Optional.ofNullable(findDeclaredAnnotation(a)); |
| declaredAnnotationMap().put(a, o); |
| } |
| return o.isPresent() ? (T)o.get() : null; |
| } |
| |
| /** |
| * Returns the specified annotation only if it's been declared on the package of this class. |
| * |
| * @param <T> The annotation class type. |
| * @param a The annotation class. |
| * @return The annotation, or <jk>null</jk> if not found. |
| */ |
| public <T extends Annotation> T getPackageAnnotation(Class<T> a) { |
| Package p = c == null ? null : c.getPackage(); |
| return (p == null ? null : p.getAnnotation(a)); |
| } |
| |
| /** |
| * Same as {@link #getDeclaredAnnotation(Class)} but returns the annotation wrapped in a {@link AnnotationInfo}. |
| * |
| * @param a The annotation to search for. |
| * @return The annotation if found, or <jk>null</jk> if not. |
| */ |
| public <T extends Annotation> AnnotationInfo<T> getDeclaredAnnotationInfo(Class<T> a) { |
| T ca = getDeclaredAnnotation(a); |
| return ca == null ? null : AnnotationInfo.of(this, ca); |
| } |
| |
| /** |
| * Same as {@link #getPackageAnnotation(Class)} but returns the annotation wrapped in a {@link AnnotationInfo}. |
| * |
| * @param a The annotation to search for. |
| * @return The annotation if found, or <jk>null</jk> if not. |
| */ |
| public <T extends Annotation> AnnotationInfo<T> getPackageAnnotationInfo(Class<T> a) { |
| T ca = getPackageAnnotation(a); |
| return ca == null ? null : AnnotationInfo.of(getPackage(), ca); |
| } |
| |
| /** |
| * Returns all annotations of the specified type defined on the specified class or parent classes/interfaces. |
| * |
| * @param a |
| * The annotation to search for. |
| * @return |
| * A list of all matching annotations found in child-to-parent order, or an empty list if none found. |
| */ |
| public <T extends Annotation> List<T> getAnnotations(Class<T> a) { |
| return appendAnnotations(new ArrayList<>(), a); |
| } |
| |
| /** |
| * Identical to {@link #getAnnotations(Class)} but optionally returns the list in reverse (parent-to-child) order. |
| * |
| * @param a |
| * The annotation to search for. |
| * @return |
| * A list of all matching annotations found or an empty list if none found. |
| */ |
| public <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a) { |
| return appendAnnotationsParentFirst(new ArrayList<>(), a); |
| } |
| |
| /** |
| * Same as getAnnotations(Class) except returns the annotations with the accompanying class. |
| * |
| * <p> |
| * Results are ordered child-to-parent. |
| * |
| * @param <T> The annotation class type. |
| * @param a The annotation class type. |
| * @return The found matches, or an empty list if annotation was not found. |
| */ |
| public <T extends Annotation> List<AnnotationInfo<T>> getAnnotationInfos(Class<T> a) { |
| return appendAnnotationInfos(new ArrayList<>(), a); |
| } |
| |
| /** |
| * Same as getAnnotations(Class) except returns the annotations with the accompanying class. |
| * |
| * <p> |
| * Results are ordered parent-to-child. |
| * |
| * @param <T> The annotation class type. |
| * @param a The annotation class type. |
| * @return The found matches, or an empty list if annotation was not found. |
| */ |
| public <T extends Annotation> List<AnnotationInfo<T>> getAnnotationInfosParentFirst(Class<T> a) { |
| return appendAnnotationInfosParentFirst(new ArrayList<>(), a); |
| } |
| |
| /** |
| * Constructs an {@link AnnotationList} of all annotations found on this class. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On this class. |
| * <li>On parent classes ordered child-to-parent. |
| * <li>On interfaces ordered child-to-parent. |
| * <li>On the package of this class. |
| * </ol> |
| * |
| * @param filter |
| * Optional filter to apply to limit which annotations are added to the list. |
| * <br>Can be <jk>null</jk> for no filtering. |
| * @return A new {@link AnnotationList} object on every call. |
| */ |
| public AnnotationList getAnnotationList(Predicate<AnnotationInfo<?>> filter) { |
| return appendAnnotationList(new AnnotationList(filter)); |
| } |
| |
| /** |
| * Constructs an {@link AnnotationList} of all annotations found on this class. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On the package of this class. |
| * <li>On interfaces ordered parent-to-child. |
| * <li>On parent classes ordered parent-to-child. |
| * <li>On this class. |
| * </ol> |
| * |
| * @param filter |
| * Optional filter to apply to limit which annotations are added to the list. |
| * <br>Can be <jk>null</jk> for no filtering. |
| * @return A new {@link AnnotationList} object on every call. |
| */ |
| public AnnotationList getAnnotationListParentFirst(Predicate<AnnotationInfo<?>> filter) { |
| return appendAnnotationListParentFirst(new AnnotationList(filter)); |
| } |
| |
| /** |
| * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified |
| * list. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On this class. |
| * <li>On parent classes ordered child-to-parent. |
| * <li>On interfaces ordered child-to-parent. |
| * <li>On the package of this class. |
| * </ol> |
| * |
| * @param l The list of annotations. |
| * @param a The annotation to search for. |
| * @return The same list. |
| */ |
| public <T extends Annotation> List<T> appendAnnotations(List<T> l, Class<T> a) { |
| for (ClassInfo ci : getParents()) |
| addIfNotNull(l, ci.getDeclaredAnnotation(a)); |
| for (ClassInfo ci : getInterfaces()) |
| addIfNotNull(l, ci.getDeclaredAnnotation(a)); |
| addIfNotNull(l, getPackageAnnotation(a)); |
| return l; |
| } |
| |
| /** |
| * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified |
| * list. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On the package of this class. |
| * <li>On interfaces ordered child-to-parent. |
| * <li>On parent classes ordered child-to-parent. |
| * <li>On this class. |
| * </ol> |
| * |
| * @param l The list of annotations. |
| * @param a The annotation to search for. |
| * @return The same list. |
| */ |
| public <T extends Annotation> List<T> appendAnnotationsParentFirst(List<T> l, Class<T> a) { |
| addIfNotNull(l, getPackageAnnotation(a)); |
| for (ClassInfo ci : getInterfacesParentFirst()) |
| addIfNotNull(l, ci.getDeclaredAnnotation(a)); |
| for (ClassInfo ci : getParentsParentFirst()) |
| addIfNotNull(l, ci.getDeclaredAnnotation(a)); |
| return l; |
| } |
| |
| /** |
| * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified |
| * list. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On this class. |
| * <li>On parent classes ordered child-to-parent. |
| * <li>On interfaces ordered child-to-parent. |
| * <li>On the package of this class. |
| * </ol> |
| * |
| * @param l The list of annotations. |
| * @param a The annotation to search for. |
| * @return The same list. |
| */ |
| public <T extends Annotation> List<AnnotationInfo<T>> appendAnnotationInfos(List<AnnotationInfo<T>> l, Class<T> a) { |
| for (ClassInfo ci : getParents()) |
| addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); |
| for (ClassInfo ci : getInterfaces()) |
| addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); |
| addIfNotNull(l, getPackageAnnotationInfo(a)); |
| return l; |
| } |
| |
| /** |
| * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified |
| * list. |
| * |
| * <p> |
| * Annotations are appended in the following orders: |
| * <ol> |
| * <li>On the package of this class. |
| * <li>On interfaces ordered child-to-parent. |
| * <li>On parent classes ordered child-to-parent. |
| * <li>On this class. |
| * </ol> |
| * |
| * @param l The list of annotations. |
| * @param a The annotation to search for. |
| * @return The same list. |
| */ |
| public <T extends Annotation> List<AnnotationInfo<T>> appendAnnotationInfosParentFirst(List<AnnotationInfo<T>> l, Class<T> a) { |
| addIfNotNull(l, getPackageAnnotationInfo(a)); |
| for (ClassInfo ci : getInterfacesParentFirst()) |
| addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); |
| for (ClassInfo ci : getParentsParentFirst()) |
| addIfNotNull(l, ci.getDeclaredAnnotationInfo(a)); |
| return l; |
| } |
| |
| AnnotationList appendAnnotationList(AnnotationList m) { |
| for (ClassInfo ci : getParents()) |
| for (Annotation a : ci.c.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(ci, a)); |
| for (ClassInfo ci : getInterfaces()) |
| for (Annotation a : ci.c.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(ci, a)); |
| Package p = c.getPackage(); |
| if (p != null) |
| for (Annotation a : p.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(p, a)); |
| return m; |
| } |
| |
| AnnotationList appendAnnotationListParentFirst(AnnotationList m) { |
| Package p = c.getPackage(); |
| if (p != null) |
| for (Annotation a : p.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(p, a)); |
| for (ClassInfo ci : getInterfacesParentFirst()) |
| for (Annotation a : ci.c.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(ci, a)); |
| for (ClassInfo ci : getParentsParentFirst()) |
| for (Annotation a : ci.c.getDeclaredAnnotations()) |
| m.add(AnnotationInfo.of(ci, a)); |
| return m; |
| } |
| |
| <T extends Annotation> T findAnnotation(Class<T> a) { |
| T t2 = getDeclaredAnnotation(a); |
| if (t2 != null) |
| return t2; |
| |
| ClassInfo sci = getParent(); |
| if (sci != null) { |
| t2 = sci.getAnnotation(a); |
| if (t2 != null) |
| return t2; |
| } |
| |
| for (ClassInfo c2 : getInterfaces()) { |
| t2 = c2.getAnnotation(a); |
| if (t2 != null) |
| return t2; |
| } |
| |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <T extends Annotation> T findDeclaredAnnotation(Class<T> a) { |
| for (Annotation a2 : c.getDeclaredAnnotations()) |
| if (a2.annotationType() == a) |
| return (T)a2; |
| return null; |
| } |
| |
| private synchronized Map<Class<?>,Optional<Annotation>> annotationMap() { |
| if (annotationMap == null) |
| annotationMap = new ConcurrentHashMap<>(); |
| return annotationMap; |
| } |
| |
| private synchronized Map<Class<?>,Optional<Annotation>> declaredAnnotationMap() { |
| if (declaredAnnotationMap == null) |
| declaredAnnotationMap = new ConcurrentHashMap<>(); |
| return declaredAnnotationMap; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Characteristics |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns <jk>true</jk> if all specified flags are applicable to this class. |
| * |
| * @param flags The flags to test for. |
| * @return <jk>true</jk> if all specified flags are applicable to this class. |
| */ |
| public boolean isAll(ReflectFlags...flags) { |
| for (ReflectFlags f : flags) { |
| switch (f) { |
| case DEPRECATED: |
| if (isNotDeprecated()) |
| return false; |
| break; |
| case NOT_DEPRECATED: |
| if (isDeprecated()) |
| return false; |
| break; |
| case PUBLIC: |
| if (isNotPublic()) |
| return false; |
| break; |
| case NOT_PUBLIC: |
| if (isPublic()) |
| return false; |
| break; |
| case STATIC: |
| if (isNotStatic()) |
| return false; |
| break; |
| case NOT_STATIC: |
| if (isStatic()) |
| return false; |
| break; |
| case MEMBER: |
| if (isNotMemberClass()) |
| return false; |
| break; |
| case NOT_MEMBER: |
| if (isMemberClass()) |
| return false; |
| break; |
| case ABSTRACT: |
| if (isNotAbstract()) |
| return false; |
| break; |
| case NOT_ABSTRACT: |
| if (isAbstract()) |
| return false; |
| break; |
| case INTERFACE: |
| if (isClass()) |
| return false; |
| break; |
| case CLASS: |
| if (isInterface()) |
| return false; |
| break; |
| default: |
| throw new RuntimeException("Invalid flag for class: " + f); |
| |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if all specified flags are applicable to this class. |
| * |
| * @param flags The flags to test for. |
| * @return <jk>true</jk> if all specified flags are applicable to this class. |
| */ |
| public boolean isAny(ReflectFlags...flags) { |
| for (ReflectFlags f : flags) { |
| switch (f) { |
| case DEPRECATED: |
| if (isDeprecated()) |
| return true; |
| break; |
| case NOT_DEPRECATED: |
| if (isNotDeprecated()) |
| return true; |
| break; |
| case PUBLIC: |
| if (isPublic()) |
| return true; |
| break; |
| case NOT_PUBLIC: |
| if (isNotPublic()) |
| return true; |
| break; |
| case STATIC: |
| if (isStatic()) |
| return true; |
| break; |
| case NOT_STATIC: |
| if (isNotStatic()) |
| return true; |
| break; |
| case MEMBER: |
| if (isMemberClass()) |
| return true; |
| break; |
| case NOT_MEMBER: |
| if (isNotMemberClass()) |
| return true; |
| break; |
| case ABSTRACT: |
| if (isAbstract()) |
| return true; |
| break; |
| case NOT_ABSTRACT: |
| if (isNotAbstract()) |
| return true; |
| break; |
| case INTERFACE: |
| if (isInterface()) |
| return true; |
| break; |
| case CLASS: |
| if (isClass()) |
| return true; |
| break; |
| default: |
| throw new RuntimeException("Invalid flag for class: " + f); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it. |
| * |
| * @return <jk>true</jk> if this class has the {@link Deprecated @Deprecated} annotation on it. |
| */ |
| public boolean isDeprecated() { |
| return c != null && c.isAnnotationPresent(Deprecated.class); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it. |
| * |
| * @return <jk>true</jk> if this class doesn't have the {@link Deprecated @Deprecated} annotation on it. |
| */ |
| public boolean isNotDeprecated() { |
| return c == null || ! c.isAnnotationPresent(Deprecated.class); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is public. |
| * |
| * @return <jk>true</jk> if this class is public. |
| */ |
| public boolean isPublic() { |
| return c != null && Modifier.isPublic(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is not public. |
| * |
| * @return <jk>true</jk> if this class is not public. |
| */ |
| public boolean isNotPublic() { |
| return c == null || ! Modifier.isPublic(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is public. |
| * |
| * <p> |
| * Note that interfaces are always reported as static, and the static keyword on a member interface is meaningless. |
| * |
| * @return <jk>true</jk> if this class is public. |
| */ |
| public boolean isStatic() { |
| return c != null && Modifier.isStatic(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is not static. |
| * |
| * <p> |
| * Note that interfaces are always reported as static, and the static keyword on a member interface is meaningless. |
| * |
| * @return <jk>true</jk> if this class is not static. |
| */ |
| public boolean isNotStatic() { |
| return c == null || ! Modifier.isStatic(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is abstract. |
| * |
| * <p> |
| * Note that interfaces are always reported as abstract. |
| * |
| * @return <jk>true</jk> if this class is abstract. |
| */ |
| public boolean isAbstract() { |
| return c != null && Modifier.isAbstract(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is not abstract. |
| * |
| * <p> |
| * Note that interfaces are always reported as abstract. |
| * |
| * @return <jk>true</jk> if this class is not abstract. |
| */ |
| public boolean isNotAbstract() { |
| return c == null || ! Modifier.isAbstract(c.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a member class. |
| * |
| * @return <jk>true</jk> if this class is a member class. |
| */ |
| public boolean isMemberClass() { |
| return c != null && c.isMemberClass(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a member class. |
| * |
| * @return <jk>true</jk> if this class is a member class. |
| */ |
| public boolean isNotMemberClass() { |
| return c == null || ! c.isMemberClass(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a member class and not static. |
| * |
| * @return <jk>true</jk> if this class is a member class and not static. |
| */ |
| public boolean isNonStaticMemberClass() { |
| return c != null && c.isMemberClass() && ! isStatic(); |
| } |
| |
| /** |
| * Returns <jk>false</jk> if this class is a member class and not static. |
| * |
| * @return <jk>false</jk> if this class is a member class and not static. |
| */ |
| public boolean isNotNonStaticMemberClass() { |
| return ! isNonStaticMemberClass(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a local class. |
| * |
| * @return <jk>true</jk> if this class is a local class. |
| */ |
| public boolean isLocalClass() { |
| return c != null && c.isLocalClass(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a local class. |
| * |
| * @return <jk>true</jk> if this class is a local class. |
| */ |
| public boolean isNotLocalClass() { |
| return c == null || ! c.isLocalClass(); |
| } |
| |
| /** |
| * Identifies if the specified visibility matches this constructor. |
| * |
| * @param v The visibility to validate against. |
| * @return <jk>true</jk> if this visibility matches the modifier attribute of this constructor. |
| */ |
| public boolean isVisible(Visibility v) { |
| return c != null && v.isVisible(c); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this is a primitive class. |
| * |
| * @return <jk>true</jk> if this is a primitive class. |
| */ |
| public boolean isPrimitive() { |
| return c != null && c.isPrimitive(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this is not a primitive class. |
| * |
| * @return <jk>true</jk> if this is not a primitive class. |
| */ |
| public boolean isNotPrimitive() { |
| return c == null || ! c.isPrimitive(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is an interface. |
| * |
| * @return <jk>true</jk> if this class is an interface. |
| */ |
| public boolean isInterface() { |
| return c != null && c.isInterface(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is not an interface. |
| * |
| * @return <jk>true</jk> if this class is not an interface. |
| */ |
| public boolean isClass() { |
| return c != null && ! c.isInterface(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a {@link RuntimeException}. |
| * |
| * @return <jk>true</jk> if this class is a {@link RuntimeException}. |
| */ |
| public boolean isRuntimeException() { |
| return isChildOf(RuntimeException.class); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Primitive wrappers |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value. |
| * |
| * @return <jk>true</jk> if the {@link #getPrimitiveWrapper()} method returns a value. |
| */ |
| public boolean hasPrimitiveWrapper() { |
| return pmap1.containsKey(c); |
| } |
| |
| /** |
| * If this class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class |
| * (e.g. <code>Integer.<jk>class</jk></code>). |
| * |
| * @return The wrapper class, or <jk>null</jk> if class is not a primitive. |
| */ |
| public Class<?> getPrimitiveWrapper() { |
| return pmap1.get(c); |
| } |
| |
| /** |
| * If this class is a primitive wrapper (e.g. <code><jk>Integer</jk>.<jk>class</jk></code>) returns it's |
| * primitive class (e.g. <code>int.<jk>class</jk></code>). |
| * |
| * @return The primitive class, or <jk>null</jk> if class is not a primitive wrapper. |
| */ |
| public Class<?> getPrimitiveForWrapper() { |
| return pmap2.get(c); |
| } |
| |
| /** |
| * If this class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class |
| * (e.g. <code>Integer.<jk>class</jk></code>). |
| * |
| * @return The wrapper class if it's primitive, or the same class if class is not a primitive. |
| */ |
| public Class<?> getWrapperIfPrimitive() { |
| if (c != null && ! c.isPrimitive()) |
| return c; |
| return pmap1.get(c); |
| } |
| |
| /** |
| * Same as {@link #getWrapperIfPrimitive()} but wraps it in a {@link ClassInfo}. |
| * |
| * @return The wrapper class if it's primitive, or the same class if class is not a primitive. |
| */ |
| public ClassInfo getWrapperInfoIfPrimitive() { |
| if (c == null || ! c.isPrimitive()) |
| return this; |
| return of(pmap1.get(c)); |
| } |
| |
| /** |
| * Returns the default value for this primitive class. |
| * |
| * @return The default value, or <jk>null</jk> if this is not a primitive class. |
| */ |
| public Object getPrimitiveDefault() { |
| return primitiveDefaultMap.get(c); |
| } |
| |
| private static final Map<Class<?>, Class<?>> |
| pmap1 = new HashMap<>(), |
| pmap2 = new HashMap<>(); |
| static { |
| pmap1.put(boolean.class, Boolean.class); |
| pmap1.put(byte.class, Byte.class); |
| pmap1.put(short.class, Short.class); |
| pmap1.put(char.class, Character.class); |
| pmap1.put(int.class, Integer.class); |
| pmap1.put(long.class, Long.class); |
| pmap1.put(float.class, Float.class); |
| pmap1.put(double.class, Double.class); |
| pmap2.put(Boolean.class, boolean.class); |
| pmap2.put(Byte.class, byte.class); |
| pmap2.put(Short.class, short.class); |
| pmap2.put(Character.class, char.class); |
| pmap2.put(Integer.class, int.class); |
| pmap2.put(Long.class, long.class); |
| pmap2.put(Float.class, float.class); |
| pmap2.put(Double.class, double.class); |
| } |
| |
| private static final Map<Class<?>,Object> primitiveDefaultMap = Collections.unmodifiableMap( |
| new AMap<Class<?>,Object>() |
| .append(Boolean.TYPE, false) |
| .append(Character.TYPE, (char)0) |
| .append(Short.TYPE, (short)0) |
| .append(Integer.TYPE, 0) |
| .append(Long.TYPE, 0l) |
| .append(Float.TYPE, 0f) |
| .append(Double.TYPE, 0d) |
| .append(Byte.TYPE, (byte)0) |
| .append(Boolean.class, false) |
| .append(Character.class, (char)0) |
| .append(Short.class, (short)0) |
| .append(Integer.class, 0) |
| .append(Long.class, 0l) |
| .append(Float.class, 0f) |
| .append(Double.class, 0d) |
| .append(Byte.class, (byte)0) |
| ); |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Labels |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns the full name of this class. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <ul> |
| * <li><js>"com.foo.MyClass"<js> - Normal class |
| * <li><js>"com.foo.MyClass[][]"<js> - Array. |
| * <li><js>"com.foo.MyClass$InnerClass"<js> - Inner class. |
| * <li><js>"com.foo.MyClass$InnerClass[][]"<js> - Inner class array. |
| * <li><js>"int"<js> - Primitive class. |
| * <li><js>"int[][]"<js> - Primitive class class. |
| * <li><js>"java.util.Map<java.lang.String,java.lang.Object>"<js> - Parameterized type. |
| * <li><js>"java.util.AbstractMap<K,V>"<js> - Parameterized generic type. |
| * <li><js>"V"<js> - Parameterized generic type argument. |
| * </ul> |
| * |
| * @return The underlying class name. |
| */ |
| public String getFullName() { |
| Class<?> ct = getComponentType().inner(); |
| int dim = getDimensions(); |
| if (ct != null && dim == 0 && ! isParameterizedType) |
| return ct.getName(); |
| StringBuilder sb = new StringBuilder(128); |
| appendFullName(sb); |
| return sb.toString(); |
| } |
| |
| /** |
| * Returns all possible names for this class. |
| * |
| * @return |
| * An array consisting of: |
| * <ul> |
| * <li>{@link #getFullName()} |
| * <li>{@link Class#getName()} - Note that this might be a dup. |
| * <li>{@link #getShortName()} |
| * <li>{@link #getSimpleName()} |
| * </ul> |
| */ |
| public String[] getNames() { |
| return new String[]{ getFullName(), c.getName(), getShortName(), getSimpleName() }; |
| } |
| |
| /** |
| * Same as {@link #getFullName()} but appends to an existing string builder. |
| * |
| * @param sb The string builder to append to. |
| * @return The same string builder. |
| */ |
| public StringBuilder appendFullName(StringBuilder sb) { |
| Class<?> ct = getComponentType().inner(); |
| int dim = getDimensions(); |
| if (ct != null && dim == 0 && ! isParameterizedType) |
| return sb.append(ct.getName()); |
| sb.append(ct != null ? ct.getName() : t.getTypeName()); |
| if (isParameterizedType) { |
| ParameterizedType pt = (ParameterizedType)t; |
| sb.append('<'); |
| boolean first = true; |
| for (Type t2 : pt.getActualTypeArguments()) { |
| if (! first) |
| sb.append(','); |
| first = false; |
| of(t2).appendFullName(sb); |
| } |
| sb.append('>'); |
| } |
| for (int i = 0; i < dim; i++) |
| sb.append('[').append(']'); |
| return sb; |
| } |
| |
| /** |
| * Returns the short name of the underlying class. |
| * |
| * <p> |
| * Similar to {@link #getSimpleName()} but also renders local or member class name prefixes. |
| * |
| * @return The short name of the underlying class. |
| */ |
| public String getShortName() { |
| Class<?> ct = getComponentType().inner(); |
| int dim = getDimensions(); |
| if (ct != null && dim == 0 && ! (isParameterizedType || isMemberClass() || c.isLocalClass())) |
| return ct.getSimpleName(); |
| StringBuilder sb = new StringBuilder(32); |
| appendShortName(sb); |
| return sb.toString(); |
| } |
| |
| /** |
| * Same as {@link #getShortName()} but appends to an existing string builder. |
| * |
| * @param sb The string builder to append to. |
| * @return The same string builder. |
| */ |
| public StringBuilder appendShortName(StringBuilder sb) { |
| Class<?> ct = getComponentType().inner(); |
| int dim = getDimensions(); |
| if (ct != null) { |
| if (ct.isLocalClass()) |
| sb.append(of(ct.getEnclosingClass()).getSimpleName()).append('$').append(ct.getSimpleName()); |
| else if (ct.isMemberClass()) |
| sb.append(of(ct.getDeclaringClass()).getSimpleName()).append('$').append(ct.getSimpleName()); |
| else |
| sb.append(ct.getSimpleName()); |
| } else { |
| sb.append(t.getTypeName()); |
| } |
| if (isParameterizedType) { |
| ParameterizedType pt = (ParameterizedType)t; |
| sb.append('<'); |
| boolean first = true; |
| for (Type t2 : pt.getActualTypeArguments()) { |
| if (! first) |
| sb.append(','); |
| first = false; |
| of(t2).appendShortName(sb); |
| } |
| sb.append('>'); |
| } |
| for (int i = 0; i < dim; i++) |
| sb.append('[').append(']'); |
| return sb; |
| } |
| |
| /** |
| * Returns the simple name of the underlying class. |
| * |
| * <p> |
| * Returns either {@link Class#getSimpleName()} or {@link Type#getTypeName()} depending on whether |
| * this is a class or type. |
| * |
| * @return The simple name of the underlying class; |
| */ |
| public String getSimpleName() { |
| return c != null ? c.getSimpleName() : t.getTypeName(); |
| } |
| |
| /** |
| * Returns the name of the underlying class. |
| * |
| * @return The name of the underlying class. |
| */ |
| public String getName() { |
| return c != null ? c.getName() : t.getTypeName(); |
| } |
| |
| /** |
| * Same as {@link #getSimpleName()} but uses <js>"Array"</js> instead of <js>"[]"</js>. |
| * |
| * @return The readable name for this class. |
| */ |
| public String getReadableName() { |
| if (c == null) |
| return t.getTypeName(); |
| if (! c.isArray()) |
| return c.getSimpleName(); |
| Class<?> c = this.c; |
| StringBuilder sb = new StringBuilder(); |
| while (c.isArray()) { |
| sb.append("Array"); |
| c = c.getComponentType(); |
| } |
| return c.getSimpleName() + sb; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Hierarchy |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>. |
| * |
| * @param child The child class. |
| * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>. |
| */ |
| public boolean isParentOf(Class<?> child) { |
| return c != null && child != null && c.isAssignableFrom(child); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>. |
| * |
| * @param child The child class. |
| * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>. |
| */ |
| public boolean isParentOf(Type child) { |
| if (child instanceof Class) |
| return isParentOf((Class<?>)child); |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a child of <c>parent</c>. |
| * |
| * @param parent The parent class. |
| * @return <jk>true</jk> if this class is a parent of <c>child</c>. |
| */ |
| public boolean isStrictChildOf(Class<?> parent) { |
| return c != null && parent != null && parent.isAssignableFrom(c) && ! c.equals(parent); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. |
| * |
| * @param parent The parent class. |
| * @return <jk>true</jk> if this class is a child or the same as <c>parent</c>. |
| */ |
| public boolean isChildOf(Class<?> parent) { |
| return c != null && parent != null && parent.isAssignableFrom(c); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>. |
| * |
| * @param parents The parents class. |
| * @return <jk>true</jk> if this class is a child or the same as any of the <c>parents</c>. |
| */ |
| public boolean isChildOfAny(Class<?>...parents) { |
| for (Class<?> p : parents) |
| if (isChildOf(p)) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. |
| * |
| * @param parent The parent class. |
| * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>. |
| */ |
| public boolean isChildOf(Type parent) { |
| if (parent instanceof Class) |
| return isChildOf((Class<?>)parent); |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is a child or the same as <c>parent</c>. |
| * |
| * @param parent The parent class. |
| * @return <jk>true</jk> if this class is a parent or the same as <c>parent</c>. |
| */ |
| public boolean isChildOf(ClassInfo parent) { |
| return isChildOf(parent.inner()); |
| } |
| |
| /** |
| * Checks for equality with the specified class. |
| * |
| * @param c The class to check equality with. |
| * @return <jk>true</jk> if the specified class is the same as this one. |
| */ |
| public boolean is(Class<?> c) { |
| return this.c != null && this.c.equals(c); |
| } |
| |
| /** |
| * Checks for equality with the specified class. |
| * |
| * @param c The class to check equality with. |
| * @return <jk>true</jk> if the specified class is the same as this one. |
| */ |
| public boolean is(ClassInfo c) { |
| if (this.c != null) |
| return this.c.equals(c.inner()); |
| return t.equals(c.t); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is any of the specified types. |
| * |
| * @param types The types to check against. |
| * @return <jk>true</jk> if this class is any of the specified types. |
| */ |
| public boolean isAny(Class<?>...types) { |
| for (Class<?> cc : types) |
| if (is(cc)) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Returns the package of this class. |
| * |
| * @return The package of this class. |
| */ |
| public Package getPackage() { |
| return c == null ? null : c.getPackage(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is not in the root package. |
| * |
| * @return <jk>true</jk> if this class is not in the root package. |
| */ |
| public boolean hasPackage() { |
| return getPackage() != null; |
| } |
| |
| /** |
| * Returns the number of dimensions if this is an array type. |
| * |
| * @return The number of dimensions if this is an array type, or <c>0</c> if it is not. |
| */ |
| public int getDimensions() { |
| if (dim == -1) { |
| int d = 0; |
| Class<?> ct = c; |
| while (ct != null && ct.isArray()) { |
| d++; |
| ct = ct.getComponentType(); |
| } |
| this.dim = d; |
| this.componentType = ct == c ? this : of(ct); |
| } |
| return dim; |
| } |
| |
| /** |
| * Returns the base component type of this class if it's an array. |
| * |
| * @return The base component type of this class if it's an array, or this object if it's not. |
| */ |
| public ClassInfo getComponentType() { |
| if (componentType == null) { |
| if (c == null) |
| componentType = this; |
| else |
| getDimensions(); |
| } |
| return componentType; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this class is an enum. |
| * |
| * @return <jk>true</jk> if this class is an enum. |
| */ |
| public boolean isEnum() { |
| return c != null && c.isEnum(); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Instantiation |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Shortcut for calling {@link Class#newInstance()} on the underlying class. |
| * |
| * @return A new instance of the underlying class |
| * @throws ExecutableException Exception occurred on invoked constructor/method/field. |
| */ |
| public Object newInstance() throws ExecutableException { |
| if (c == null) |
| throw new ExecutableException("Type ''{0}'' cannot be instantiated", getFullName()); |
| try { |
| return c.newInstance(); |
| } catch (InstantiationException | IllegalAccessException e) { |
| throw new ExecutableException(e); |
| } |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Parameter types |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Finds the real parameter type of this class. |
| * |
| * @param index The zero-based index of the parameter to resolve. |
| * @param pt The parameterized type class containing the parameterized type to resolve (e.g. <c>HashMap</c>). |
| * @return The resolved real class. |
| */ |
| public Class<?> getParameterType(int index, Class<?> pt) { |
| if (pt == null) |
| throw new FormattedIllegalArgumentException("Parameterized type cannot be null"); |
| |
| // We need to make up a mapping of type names. |
| Map<Type,Type> typeMap = new HashMap<>(); |
| Class<?> cc = c; |
| while (pt != cc.getSuperclass()) { |
| extractTypes(typeMap, cc); |
| cc = cc.getSuperclass(); |
| if (cc == null) |
| throw new FormattedIllegalArgumentException("Class ''{0}'' is not a subclass of parameterized type ''{1}''", c.getSimpleName(), pt.getSimpleName()); |
| } |
| |
| Type gsc = cc.getGenericSuperclass(); |
| |
| if (! (gsc instanceof ParameterizedType)) |
| throw new FormattedIllegalArgumentException("Class ''{0}'' is not a parameterized type", pt.getSimpleName()); |
| |
| ParameterizedType cpt = (ParameterizedType)gsc; |
| Type[] atArgs = cpt.getActualTypeArguments(); |
| if (index >= atArgs.length) |
| throw new FormattedIllegalArgumentException("Invalid type index. index={0}, argsLength={1}", index, atArgs.length); |
| Type actualType = cpt.getActualTypeArguments()[index]; |
| |
| if (typeMap.containsKey(actualType)) |
| actualType = typeMap.get(actualType); |
| |
| if (actualType instanceof Class) { |
| return (Class<?>)actualType; |
| |
| } else if (actualType instanceof GenericArrayType) { |
| Type gct = ((GenericArrayType)actualType).getGenericComponentType(); |
| if (gct instanceof ParameterizedType) |
| return Array.newInstance((Class<?>)((ParameterizedType)gct).getRawType(), 0).getClass(); |
| } else if (actualType instanceof TypeVariable) { |
| TypeVariable<?> typeVariable = (TypeVariable<?>)actualType; |
| List<Class<?>> nestedOuterTypes = new LinkedList<>(); |
| for (Class<?> ec = cc.getEnclosingClass(); ec != null; ec = ec.getEnclosingClass()) { |
| Class<?> outerClass = cc.getClass(); |
| nestedOuterTypes.add(outerClass); |
| Map<Type,Type> outerTypeMap = new HashMap<>(); |
| extractTypes(outerTypeMap, outerClass); |
| for (Map.Entry<Type,Type> entry : outerTypeMap.entrySet()) { |
| Type key = entry.getKey(), value = entry.getValue(); |
| if (key instanceof TypeVariable) { |
| TypeVariable<?> keyType = (TypeVariable<?>)key; |
| if (keyType.getName().equals(typeVariable.getName()) && isInnerClass(keyType.getGenericDeclaration(), typeVariable.getGenericDeclaration())) { |
| if (value instanceof Class) |
| return (Class<?>)value; |
| typeVariable = (TypeVariable<?>)entry.getValue(); |
| } |
| } |
| } |
| } |
| } else if (actualType instanceof ParameterizedType) { |
| return (Class<?>)((ParameterizedType)actualType).getRawType(); |
| } |
| throw new FormattedIllegalArgumentException("Could not resolve variable ''{0}'' to a type.", actualType.getTypeName()); |
| } |
| |
| private static boolean isInnerClass(GenericDeclaration od, GenericDeclaration id) { |
| if (od instanceof Class && id instanceof Class) { |
| Class<?> oc = (Class<?>)od; |
| Class<?> ic = (Class<?>)id; |
| while ((ic = ic.getEnclosingClass()) != null) |
| if (ic == oc) |
| return true; |
| } |
| return false; |
| } |
| |
| private static void extractTypes(Map<Type,Type> typeMap, Class<?> c) { |
| Type gs = c.getGenericSuperclass(); |
| if (gs instanceof ParameterizedType) { |
| ParameterizedType pt = (ParameterizedType)gs; |
| Type[] typeParameters = ((Class<?>)pt.getRawType()).getTypeParameters(); |
| Type[] actualTypeArguments = pt.getActualTypeArguments(); |
| for (int i = 0; i < typeParameters.length; i++) { |
| if (typeMap.containsKey(actualTypeArguments[i])) |
| actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]); |
| typeMap.put(typeParameters[i], actualTypeArguments[i]); |
| } |
| } |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Other |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| @Override |
| public String toString() { |
| return t.toString(); |
| } |
| |
| @Override |
| public int hashCode() { |
| return t.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return o == null ? false : ((ClassInfo)o).t.equals(t); |
| } |
| } |