All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.util.Types Maven / Gradle / Ivy

Go to download

SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.

There is a newer version: 2.99.0
Show newest version
/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2016 Board of Regents of the University of
 * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
 * Institute of Molecular Cell Biology and Genetics.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.util;

import java.io.File;

// Portions of this class were adapted from the
// org.apache.commons.lang3.reflect.TypeUtils and
// org.apache.commons.lang3.Validate classes of
// Apache Commons Lang 3.4, which is distributed
// under the Apache 2 license.
// See lines below starting with "BEGIN FORK OF APACHE COMMONS LANG".
//
// Portions of this class were adapted from the GenTyRef project
// by Wouter Coekaerts, which is distributed under the Apache 2 license.
// See lines below starting with "BEGIN FORK OF GENTYREF".
//
// See NOTICE.txt for further details on third-party licenses.

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.scijava.util.FileUtils;

/**
 * Utility class for working with generic types, fields and methods.
 * 

* Logic and inspiration were drawn from the following excellent libraries: *

*
    *
  • Google Guava's {@code com.google.common.reflect} package.
  • *
  • Apache Commons Lang 3's {@code org.apache.commons.lang3.reflect} package. *
  • *
  • GenTyRef (Generic Type * Reflector), a library for runtime generic type introspection.
  • *
* * @author Curtis Rueden */ public final class Types { private Types() { // NB: Prevent instantiation of utility class. } /** * Loads the class with the given name, using the current thread's context * class loader, or null if it cannot be loaded. * * @param name The name of the class to load. * @return The loaded class, or null if the class could not be loaded. * @see #load(String, ClassLoader, boolean) */ public static Class load(final String name) { return load(name, null, true); } /** * Loads the class with the given name, using the specified * {@link ClassLoader}, or null if it cannot be loaded. * * @param name The name of the class to load. * @param classLoader The class loader with which to load the class; if null, * the current thread's context class loader will be used. * @return The loaded class, or null if the class could not be loaded. * @see #load(String, ClassLoader, boolean) */ public static Class load(final String name, final ClassLoader classLoader) { return load(name, classLoader, true); } /** * Loads the class with the given name, using the current thread's context * class loader. * * @param className the name of the class to load. * @param quietly Whether to return {@code null} (rather than throwing * {@link IllegalArgumentException}) if something goes wrong loading * the class. * @return The loaded class, or {@code null} if the class could not be loaded * and the {@code quietly} flag is set. * @see #load(String, ClassLoader, boolean) * @throws IllegalArgumentException If the class cannot be loaded and the * {@code quietly} flag is not set. */ public static Class load(final String className, final boolean quietly) { return load(className, null, quietly); } /** * Loads the class with the given name, using the specified * {@link ClassLoader}, or null if it cannot be loaded. *

* This method is capable of parsing several different class name syntaxes. In * particular, array classes (including primitives) represented using either * square brackets or internal Java array name syntax are supported. Examples: *

*
    *
  • {@code boolean} is loaded as {@code boolean.class}
  • *
  • {@code Z} is loaded as {@code boolean.class}
  • *
  • {@code double[]} is loaded as {@code double[].class}
  • *
  • {@code string[]} is loaded as {@code java.lang.String.class}
  • *
  • {@code [F} is loaded as {@code float[].class}
  • *
* * @param name The name of the class to load. * @param classLoader The class loader with which to load the class; if null, * the current thread's context class loader will be used. * @param quietly Whether to return {@code null} (rather than throwing * {@link IllegalArgumentException}) if something goes wrong loading * the class * @return The loaded class, or {@code null} if the class could not be loaded * and the {@code quietly} flag is set. * @throws IllegalArgumentException If the class cannot be loaded and the * {@code quietly} flag is not set. */ public static Class load(final String name, final ClassLoader classLoader, final boolean quietly) { // handle primitive types if (name.equals("Z") || name.equals("boolean")) return boolean.class; if (name.equals("B") || name.equals("byte")) return byte.class; if (name.equals("C") || name.equals("char")) return char.class; if (name.equals("D") || name.equals("double")) return double.class; if (name.equals("F") || name.equals("float")) return float.class; if (name.equals("I") || name.equals("int")) return int.class; if (name.equals("J") || name.equals("long")) return long.class; if (name.equals("S") || name.equals("short")) return short.class; if (name.equals("V") || name.equals("void")) return void.class; // handle built-in class shortcuts final String className; if (name.equals("string")) className = "java.lang.String"; else className = name; // handle source style arrays (e.g.: "java.lang.String[]") if (name.endsWith("[]")) { final String elementClassName = name.substring(0, name.length() - 2); return arrayOrNull(load(elementClassName, classLoader)); } // handle non-primitive internal arrays (e.g.: "[Ljava.lang.String;") if (name.startsWith("[L") && name.endsWith(";")) { final String elementClassName = name.substring(2, name.length() - 1); return arrayOrNull(load(elementClassName, classLoader)); } // handle other internal arrays (e.g.: "[I", "[[I", "[[Ljava.lang.String;") if (name.startsWith("[")) { final String elementClassName = name.substring(1); return arrayOrNull(load(elementClassName, classLoader)); } // load the class! try { final ClassLoader cl = classLoader == null ? // Thread.currentThread().getContextClassLoader() : classLoader; return cl.loadClass(className); } catch (final Throwable t) { // NB: Do not allow any failure to load the class to crash us. // Not ClassNotFoundException. // Not NoClassDefFoundError. // Not UnsupportedClassVersionError! if (quietly) return null; throw iae(t, "Cannot load class: " + className); } } /** * Gets the base location of the given class. * * @param c The class whose location is desired. * @return URL pointing to the class, or null if the location could not be * determined. * @see #location(Class, boolean) */ public static URL location(final Class c) { return location(c, true); } /** * Gets the base location of the given class. *

* If the class is directly on the file system (e.g., * "/path/to/my/package/MyClass.class") then it will return the base directory * (e.g., "file:/path/to"). *

*

* If the class is within a JAR file (e.g., * "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the * path to the JAR (e.g., "file:/path/to/my-jar.jar"). *

* * @param c The class whose location is desired. * @param quietly Whether to return {@code null} (rather than throwing * {@link IllegalArgumentException}) if something goes wrong * determining the location. * @return URL pointing to the class, or null if the location could not be * determined and the {@code quietly} flag is set. * @throws IllegalArgumentException If the location cannot be determined and * the {@code quietly} flag is not set. * @see FileUtils#urlToFile(URL) to convert the result to a {@link File}. */ public static URL location(final Class c, final boolean quietly) { Exception cause = null; String why = null; // try the easy way first try { final CodeSource codeSource = c.getProtectionDomain().getCodeSource(); if (codeSource != null) { final URL location = codeSource.getLocation(); if (location != null) return location; why = "null code source location"; } else why = "null code source"; } catch (final SecurityException exc) { // NB: Cannot access protection domain. cause = exc; why = "cannot access protection domain"; } // NB: The easy way failed, so we try the hard way. We ask for the class // itself as a resource, then strip the class's path from the URL string, // leaving the base path. // get the class's raw resource path final URL classResource = c.getResource(c.getSimpleName() + ".class"); if (classResource == null) { // cannot find class resource if (quietly) return null; throw iae(cause, "No class resource for class: " + name(c), why); } final String url = classResource.toString(); final String suffix = c.getCanonicalName().replace('.', '/') + ".class"; if (!url.endsWith(suffix)) { // weird URL if (quietly) return null; throw iae(cause, "Unsupported URL format: " + url, why); } // strip the class's path from the URL string final String base = url.substring(0, url.length() - suffix.length()); String path = base; // remove the "jar:" prefix and "!/" suffix, if present if (path.startsWith("jar:")) path = path.substring(4, path.length() - 2); try { return new URL(path); } catch (final MalformedURLException e) { if (quietly) return null; throw iae(e, "Malformed URL", why); } } /** * Gets a string representation of the given type. * * @param t Type whose name is desired. * @return The name of the given type. */ public static String name(final Type t) { if (t instanceof Class) { final Class c = (Class) t; return c.isArray() ? (name(component(c)) + "[]") : c.getName(); } return t.toString(); } /** * Gets the (first) raw class of the given type. *
    *
  • If the type is a {@code Class} itself, the type itself is returned. *
  • *
  • If the type is a {@link ParameterizedType}, the raw type of the * parameterized type is returned.
  • *
  • If the type is a {@link GenericArrayType}, the returned type is the * corresponding array class. For example: {@code List[] => List[]}. *
  • *
  • If the type is a type variable or wildcard type, the raw type of the * first upper bound is returned. For example: * {@code => Foo}.
  • *
*

* If you want all raw classes of the given type, use {@link #raws}. *

* * @param type The type from which to discern the (first) raw class. * @return The type's first raw class. */ public static Class raw(final Type type) { if (type == null) return null; if (type instanceof Class) return (Class) type; final List> c = raws(type); if (c == null || c.size() == 0) return null; return c.get(0); } /** * Gets all raw classes corresponding to the given type. *

* For example, a type parameter {@code A extends Number & Iterable} will * return both {@link Number} and {@link Iterable} as its raw classes. *

* * @param type The type from which to discern the raw classes. * @return List of the type's raw classes. * @see #raw */ public static List> raws(final Type type) { if (type == null) return null; return GenericTypeReflector.getUpperBoundClassAndInterfaces(type); } public static boolean isBoolean(final Class type) { return type == boolean.class || Boolean.class.isAssignableFrom(type); } public static boolean isByte(final Class type) { return type == byte.class || Byte.class.isAssignableFrom(type); } public static boolean isCharacter(final Class type) { return type == char.class || Character.class.isAssignableFrom(type); } public static boolean isDouble(final Class type) { return type == double.class || Double.class.isAssignableFrom(type); } public static boolean isFloat(final Class type) { return type == float.class || Float.class.isAssignableFrom(type); } public static boolean isInteger(final Class type) { return type == int.class || Integer.class.isAssignableFrom(type); } public static boolean isLong(final Class type) { return type == long.class || Long.class.isAssignableFrom(type); } public static boolean isShort(final Class type) { return type == short.class || Short.class.isAssignableFrom(type); } public static boolean isNumber(final Class type) { return Number.class.isAssignableFrom(type) || type == byte.class || type == double.class || type == float.class || type == int.class || type == long.class || type == short.class; } public static boolean isText(final Class type) { return String.class.isAssignableFrom(type) || isCharacter(type); } /** * Returns the non-primitive {@link Class} closest to the given type. *

* Specifically, the following type conversions are done: *

*
    *
  • boolean.class becomes Boolean.class
  • *
  • byte.class becomes Byte.class
  • *
  • char.class becomes Character.class
  • *
  • double.class becomes Double.class
  • *
  • float.class becomes Float.class
  • *
  • int.class becomes Integer.class
  • *
  • long.class becomes Long.class
  • *
  • short.class becomes Short.class
  • *
  • void.class becomes Void.class
  • *
*

* All other types are unchanged. *

*/ public static Class box(final Class type) { final Class destType; if (type == boolean.class) destType = Boolean.class; else if (type == byte.class) destType = Byte.class; else if (type == char.class) destType = Character.class; else if (type == double.class) destType = Double.class; else if (type == float.class) destType = Float.class; else if (type == int.class) destType = Integer.class; else if (type == long.class) destType = Long.class; else if (type == short.class) destType = Short.class; else if (type == void.class) destType = Void.class; else destType = type; @SuppressWarnings("unchecked") final Class result = (Class) destType; return result; } /** * Returns the primitive {@link Class} closest to the given type. *

* Specifically, the following type conversions are done: *

*
    *
  • Boolean.class becomes boolean.class
  • *
  • Byte.class becomes byte.class
  • *
  • Character.class becomes char.class
  • *
  • Double.class becomes double.class
  • *
  • Float.class becomes float.class
  • *
  • Integer.class becomes int.class
  • *
  • Long.class becomes long.class
  • *
  • Short.class becomes short.class
  • *
  • Void.class becomes void.class
  • *
*

* All other types are unchanged. *

*/ public static Class unbox(final Class type) { final Class destType; if (type == Boolean.class) destType = boolean.class; else if (type == Byte.class) destType = byte.class; else if (type == Character.class) destType = char.class; else if (type == Double.class) destType = double.class; else if (type == Float.class) destType = float.class; else if (type == Integer.class) destType = int.class; else if (type == Long.class) destType = long.class; else if (type == Short.class) destType = short.class; else if (type == Void.class) destType = void.class; else destType = type; @SuppressWarnings("unchecked") final Class result = (Class) destType; return result; } /** * Gets the "null" value for the given type. For non-primitives, this will * actually be null. For primitives, it will be zero for numeric types, false * for boolean, and the null character for char. */ public static T nullValue(final Class type) { final Object defaultValue; if (type == boolean.class) defaultValue = false; else if (type == byte.class) defaultValue = (byte) 0; else if (type == char.class) defaultValue = '\0'; else if (type == double.class) defaultValue = 0d; else if (type == float.class) defaultValue = 0f; else if (type == int.class) defaultValue = 0; else if (type == long.class) defaultValue = 0L; else if (type == short.class) defaultValue = (short) 0; else defaultValue = null; @SuppressWarnings("unchecked") final T result = (T) defaultValue; return result; } /** * Gets the field with the specified name, of the given class, or superclass * thereof. *

* Unlike {@link Class#getField(String)}, this method will return fields of * any visibility, not just {@code public}. And unlike * {@link Class#getDeclaredField(String)}, it will do so recursively, * returning the first field of the given name from the class's superclass * hierarchy. *

*

* Note that this method does not guarantee that the returned field is * accessible; if the field is not {@code public}, calling code will need to * use {@link Field#setAccessible(boolean)} in order to manipulate the field's * contents. *

* * @param c The class (or subclass thereof) containing the desired field. * @param name * @return The first field with the given name in the class's superclass * hierarchy. * @throws IllegalArgumentException if the specified class does not contain a * method with the given name */ public static Field field(final Class c, final String name) { if (c == null) throw iae("No such field: " + name); try { return c.getDeclaredField(name); } catch (final NoSuchFieldException e) {} return field(c.getSuperclass(), name); } /** * Gets the method with the specified name and argument types, of the given * class, or superclass thereof. *

* Unlike {@link Class#getMethod(String, Class[])}, this method will return * methods of any visibility, not just {@code public}. And unlike * {@link Class#getDeclaredMethod(String, Class[])}, it will do so * recursively, returning the first method of the given name and argument * types from the class's superclass hierarchy. *

*

* Note that this method does not guarantee that the returned method is * accessible; if the method is not {@code public}, calling code will need to * use {@link Method#setAccessible(boolean)} in order to invoke the method. *

* * @param c The class (or subclass thereof) containing the desired method. * @param name Name of the method. * @param parameterTypes Types of the method parameters. * @return The first method with the given name and argument types in the * class's superclass hierarchy. * @throws IllegalArgumentException If the specified class does not contain a * method with the given name and argument types. */ public static Method method(final Class c, final String name, final Class... parameterTypes) { if (c == null) throw iae("No such field: " + name); try { return c.getDeclaredMethod(name, parameterTypes); } catch (final NoSuchMethodException exc) {} return method(c.getSuperclass(), name, parameterTypes); } /** * Gets the array class corresponding to the given element type. *

* For example, {@code arrayType(double.class)} returns {@code double[].class} * . *

* * @param componentType The type of elements which the array possesses * @throws IllegalArgumentException if the type cannot be the component type * of an array (this is the case e.g. for {@code void.class}). */ public static Class array(final Class componentType) { if (componentType == null) return null; // NB: It appears the reflection API has no built-in way to do this. // So unfortunately, we must allocate a new object and then inspect it. return Array.newInstance(componentType, 0).getClass(); } /** * Gets the array class corresponding to the given element type and * dimensionality. *

* For example, {@code arrayType(double.class, 2)} returns * {@code double[][].class} . *

* * @param componentType The type of elements which the array possesses * @param dim The dimensionality of the array */ public static Class array(final Class componentType, final int dim) { if (dim < 0) throw iae("Negative dimension"); if (dim == 0) return componentType; return array(array(componentType), dim - 1); } /** * Gets the array type—which might be a {@link Class} or a * {@link GenericArrayType} depending on the argument—corresponding to * the given element type. *

* For example, {@code arrayType(double.class)} returns {@code double[].class} * . *

* * @param componentType The type of elements which the array possesses * @see #component */ public static Type array(final Type componentType) { if (componentType == null) return null; if (componentType instanceof Class) { return array((Class) componentType); } return new TypeUtils.GenericArrayTypeImpl(componentType); } /** * Gets the component type of the given array type, or null if not an array. *

* If you have a {@link Class}, you can call {@link Class#getComponentType()} * for a narrower return type. *

*

* This is the opposite of {@link #array(Type)}. *

*/ public static Type component(final Type type) { if (type instanceof Class) { return ((Class) type).getComponentType(); } if (type instanceof GenericArrayType) { return ((GenericArrayType) type).getGenericComponentType(); } return null; } /** * Returns the "safe" generic type of the given field, as viewed from the * given type. This may be narrower than what {@link Field#getGenericType()} * returns, if the field is declared in a superclass, or {@code type} has a * type parameter that is used in the type of the field. *

* For example, suppose we have the following three classes: *

* *
	 * public class Thing<T> {
	 *
	 * 	public T thing;
	 * }
	 *
	 * public class NumberThing<N extends Number> extends Thing<N> {}
	 *
	 * public class IntegerThing extends NumberThing<Integer> {}
	 * 
* * Then this method operates as follows: * *
	 * field = Types.field(Thing.class, "thing");
	 *
	 * field.getType(); // Object
	 * field.getGenericType(); // T
	 *
	 * Types.fieldType(field, Thing.class); // T
	 * Types.fieldType(field, NumberThing.class); // N extends Number
	 * Types.fieldType(field, IntegerThing.class); // Integer
	 * 
*/ public static Type fieldType(final Field field, final Class type) { final Type wildType = GenericTypeReflector.addWildcardParameters(type); return GenericTypeReflector.getExactFieldType(field, wildType); } /** * As {@link #fieldType(Field, Class)}, but with respect to the return type of * the given {@link Method} rather than a {@link Field}. */ public static Type methodReturnType(final Method method, final Class type) { final Type wildType = GenericTypeReflector.addWildcardParameters(type); return GenericTypeReflector.getExactReturnType(method, wildType); } /** * As {@link #fieldType(Field, Class)}, but with respect to the parameter * types of the given {@link Method} rather than a {@link Field}. */ public static Type[] methodParamTypes(final Method method, final Class type) { final Type wildType = GenericTypeReflector.addWildcardParameters(type); return GenericTypeReflector.getExactParameterTypes(method, wildType); } /** * Gets the given type's {@code n}th type parameter of the specified class. *

* For example, with class {@code StringList implements List}, * {@code Types.param(StringList.class, Collection.class, 0)} returns * {@code String}. *

*/ public static Type param(final Type type, final Class c, final int no) { return GenericTypeReflector.getTypeParameter(type, // c.getTypeParameters()[no]); } /** * Discerns whether it would be legal to assign a reference of type * {@code source} to a reference of type {@code target}. * * @param source The type from which assignment is desired. * @param target The type to which assignment is desired. * @return True if the source is assignable to the target. * @throws NullPointerException if {@code target} is null. * @see Class#isAssignableFrom(Class) */ public static boolean isAssignable(final Type source, final Type target) { return TypeUtils.isAssignable(source, target); } /** * Checks whether the given object can be cast to the specified type. * * @return true If the destination class is assignable from the source * object's class, or if the source object is null and destination * class is non-null. * @see #cast(Object, Class) */ public static boolean isInstance(final Object obj, final Class dest) { if (dest == null) return false; return obj == null || dest.isInstance(obj); } /** * Casts the given object to the specified type, or null if the types are * incompatible. */ public static T cast(final Object src, final Class dest) { if (!isInstance(src, dest)) return null; @SuppressWarnings("unchecked") final T result = (T) src; return result; } /** * Converts the given string value to an enumeration constant of the specified * type. * * @param name The value to convert. * @param dest The type of the enumeration constant. * @return The converted enumeration constant. * @throws IllegalArgumentException if the type is not an enumeration type, or * has no such constant. */ public static T enumValue(final String name, final Class dest) { if (!dest.isEnum()) throw iae("Not an enum type: " + name(dest)); @SuppressWarnings({ "rawtypes", "unchecked" }) final Enum result = Enum.valueOf((Class) dest, name); @SuppressWarnings("unchecked") final T typedResult = (T) result; return typedResult; } /** * Creates a new {@link ParameterizedType} of the given class together with * the specified type arguments. * * @param rawType The class of the {@link ParameterizedType}. * @param typeArgs The type arguments to use in parameterizing it. * @return The newly created {@link ParameterizedType}. */ public static ParameterizedType parameterize(final Class rawType, final Type... typeArgs) { return parameterize(rawType, rawType.getDeclaringClass(), typeArgs); } /** * Creates a new {@link ParameterizedType} of the given class together with * the specified type arguments. * * @param rawType The class of the {@link ParameterizedType}. * @param ownerType The owner type of the parameterized class. * @param typeArgs The type arguments to use in parameterizing it. * @return The newly created {@link ParameterizedType}. */ public static ParameterizedType parameterize(final Class rawType, final Type ownerType, final Type... typeArgs) { return new TypeUtils.ParameterizedTypeImpl(rawType, ownerType, typeArgs); } /** * Creates a new {@link WildcardType} with no upper or lower bounds (i.e., * {@code ?}). * * @return The newly created {@link WildcardType}. */ public static WildcardType wildcard() { return wildcard((Type) null, (Type) null); } /** * Creates a new {@link WildcardType} with the given upper and/or lower bound. * * @param upperBound Upper bound of the wildcard, or null for none. * @param lowerBound Lower bound of the wildcard, or null for none. * @return The newly created {@link WildcardType}. */ public static WildcardType wildcard(final Type upperBound, final Type lowerBound) { return new TypeUtils.WildcardTypeImpl(upperBound, lowerBound); } /** * Creates a new {@link WildcardType} with the given upper and/or lower * bounds. * * @param upperBounds Upper bounds of the wildcard, or null for none. * @param lowerBounds Lower bounds of the wildcard, or null for none. * @return The newly created {@link WildcardType}. */ public static WildcardType wildcard(final Type[] upperBounds, final Type[] lowerBounds) { return new TypeUtils.WildcardTypeImpl(upperBounds, lowerBounds); } /** * Learn, recursively, whether any of the type parameters associated with * {@code type} are bound to variables. * * @param type the type to check for type variables * @return boolean */ public static boolean containsTypeVars(final Type type) { return TypeUtils.containsTypeVariables(type); } /** * Gets the type arguments of a class/interface based on a subtype. For * instance, this method will determine that both of the parameters for the * interface {@link Map} are {@link Object} for the subtype * {@link java.util.Properties Properties} even though the subtype does not * directly implement the {@code Map} interface. *

* This method returns {@code null} if {@code type} is not assignable to * {@code toClass}. It returns an empty map if none of the classes or * interfaces in its inheritance hierarchy specify any type arguments. *

*

* A side effect of this method is that it also retrieves the type arguments * for the classes and interfaces that are part of the hierarchy between * {@code type} and {@code toClass}. So with the above example, this method * will also determine that the type arguments for {@link java.util.Hashtable * Hashtable} are also both {@code Object}. In cases where the interface * specified by {@code toClass} is (indirectly) implemented more than once * (e.g. where {@code toClass} specifies the interface * {@link java.lang.Iterable Iterable} and {@code type} specifies a * parameterized type that implements both {@link java.util.Set Set} and * {@link java.util.Collection Collection}), this method will look at the * inheritance hierarchy of only one of the implementations/subclasses; the * first interface encountered that isn't a subinterface to one of the others * in the {@code type} to {@code toClass} hierarchy. *

* * @param type the type from which to determine the type parameters of * {@code toClass} * @param toClass the class whose type parameters are to be determined based * on the subtype {@code type} * @return a {@code Map} of the type assignments for the type variables in * each type in the inheritance hierarchy from {@code type} to * {@code toClass} inclusive. */ public static Map, Type> args(final Type type, final Class toClass) { return TypeUtils.getTypeArguments(type, toClass); } /** * Tries to determine the type arguments of a class/interface based on a super * parameterized type's type arguments. This method is the inverse of * {@link #args(Type, Class)} which gets a class/interface's type arguments * based on a subtype. It is far more limited in determining the type * arguments for the subject class's type variables in that it can only * determine those parameters that map from the subject {@link Class} object * to the supertype. *

* Example: {@link java.util.TreeSet TreeSet} sets its parameter as the * parameter for {@link java.util.NavigableSet NavigableSet}, which in turn * sets the parameter of {@link java.util.SortedSet}, which in turn sets the * parameter of {@link Set}, which in turn sets the parameter of * {@link java.util.Collection}, which in turn sets the parameter of * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps * (indirectly) to {@code Iterable}'s parameter, it will be able to determine * that based on the super type {@code Iterable>>}, the parameter of {@code TreeSet} * is {@code ? extends Map>}. *

* * @param c the class whose type parameters are to be determined, not * {@code null} * @param superType the super type from which {@code c}'s type arguments are * to be determined, not {@code null} * @return a {@code Map} of the type assignments that could be determined for * the type variables in each type in the inheritance hierarchy from * {@code type} to {@code c} inclusive. */ public static Map, Type> args(final Class c, final ParameterizedType superType) { return TypeUtils.determineTypeArguments(c, superType); } /** * Create a parameterized type instance. * * @param raw the raw class to create a parameterized type instance for * @param typeArgMappings the mapping used for parameterization * @return {@link ParameterizedType} */ public static final ParameterizedType parameterize(final Class raw, final Map, Type> typeArgMappings) { return TypeUtils.parameterize(raw, typeArgMappings); } // -- Helper methods -- private static IllegalArgumentException iae(final String... s) { return iae(null, s); } private static IllegalArgumentException iae(final Throwable cause, final String... notes) { final String s = String.join(", ", notes); final IllegalArgumentException exc = new IllegalArgumentException(s); if (cause != null) exc.initCause(cause); throw exc; } private static Class arrayOrNull(final Class componentType) { try { return Types.array(componentType); } catch (final IllegalArgumentException exc) { return null; } } // -- BEGIN FORK OF APACHE COMMONS LANG 3.4 CODE -- /* * 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. */ /** *

* Utility methods focusing on type inspection, particularly with regard to * generics. *

* * @since 3.0 * @version $Id: TypeUtils.java 1606051 2014-06-27 12:22:17Z ggregory $ */ @SuppressWarnings("unused") private static class TypeUtils { /** * {@link WildcardType} builder. * * @since 3.2 */ public static class WildcardTypeBuilder { /** * Constructor */ private WildcardTypeBuilder() {} private Type[] upperBounds; private Type[] lowerBounds; /** * Specify upper bounds of the wildcard type to build. * * @param bounds to set * @return {@code this} */ public WildcardTypeBuilder withUpperBounds(final Type... bounds) { this.upperBounds = bounds; return this; } /** * Specify lower bounds of the wildcard type to build. * * @param bounds to set * @return {@code this} */ public WildcardTypeBuilder withLowerBounds(final Type... bounds) { this.lowerBounds = bounds; return this; } public WildcardType build() { return new WildcardTypeImpl(upperBounds, lowerBounds); } } /** * GenericArrayType implementation class. * * @since 3.2 */ private static final class GenericArrayTypeImpl implements GenericArrayType { private final Type componentType; /** * Constructor * * @param componentType of this array type */ private GenericArrayTypeImpl(final Type componentType) { this.componentType = componentType; } @Override public Type getGenericComponentType() { return componentType; } @Override public String toString() { return TypeUtils.toString(this); } @Override public boolean equals(final Object obj) { return obj == this || obj instanceof GenericArrayType && TypeUtils .equals(this, (GenericArrayType) obj); } @Override public int hashCode() { int result = 67 << 4; result |= componentType.hashCode(); return result; } } /** * ParameterizedType implementation class. * * @since 3.2 */ private static final class ParameterizedTypeImpl implements ParameterizedType { private final Class raw; private final Type useOwner; private final Type[] typeArguments; /** * Constructor * * @param raw type * @param useOwner owner type to use, if any * @param typeArguments formal type arguments */ private ParameterizedTypeImpl(final Class raw, final Type useOwner, final Type[] typeArguments) { this.raw = raw; this.useOwner = useOwner; this.typeArguments = typeArguments; } @Override public Type getRawType() { return raw; } @Override public Type getOwnerType() { return useOwner; } @Override public Type[] getActualTypeArguments() { return typeArguments.clone(); } @Override public String toString() { return TypeUtils.toString(this); } @Override public boolean equals(final Object obj) { return obj == this || obj instanceof ParameterizedType && TypeUtils .equals(this, ((ParameterizedType) obj)); } @Override public int hashCode() { int result = 71 << 4; result |= raw.hashCode(); result <<= 4; result |= Objects.hashCode(useOwner); result <<= 8; result |= Arrays.hashCode(typeArguments); return result; } } /** * WildcardType implementation class. * * @since 3.2 */ private static final class WildcardTypeImpl implements WildcardType { private static final Type[] EMPTY_BOUNDS = new Type[0]; private final Type[] upperBounds; private final Type[] lowerBounds; /** * Constructor * * @param upperBound of this type * @param lowerBound of this type */ private WildcardTypeImpl(final Type upperBound, final Type lowerBound) { this(upperBound == null ? null : new Type[] { upperBound }, lowerBound == null ? null : new Type[] { lowerBound }); } /** * Constructor * * @param upperBounds of this type * @param lowerBounds of this type */ private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) { this.upperBounds = upperBounds == null ? EMPTY_BOUNDS : upperBounds; this.lowerBounds = lowerBounds == null ? EMPTY_BOUNDS : lowerBounds; } @Override public Type[] getUpperBounds() { return upperBounds.clone(); } @Override public Type[] getLowerBounds() { return lowerBounds.clone(); } @Override public String toString() { return TypeUtils.toString(this); } @Override public boolean equals(final Object obj) { return obj == this || obj instanceof WildcardType && TypeUtils.equals( this, (WildcardType) obj); } @Override public int hashCode() { int result = 73 << 8; result |= Arrays.hashCode(upperBounds); result <<= 8; result |= Arrays.hashCode(lowerBounds); return result; } } /** * A wildcard instance matching {@code ?}. * * @since 3.2 */ public static final WildcardType WILDCARD_ALL = // wildcardType().withUpperBounds(Object.class).build(); /** *

* Checks if the subject type may be implicitly cast to the target type * following the Java generics rules. If both types are {@link Class} * objects, the method returns the result of * {@link Class#isAssignableFrom(Class)}. *

* * @param type the subject type to be assigned to the target type * @param toType the target type * @return {@code true} if {@code type} is assignable to {@code toType}. * @throws NullPointerException if {@code toType} is null. */ public static boolean isAssignable(final Type type, final Type toType) { if (toType == null) { throw new NullPointerException("Destination type is null"); } return isAssignable(type, toType, null); } /** *

* Checks if the subject type may be implicitly cast to the target type * following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toType the target type * @param typeVarAssigns optional map of type variable assignments * @return {@code true} if {@code type} is assignable to {@code toType}. */ private static boolean isAssignable(final Type type, final Type toType, final Map, Type> typeVarAssigns) { if (toType == null || toType instanceof Class) { return isAssignable(type, (Class) toType); } if (toType instanceof ParameterizedType) { return isAssignable(type, (ParameterizedType) toType, typeVarAssigns); } if (toType instanceof GenericArrayType) { return isAssignable(type, (GenericArrayType) toType, typeVarAssigns); } if (toType instanceof WildcardType) { return isAssignable(type, (WildcardType) toType, typeVarAssigns); } if (toType instanceof TypeVariable) { return isAssignable(type, (TypeVariable) toType, typeVarAssigns); } throw new IllegalStateException("found an unhandled type: " + toType); } /** *

* Checks if the subject type may be implicitly cast to the target class * following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toClass the target class * @return {@code true} if {@code type} is assignable to {@code toClass}. */ private static boolean isAssignable(final Type type, final Class toClass) { if (type == null) { // consistency with ClassUtils.isAssignable() behavior return toClass == null || !toClass.isPrimitive(); } // only a null type can be assigned to null type which // would have cause the previous to return true if (toClass == null) { return false; } // all types are assignable to themselves if (toClass.equals(type)) { return true; } if (type instanceof Class) { // just comparing two classes return toClass.isAssignableFrom((Class) type); } if (type instanceof ParameterizedType) { // only have to compare the raw type to the class return isAssignable(getRawType((ParameterizedType) type), toClass); } // * if (type instanceof TypeVariable) { // if any of the bounds are assignable to the class, then the // type is assignable to the class. for (final Type bound : ((TypeVariable) type).getBounds()) { if (isAssignable(bound, toClass)) { return true; } } return false; } // the only classes to which a generic array type can be assigned // are class Object and array classes if (type instanceof GenericArrayType) { return toClass.equals(Object.class) || toClass.isArray() && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass.getComponentType()); } // wildcard types are not assignable to a class (though one would think // "? super Object" would be assignable to Object) if (type instanceof WildcardType) { return false; } throw new IllegalStateException("found an unhandled type: " + type); } /** *

* Checks if the subject type may be implicitly cast to the target * parameterized type following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toParameterizedType the target parameterized type * @param typeVarAssigns a map with type variables * @return {@code true} if {@code type} is assignable to {@code toType}. */ private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType, final Map, Type> typeVarAssigns) { if (type == null) { return true; } // only a null type can be assigned to null type which // would have cause the previous to return true if (toParameterizedType == null) { return false; } // all types are assignable to themselves if (toParameterizedType.equals(type)) { return true; } // get the target type's raw type final Class toClass = getRawType(toParameterizedType); // get the subject type's type arguments including owner type arguments // and supertype arguments up to and including the target class. final Map, Type> fromTypeVarAssigns = getTypeArguments( type, toClass, null); // null means the two types are not compatible if (fromTypeVarAssigns == null) { return false; } // compatible types, but there's no type arguments. this is equivalent // to comparing Map< ?, ? > to Map, and raw types are always assignable // to parameterized types. if (fromTypeVarAssigns.isEmpty()) { return true; } // get the target type's type arguments including owner type arguments final Map, Type> toTypeVarAssigns = getTypeArguments( toParameterizedType, toClass, typeVarAssigns); // now to check each type argument for (final TypeVariable var : toTypeVarAssigns.keySet()) { final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns); final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns); // parameters must either be absent from the subject type, within // the bounds of the wildcard type, or be an exact match to the // parameters of the target type. if (fromTypeArg != null && !toTypeArg.equals(fromTypeArg) && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, typeVarAssigns))) { return false; } } return true; } /** * Look up {@code var} in {@code typeVarAssigns} transitively, i.e. * keep looking until the value found is not a type variable. * * @param var the type variable to look up * @param typeVarAssigns the map used for the look up * @return Type or {@code null} if some variable was not in the map * @since 3.2 */ private static Type unrollVariableAssignments(TypeVariable var, final Map, Type> typeVarAssigns) { Type result; do { result = typeVarAssigns.get(var); if (result instanceof TypeVariable && !result.equals(var)) { var = (TypeVariable) result; continue; } break; } while (true); return result; } /** *

* Checks if the subject type may be implicitly cast to the target generic * array type following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toGenericArrayType the target generic array type * @param typeVarAssigns a map with type variables * @return {@code true} if {@code type} is assignable to * {@code toGenericArrayType}. */ private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType, final Map, Type> typeVarAssigns) { if (type == null) { return true; } // only a null type can be assigned to null type which // would have cause the previous to return true if (toGenericArrayType == null) { return false; } // all types are assignable to themselves if (toGenericArrayType.equals(type)) { return true; } final Type toComponentType = toGenericArrayType.getGenericComponentType(); if (type instanceof Class) { final Class cls = (Class) type; // compare the component types return cls.isArray() && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns); } if (type instanceof GenericArrayType) { // compare the component types return isAssignable(((GenericArrayType) type).getGenericComponentType(), toComponentType, typeVarAssigns); } if (type instanceof WildcardType) { // so long as one of the upper bounds is assignable, it's good for (final Type bound : getImplicitUpperBounds((WildcardType) type)) { if (isAssignable(bound, toGenericArrayType)) { return true; } } return false; } if (type instanceof TypeVariable) { // probably should remove the following logic and just return false. // type variables cannot specify arrays as bounds. for (final Type bound : getImplicitBounds((TypeVariable) type)) { if (isAssignable(bound, toGenericArrayType)) { return true; } } return false; } if (type instanceof ParameterizedType) { // the raw type of a parameterized type is never an array or // generic array, otherwise the declaration would look like this: // Collection[]< ? extends String > collection; return false; } throw new IllegalStateException("found an unhandled type: " + type); } /** *

* Checks if the subject type may be implicitly cast to the target wildcard * type following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toWildcardType the target wildcard type * @param typeVarAssigns a map with type variables * @return {@code true} if {@code type} is assignable to * {@code toWildcardType}. */ private static boolean isAssignable(final Type type, final WildcardType toWildcardType, final Map, Type> typeVarAssigns) { if (type == null) { return true; } // only a null type can be assigned to null type which // would have cause the previous to return true if (toWildcardType == null) { return false; } // all types are assignable to themselves if (toWildcardType.equals(type)) { return true; } final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType); final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType); if (type instanceof WildcardType) { final WildcardType wildcardType = (WildcardType) type; final Type[] upperBounds = getImplicitUpperBounds(wildcardType); final Type[] lowerBounds = getImplicitLowerBounds(wildcardType); for (Type toBound : toUpperBounds) { // if there are assignments for unresolved type variables, // now's the time to substitute them. toBound = substituteTypeVariables(toBound, typeVarAssigns); // each upper bound of the subject type has to be assignable to // each // upper bound of the target type for (final Type bound : upperBounds) { if (!isAssignable(bound, toBound, typeVarAssigns)) { return false; } } } for (Type toBound : toLowerBounds) { // if there are assignments for unresolved type variables, // now's the time to substitute them. toBound = substituteTypeVariables(toBound, typeVarAssigns); // each lower bound of the target type has to be assignable to // each // lower bound of the subject type for (final Type bound : lowerBounds) { if (!isAssignable(toBound, bound, typeVarAssigns)) { return false; } } } return true; } for (final Type toBound : toUpperBounds) { // if there are assignments for unresolved type variables, // now's the time to substitute them. if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), typeVarAssigns)) { return false; } } for (final Type toBound : toLowerBounds) { // if there are assignments for unresolved type variables, // now's the time to substitute them. if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, typeVarAssigns)) { return false; } } return true; } /** *

* Checks if the subject type may be implicitly cast to the target type * variable following the Java generics rules. *

* * @param type the subject type to be assigned to the target type * @param toTypeVariable the target type variable * @param typeVarAssigns a map with type variables * @return {@code true} if {@code type} is assignable to * {@code toTypeVariable}. */ private static boolean isAssignable(final Type type, final TypeVariable toTypeVariable, final Map, Type> typeVarAssigns) { if (type == null) { return true; } // only a null type can be assigned to null type which // would have cause the previous to return true if (toTypeVariable == null) { return false; } // all types are assignable to themselves if (toTypeVariable.equals(type)) { return true; } if (type instanceof TypeVariable) { // a type variable is assignable to another type variable, if // and only if the former is the latter, extends the latter, or // is otherwise a descendant of the latter. final Type[] bounds = getImplicitBounds((TypeVariable) type); for (final Type bound : bounds) { if (isAssignable(bound, toTypeVariable, typeVarAssigns)) { return true; } } } if (type instanceof Class || type instanceof ParameterizedType || type instanceof GenericArrayType || type instanceof WildcardType) { return false; } throw new IllegalStateException("found an unhandled type: " + type); } /** *

* Find the mapping for {@code type} in {@code typeVarAssigns}. *

* * @param type the type to be replaced * @param typeVarAssigns the map with type variables * @return the replaced type * @throws IllegalArgumentException if the type cannot be substituted */ private static Type substituteTypeVariables(final Type type, final Map, Type> typeVarAssigns) { if (type instanceof TypeVariable && typeVarAssigns != null) { final Type replacementType = typeVarAssigns.get(type); if (replacementType == null) { throw new IllegalArgumentException( "missing assignment type for type variable " + type); } return replacementType; } return type; } /** *

* Retrieves all the type arguments for this parameterized type including * owner hierarchy arguments such as {@code Outer.Inner.DeepInner * } . The arguments are returned in a {@link Map} specifying the * argument type for each {@link TypeVariable}. *

* * @param type specifies the subject parameterized type from which to * harvest the parameters. * @return a {@code Map} of the type arguments to their respective type * variables. */ public static Map, Type> getTypeArguments( final ParameterizedType type) { return getTypeArguments(type, getRawType(type), null); } /** *

* Gets the type arguments of a class/interface based on a subtype. For * instance, this method will determine that both of the parameters for the * interface {@link Map} are {@link Object} for the subtype * {@link java.util.Properties Properties} even though the subtype does not * directly implement the {@code Map} interface. *

*

* This method returns {@code null} if {@code type} is not assignable to * {@code toClass}. It returns an empty map if none of the classes or * interfaces in its inheritance hierarchy specify any type arguments. *

*

* A side effect of this method is that it also retrieves the type arguments * for the classes and interfaces that are part of the hierarchy between * {@code type} and {@code toClass}. So with the above example, this method * will also determine that the type arguments for * {@link java.util.Hashtable Hashtable} are also both {@code Object}. In * cases where the interface specified by {@code toClass} is (indirectly) * implemented more than once (e.g. where {@code toClass} specifies the * interface {@link java.lang.Iterable Iterable} and {@code type} specifies * a parameterized type that implements both {@link java.util.Set Set} and * {@link java.util.Collection Collection}), this method will look at the * inheritance hierarchy of only one of the implementations/subclasses; the * first interface encountered that isn't a subinterface to one of the * others in the {@code type} to {@code toClass} hierarchy. *

* * @param type the type from which to determine the type parameters of * {@code toClass} * @param toClass the class whose type parameters are to be determined based * on the subtype {@code type} * @return a {@code Map} of the type assignments for the type variables in * each type in the inheritance hierarchy from {@code type} to * {@code toClass} inclusive. */ public static Map, Type> getTypeArguments(final Type type, final Class toClass) { return getTypeArguments(type, toClass, null); } /** *

* Return a map of the type arguments of @{code type} in the context of * {@code toClass}. *

* * @param type the type in question * @param toClass the class * @param subtypeVarAssigns a map with type variables * @return the {@code Map} with type arguments */ private static Map, Type> getTypeArguments(final Type type, final Class toClass, final Map, Type> subtypeVarAssigns) { if (type instanceof Class) { return getTypeArguments((Class) type, toClass, subtypeVarAssigns); } if (type instanceof ParameterizedType) { return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns); } if (type instanceof GenericArrayType) { return getTypeArguments(((GenericArrayType) type) .getGenericComponentType(), toClass.isArray() ? toClass .getComponentType() : toClass, subtypeVarAssigns); } // since wildcard types are not assignable to classes, should this just // return null? if (type instanceof WildcardType) { for (final Type bound : getImplicitUpperBounds((WildcardType) type)) { // find the first bound that is assignable to the target class if (isAssignable(bound, toClass)) { return getTypeArguments(bound, toClass, subtypeVarAssigns); } } return null; } if (type instanceof TypeVariable) { for (final Type bound : getImplicitBounds((TypeVariable) type)) { // find the first bound that is assignable to the target class if (isAssignable(bound, toClass)) { return getTypeArguments(bound, toClass, subtypeVarAssigns); } } return null; } throw new IllegalStateException("found an unhandled type: " + type); } /** *

* Return a map of the type arguments of a parameterized type in the context * of {@code toClass}. *

* * @param parameterizedType the parameterized type * @param toClass the class * @param subtypeVarAssigns a map with type variables * @return the {@code Map} with type arguments */ private static Map, Type> getTypeArguments( final ParameterizedType parameterizedType, final Class toClass, final Map, Type> subtypeVarAssigns) { final Class cls = getRawType(parameterizedType); // make sure they're assignable if (!isAssignable(cls, toClass)) { return null; } final Type ownerType = parameterizedType.getOwnerType(); Map, Type> typeVarAssigns; if (ownerType instanceof ParameterizedType) { // get the owner type arguments first final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType; typeVarAssigns = getTypeArguments(parameterizedOwnerType, getRawType( parameterizedOwnerType), subtypeVarAssigns); } else { // no owner, prep the type variable assignments map typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns); } // get the subject parameterized type's arguments final Type[] typeArgs = parameterizedType.getActualTypeArguments(); // and get the corresponding type variables from the raw class final TypeVariable[] typeParams = cls.getTypeParameters(); // map the arguments to their respective type variables for (int i = 0; i < typeParams.length; i++) { final Type typeArg = typeArgs[i]; typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg) ? typeVarAssigns.get(typeArg) : typeArg); } if (toClass.equals(cls)) { // target class has been reached. Done. return typeVarAssigns; } // walk the inheritance hierarchy until the target class is reached return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); } /** *

* Return a map of the type arguments of a class in the context of @{code * toClass}. *

* * @param cls the class in question * @param toClass the context class * @param subtypeVarAssigns a map with type variables * @return the {@code Map} with type arguments */ private static Map, Type> getTypeArguments(Class cls, final Class toClass, final Map, Type> subtypeVarAssigns) { // make sure they're assignable if (!isAssignable(cls, toClass)) { return null; } // can't work with primitives if (cls.isPrimitive()) { // both classes are primitives? if (toClass.isPrimitive()) { // dealing with widening here. No type arguments to be // harvested with these two types. return new HashMap<>(); } // work with wrapper the wrapper class instead of the primitive cls = Types.box(cls); } // create a copy of the incoming map, or an empty one if it's null final HashMap, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>( subtypeVarAssigns); // has target class been reached? if (toClass.equals(cls)) { return typeVarAssigns; } // walk the inheritance hierarchy until the target class is reached return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); } /** *

* Tries to determine the type arguments of a class/interface based on a * super parameterized type's type arguments. This method is the inverse of * {@link #getTypeArguments(Type, Class)} which gets a class/interface's * type arguments based on a subtype. It is far more limited in determining * the type arguments for the subject class's type variables in that it can * only determine those parameters that map from the subject {@link Class} * object to the supertype. *

*

* Example: {@link java.util.TreeSet TreeSet} sets its parameter as the * parameter for {@link java.util.NavigableSet NavigableSet}, which in turn * sets the parameter of {@link java.util.SortedSet}, which in turn sets the * parameter of {@link Set}, which in turn sets the parameter of * {@link java.util.Collection}, which in turn sets the parameter of * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps * (indirectly) to {@code Iterable}'s parameter, it will be able to * determine that based on the super type {@code Iterable>>}, the parameter of {@code TreeSet} * is {@code ? extends Map>}. *

* * @param cls the class whose type parameters are to be determined, not * {@code null} * @param superType the super type from which {@code cls}'s type arguments * are to be determined, not {@code null} * @return a {@code Map} of the type assignments that could be determined * for the type variables in each type in the inheritance hierarchy * from {@code type} to {@code toClass} inclusive. */ public static Map, Type> determineTypeArguments( final Class cls, final ParameterizedType superType) { validateNotNull(cls, "cls is null"); validateNotNull(superType, "superType is null"); final Class superClass = getRawType(superType); // compatibility check if (!isAssignable(cls, superClass)) { return null; } if (cls.equals(superClass)) { return getTypeArguments(superType, superClass, null); } // get the next class in the inheritance hierarchy final Type midType = getClosestParentType(cls, superClass); // can only be a class or a parameterized type if (midType instanceof Class) { return determineTypeArguments((Class) midType, superType); } final ParameterizedType midParameterizedType = (ParameterizedType) midType; final Class midClass = getRawType(midParameterizedType); // get the type variables of the mid class that map to the type // arguments of the super class final Map, Type> typeVarAssigns = determineTypeArguments( midClass, superType); // map the arguments of the mid type to the class type variables mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns); return typeVarAssigns; } /** *

* Performs a mapping of type variables. *

* * @param the generic type of the class in question * @param cls the class in question * @param parameterizedType the parameterized type * @param typeVarAssigns the map to be filled */ private static void mapTypeVariablesToArguments(final Class cls, final ParameterizedType parameterizedType, final Map, Type> typeVarAssigns) { // capture the type variables from the owner type that have assignments final Type ownerType = parameterizedType.getOwnerType(); if (ownerType instanceof ParameterizedType) { // recursion to make sure the owner's owner type gets processed mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns); } // parameterizedType is a generic interface/class (or it's in the owner // hierarchy of said interface/class) implemented/extended by the class // cls. Find out which type variables of cls are type arguments of // parameterizedType: final Type[] typeArgs = parameterizedType.getActualTypeArguments(); // of the cls's type variables that are arguments of parameterizedType, // find out which ones can be determined from the super type's arguments final TypeVariable[] typeVars = getRawType(parameterizedType) .getTypeParameters(); // use List view of type parameters of cls so the contains() method can be // used: final List>> typeVarList = Arrays.asList(cls .getTypeParameters()); for (int i = 0; i < typeArgs.length; i++) { final TypeVariable typeVar = typeVars[i]; final Type typeArg = typeArgs[i]; // argument of parameterizedType is a type variable of cls if (typeVarList.contains(typeArg) // type variable of parameterizedType has an assignment in // the super type. && typeVarAssigns.containsKey(typeVar)) { // map the assignment to the cls's type variable typeVarAssigns.put((TypeVariable) typeArg, typeVarAssigns.get( typeVar)); } } } /** *

* Get the closest parent type to the super class specified by * {@code superClass}. *

* * @param cls the class in question * @param superClass the super class * @return the closes parent type */ private static Type getClosestParentType(final Class cls, final Class superClass) { // only look at the interfaces if the super class is also an interface if (superClass.isInterface()) { // get the generic interfaces of the subject class final Type[] interfaceTypes = cls.getGenericInterfaces(); // will hold the best generic interface match found Type genericInterface = null; // find the interface closest to the super class for (final Type midType : interfaceTypes) { Class midClass = null; if (midType instanceof ParameterizedType) { midClass = getRawType((ParameterizedType) midType); } else if (midType instanceof Class) { midClass = (Class) midType; } else { throw new IllegalStateException("Unexpected generic" + " interface type found: " + midType); } // check if this interface is further up the inheritance chain // than the previously found match if (isAssignable(midClass, superClass) && isAssignable( genericInterface, (Type) midClass)) { genericInterface = midType; } } // found a match? if (genericInterface != null) { return genericInterface; } } // none of the interfaces were descendants of the target class, so the // super class has to be one, instead return cls.getGenericSuperclass(); } /** *

* Checks if the given value can be assigned to the target type following * the Java generics rules. *

* * @param value the value to be checked * @param type the target type * @return {@code true} if {@code value} is an instance of {@code type}. */ public static boolean isInstance(final Object value, final Type type) { if (type == null) { return false; } return value == null ? !(type instanceof Class) || !((Class) type) .isPrimitive() : isAssignable(value.getClass(), type, null); } /** *

* This method strips out the redundant upper bound types in type variable * types and wildcard types (or it would with wildcard types if multiple * upper bounds were allowed). *

*

* Example, with the variable type declaration: * *

		 * <K extends java.util.Collection<String> &
		 * java.util.List<String>>
		 * 
*

* since {@code List} is a subinterface of {@code Collection}, this method * will return the bounds as if the declaration had been: *

* *
		 * <K extends java.util.List<String>>
		 * 
* * @param bounds an array of types representing the upper bounds of either * {@link WildcardType} or {@link TypeVariable}, not {@code null}. * @return an array containing the values from {@code bounds} minus the * redundant types. */ public static Type[] normalizeUpperBounds(final Type[] bounds) { validateNotNull(bounds, "null value specified for bounds array"); // don't bother if there's only one (or none) type if (bounds.length < 2) { return bounds; } final Set types = new HashSet<>(bounds.length); for (final Type type1 : bounds) { boolean subtypeFound = false; for (final Type type2 : bounds) { if (type1 != type2 && isAssignable(type2, type1, null)) { subtypeFound = true; break; } } if (!subtypeFound) { types.add(type1); } } return types.toArray(new Type[types.size()]); } /** *

* Returns an array containing the sole type of {@link Object} if * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it * returns the result of {@link TypeVariable#getBounds()} passed into * {@link #normalizeUpperBounds}. *

* * @param typeVariable the subject type variable, not {@code null} * @return a non-empty array containing the bounds of the type variable. */ public static Type[] getImplicitBounds(final TypeVariable typeVariable) { validateNotNull(typeVariable, "typeVariable is null"); final Type[] bounds = typeVariable.getBounds(); return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); } /** *

* Returns an array containing the sole value of {@link Object} if * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, * it returns the result of {@link WildcardType#getUpperBounds()} passed * into {@link #normalizeUpperBounds}. *

* * @param wildcardType the subject wildcard type, not {@code null} * @return a non-empty array containing the upper bounds of the wildcard * type. */ public static Type[] getImplicitUpperBounds( final WildcardType wildcardType) { validateNotNull(wildcardType, "wildcardType is null"); final Type[] bounds = wildcardType.getUpperBounds(); return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); } /** *

* Returns an array containing a single value of {@code null} if * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, * it returns the result of {@link WildcardType#getLowerBounds()}. *

* * @param wildcardType the subject wildcard type, not {@code null} * @return a non-empty array containing the lower bounds of the wildcard * type. */ public static Type[] getImplicitLowerBounds( final WildcardType wildcardType) { validateNotNull(wildcardType, "wildcardType is null"); final Type[] bounds = wildcardType.getLowerBounds(); return bounds.length == 0 ? new Type[] { null } : bounds; } /** *

* Determines whether or not specified types satisfy the bounds of their * mapped type variables. When a type parameter extends another (such as * {@code }), uses another as a type parameter (such as * {@code >}), or otherwise depends on another type * variable to be specified, the dependencies must be included in * {@code typeVarAssigns}. *

* * @param typeVarAssigns specifies the potential types to be assigned to the * type variables, not {@code null}. * @return whether or not the types can be assigned to their respective type * variables. */ public static boolean typesSatisfyVariables( final Map, Type> typeVarAssigns) { validateNotNull(typeVarAssigns, "typeVarAssigns is null"); // all types must be assignable to all the bounds of the their mapped // type variable. for (final Map.Entry, Type> entry : typeVarAssigns .entrySet()) { final TypeVariable typeVar = entry.getKey(); final Type type = entry.getValue(); for (final Type bound : getImplicitBounds(typeVar)) { if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns), typeVarAssigns)) { return false; } } } return true; } /** *

* Transforms the passed in type to a {@link Class} object. Type-checking * method of convenience. *

* * @param parameterizedType the type to be converted * @return the corresponding {@code Class} object * @throws IllegalStateException if the conversion fails */ private static Class getRawType( final ParameterizedType parameterizedType) { final Type rawType = parameterizedType.getRawType(); // check if raw type is a Class object // not currently necessary, but since the return type is Type instead of // Class, there's enough reason to believe that future versions of Java // may return other Type implementations. And type-safety checking is // rarely a bad idea. if (!(rawType instanceof Class)) { throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType); } return (Class) rawType; } /** *

* Get the raw type of a Java type, given its context. Primarily for use * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do * not know the runtime type of {@code type}: if you know you have a * {@link Class} instance, it is already raw; if you know you have a * {@link ParameterizedType}, its raw type is only a method call away. *

* * @param type to resolve * @param assigningType type to be resolved against * @return the resolved {@link Class} object or {@code null} if the type * could not be resolved */ public static Class getRawType(final Type type, final Type assigningType) { if (type instanceof Class) { // it is raw, no problem return (Class) type; } if (type instanceof ParameterizedType) { // simple enough to get the raw type of a ParameterizedType return getRawType((ParameterizedType) type); } if (type instanceof TypeVariable) { if (assigningType == null) { return null; } // get the entity declaring this type variable final Object genericDeclaration = ((TypeVariable) type) .getGenericDeclaration(); // can't get the raw type of a method- or constructor-declared type // variable if (!(genericDeclaration instanceof Class)) { return null; } // get the type arguments for the declaring class/interface based // on the enclosing type final Map, Type> typeVarAssigns = getTypeArguments( assigningType, (Class) genericDeclaration); // enclosingType has to be a subclass (or subinterface) of the // declaring type if (typeVarAssigns == null) { return null; } // get the argument assigned to this type variable final Type typeArgument = typeVarAssigns.get(type); if (typeArgument == null) { return null; } // get the argument for this type variable return getRawType(typeArgument, assigningType); } if (type instanceof GenericArrayType) { // get raw component type final Class rawComponentType = getRawType(((GenericArrayType) type) .getGenericComponentType(), assigningType); // return the corresponding array type return array(rawComponentType); } // (hand-waving) this is not the method you're looking for if (type instanceof WildcardType) { return null; } throw new IllegalArgumentException("unknown type: " + type); } /** * Learn whether the specified type denotes an array type. * * @param type the type to be checked * @return {@code true} if {@code type} is an array class or a * {@link GenericArrayType}. */ public static boolean isArrayType(final Type type) { return type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray(); } /** * Get the array component type of {@code type}. * * @param type the type to be checked * @return component type or null if type is not an array type */ public static Type getArrayComponentType(final Type type) { if (type instanceof Class) { final Class clazz = (Class) type; return clazz.isArray() ? clazz.getComponentType() : null; } if (type instanceof GenericArrayType) { return ((GenericArrayType) type).getGenericComponentType(); } return null; } /** * Get a type representing {@code type} with variable assignments * "unrolled." * * @param typeArguments as from * {@link TypeUtils#getTypeArguments(Type, Class)} * @param type the type to unroll variable assignments for * @return Type * @since 3.2 */ public static Type unrollVariables(Map, Type> typeArguments, final Type type) { if (typeArguments == null) { typeArguments = Collections., Type> emptyMap(); } if (containsTypeVariables(type)) { if (type instanceof TypeVariable) { return unrollVariables(typeArguments, typeArguments.get(type)); } if (type instanceof ParameterizedType) { final ParameterizedType p = (ParameterizedType) type; final Map, Type> parameterizedTypeArguments; if (p.getOwnerType() == null) { parameterizedTypeArguments = typeArguments; } else { parameterizedTypeArguments = new HashMap<>(typeArguments); parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p)); } final Type[] args = p.getActualTypeArguments(); for (int i = 0; i < args.length; i++) { final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]); if (unrolled != null) { args[i] = unrolled; } } return parameterizeWithOwner(p.getOwnerType(), (Class) p .getRawType(), args); } if (type instanceof WildcardType) { final WildcardType wild = (WildcardType) type; return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild .getUpperBounds())).withLowerBounds(unrollBounds(typeArguments, wild .getLowerBounds())).build(); } } return type; } /** * Local helper method to unroll variables in a type bounds array. * * @param typeArguments assignments {@link Map} * @param bounds in which to expand variables * @return {@code bounds} with any variables reassigned * @since 3.2 */ private static Type[] unrollBounds( final Map, Type> typeArguments, final Type[] bounds) { final ArrayList result = new ArrayList<>(); for (final Type bound : bounds) { final Type unrolled = unrollVariables(typeArguments, bound); if (unrolled != null) result.add(unrolled); } return result.toArray(new Type[result.size()]); } /** * Learn, recursively, whether any of the type parameters associated with * {@code type} are bound to variables. * * @param type the type to check for type variables * @return boolean * @since 3.2 */ public static boolean containsTypeVariables(final Type type) { if (type instanceof TypeVariable) { return true; } if (type instanceof Class) { return ((Class) type).getTypeParameters().length > 0; } if (type instanceof ParameterizedType) { for (final Type arg : ((ParameterizedType) type) .getActualTypeArguments()) { if (containsTypeVariables(arg)) { return true; } } return false; } if (type instanceof WildcardType) { final WildcardType wild = (WildcardType) type; return containsTypeVariables(TypeUtils.getImplicitLowerBounds( wild)[0]) || containsTypeVariables(TypeUtils.getImplicitUpperBounds( wild)[0]); } return false; } /** * Create a parameterized type instance. * * @param raw the raw class to create a parameterized type instance for * @param typeArguments the types used for parameterization * @return {@link ParameterizedType} * @since 3.2 */ public static final ParameterizedType parameterize(final Class raw, final Type... typeArguments) { return parameterizeWithOwner(null, raw, typeArguments); } /** * Create a parameterized type instance. * * @param raw the raw class to create a parameterized type instance for * @param typeArgMappings the mapping used for parameterization * @return {@link ParameterizedType} * @since 3.2 */ public static final ParameterizedType parameterize(final Class raw, final Map, Type> typeArgMappings) { validateNotNull(raw, "raw class is null"); validateNotNull(typeArgMappings, "typeArgMappings is null"); return parameterizeWithOwner(null, raw, extractTypeArgumentsFrom( typeArgMappings, raw.getTypeParameters())); } /** * Create a parameterized type instance. * * @param owner the owning type * @param raw the raw class to create a parameterized type instance for * @param typeArguments the types used for parameterization * @return {@link ParameterizedType} * @since 3.2 */ public static final ParameterizedType parameterizeWithOwner( final Type owner, final Class raw, final Type... typeArguments) { validateNotNull(raw, "raw class is null"); final Type useOwner; if (raw.getEnclosingClass() == null) { validateIsTrue(owner == null, "no owner allowed for top-level %s", raw); useOwner = null; } else if (owner == null) { useOwner = raw.getEnclosingClass(); } else { validateIsTrue(TypeUtils.isAssignable(owner, raw.getEnclosingClass()), "%s is invalid owner type for parameterized %s", owner, raw); useOwner = owner; } validateNoNullElements(typeArguments, "null type argument at index %s"); validateIsTrue(raw.getTypeParameters().length == typeArguments.length, "invalid number of type parameters specified: expected %s, got %s", raw .getTypeParameters().length, typeArguments.length); return new ParameterizedTypeImpl(raw, useOwner, typeArguments); } /** * Create a parameterized type instance. * * @param owner the owning type * @param raw the raw class to create a parameterized type instance for * @param typeArgMappings the mapping used for parameterization * @return {@link ParameterizedType} * @since 3.2 */ public static final ParameterizedType parameterizeWithOwner( final Type owner, final Class raw, final Map, Type> typeArgMappings) { validateNotNull(raw, "raw class is null"); validateNotNull(typeArgMappings, "typeArgMappings is null"); return parameterizeWithOwner(owner, raw, extractTypeArgumentsFrom( typeArgMappings, raw.getTypeParameters())); } /** * Helper method to establish the formal parameters for a parameterized * type. * * @param mappings map containing the assignements * @param variables expected map keys * @return array of map values corresponding to specified keys */ private static Type[] extractTypeArgumentsFrom( final Map, Type> mappings, final TypeVariable[] variables) { final Type[] result = new Type[variables.length]; int index = 0; for (final TypeVariable var : variables) { validateIsTrue(mappings.containsKey(var), "missing argument mapping for %s", toString(var)); result[index++] = mappings.get(var); } return result; } /** * Get a {@link WildcardTypeBuilder}. * * @return {@link WildcardTypeBuilder} * @since 3.2 */ public static WildcardTypeBuilder wildcardType() { return new WildcardTypeBuilder(); } /** * Create a generic array type instance. * * @param componentType the type of the elements of the array. For example * the component type of {@code boolean[]} is {@code boolean} * @return {@link GenericArrayType} * @since 3.2 */ public static GenericArrayType genericArrayType(final Type componentType) { return new GenericArrayTypeImpl(validateNotNull(componentType, "componentType is null")); } /** * Check equality of types. * * @param t1 the first type * @param t2 the second type * @return boolean * @since 3.2 */ public static boolean equals(final Type t1, final Type t2) { if (Objects.equals(t1, t2)) { return true; } if (t1 instanceof ParameterizedType) { return equals((ParameterizedType) t1, t2); } if (t1 instanceof GenericArrayType) { return equals((GenericArrayType) t1, t2); } if (t1 instanceof WildcardType) { return equals((WildcardType) t1, t2); } return false; } /** * Learn whether {@code t} equals {@code p}. * * @param p LHS * @param t RHS * @return boolean * @since 3.2 */ private static boolean equals(final ParameterizedType p, final Type t) { if (t instanceof ParameterizedType) { final ParameterizedType other = (ParameterizedType) t; if (equals(p.getRawType(), other.getRawType()) && equals(p .getOwnerType(), other.getOwnerType())) { return equals(p.getActualTypeArguments(), other .getActualTypeArguments()); } } return false; } /** * Learn whether {@code t} equals {@code a}. * * @param a LHS * @param t RHS * @return boolean * @since 3.2 */ private static boolean equals(final GenericArrayType a, final Type t) { return t instanceof GenericArrayType && equals(a .getGenericComponentType(), ((GenericArrayType) t) .getGenericComponentType()); } /** * Learn whether {@code t} equals {@code w}. * * @param w LHS * @param t RHS * @return boolean * @since 3.2 */ private static boolean equals(final WildcardType w, final Type t) { if (t instanceof WildcardType) { final WildcardType other = (WildcardType) t; return equals(getImplicitLowerBounds(w), getImplicitLowerBounds( other)) && equals(getImplicitUpperBounds(w), getImplicitUpperBounds( other)); } return true; } /** * Learn whether {@code t1} equals {@code t2}. * * @param t1 LHS * @param t2 RHS * @return boolean * @since 3.2 */ private static boolean equals(final Type[] t1, final Type[] t2) { if (t1.length == t2.length) { for (int i = 0; i < t1.length; i++) { if (!equals(t1[i], t2[i])) { return false; } } return true; } return false; } /** * Present a given type as a Java-esque String. * * @param type the type to create a String representation for, not * {@code null} * @return String * @since 3.2 */ public static String toString(final Type type) { return toString(type, new HashSet<>()); } private static String toString(final Type type, final Set done) { validateNotNull(type); if (type instanceof Class) { return classToString((Class) type, done); } if (type instanceof ParameterizedType) { return parameterizedTypeToString((ParameterizedType) type, done); } if (type instanceof WildcardType) { return wildcardTypeToString((WildcardType) type, done); } if (type instanceof CaptureType) { return captureTypeToString((CaptureType) type, done); } if (type instanceof TypeVariable) { return typeVariableToString((TypeVariable) type, done); } if (type instanceof GenericArrayType) { return genericArrayTypeToString((GenericArrayType) type); } throw new IllegalArgumentException("Unknown generic type: " + // type.getClass().getName()); } /** * Format a {@link TypeVariable} including its {@link GenericDeclaration}. * * @param var the type variable to create a String representation for, not * {@code null} * @return String * @since 3.2 */ public static String toLongString(final TypeVariable var) { validateNotNull(var, "var is null"); final StringBuilder buf = new StringBuilder(); final GenericDeclaration d = ((TypeVariable) var) .getGenericDeclaration(); if (d instanceof Class) { Class c = (Class) d; while (true) { if (c.getEnclosingClass() == null) { buf.insert(0, c.getName()); break; } buf.insert(0, c.getSimpleName()).insert(0, '.'); c = c.getEnclosingClass(); } } else if (d instanceof Type) {// not possible as of now buf.append(toString((Type) d)); } else { buf.append(d); } return buf.append(':').append(typeVariableToString(var, new HashSet<>())) .toString(); } // /** // * Wrap the specified {@link Type} in a {@link Typed} wrapper. // * // * @param inferred generic type // * @param type to wrap // * @return Typed<T> // * @since 3.2 // */ // public static Typed wrap(final Type type) { // return new Typed() { // // @Override // public Type getType() { // return type; // } // }; // } // // /** // * Wrap the specified {@link Class} in a {@link Typed} wrapper. // * // * @param generic type // * @param type to wrap // * @return Typed<T> // * @since 3.2 // */ // public static Typed wrap(final Class type) { // return TypeUtils. wrap((Type) type); // } /** * Format a {@link Class} as a {@link String}. * * @param c {@code Class} to format * @param done list of already-encountered types * @return String * @since 3.2 */ private static String classToString(final Class c, final Set done) { final StringBuilder buf = new StringBuilder(); if (c.getEnclosingClass() != null) { buf.append(classToString(c.getEnclosingClass(), done)).append('.') .append(c.getSimpleName()); } else { buf.append(c.getName()); } if (c.getTypeParameters().length > 0) { buf.append('<'); appendAllTo(buf, ", ", done, c.getTypeParameters()); buf.append('>'); } return buf.toString(); } /** * Format a {@link TypeVariable} as a {@link String}. * * @param v {@code TypeVariable} to format * @param done list of already-encountered types * @return String * @since 3.2 */ private static String typeVariableToString(final TypeVariable v, final Set done) { final StringBuilder buf = new StringBuilder(v.getName()); if (done.contains(v)) return buf.toString(); done.add(v); final Type[] bounds = v.getBounds(); if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals( bounds[0]))) { buf.append(" extends "); appendAllTo(buf, " & ", done, v.getBounds()); } return buf.toString(); } /** * Format a {@link ParameterizedType} as a {@link String}. * * @param p {@code ParameterizedType} to format * @param done list of already-encountered types * @return String * @since 3.2 */ private static String parameterizedTypeToString(final ParameterizedType p, final Set done) { final StringBuilder buf = new StringBuilder(); final Type useOwner = p.getOwnerType(); final Class raw = (Class) p.getRawType(); final Type[] typeArguments = p.getActualTypeArguments(); if (useOwner == null) { buf.append(raw.getName()); } else { if (useOwner instanceof Class) { buf.append(((Class) useOwner).getName()); } else { buf.append(useOwner.toString()); } buf.append('.').append(raw.getSimpleName()); } appendAllTo(buf.append('<'), ", ", done, typeArguments).append('>'); return buf.toString(); } /** * Format a {@link WildcardType} as a {@link String}. * * @param w {@code WildcardType} to format * @param done list of already-encountered types * @return String * @since 3.2 */ private static String wildcardTypeToString(final WildcardType w, final Set done) { final StringBuilder buf = new StringBuilder().append('?'); if (done.contains(w)) return buf.toString(); done.add(w); appendTypeBounds(buf, w.getLowerBounds(), w.getUpperBounds(), done); return buf.toString(); } /** * Format a {@link CaptureType} as a {@link String}. * * @param t {@code CaptureType} to format * @param done list of already-encountered types * @return String * @since 3.2 */ private static String captureTypeToString(final CaptureType t, final Set done) { final StringBuilder buf = new StringBuilder().append("capture of ?"); if (done.contains(t)) return buf.toString(); done.add(t); appendTypeBounds(buf, t.getLowerBounds(), t.getUpperBounds(), done); return buf.toString(); } /** * Format a {@link GenericArrayType} as a {@link String}. * * @param g {@code GenericArrayType} to format * @return String * @since 3.2 */ private static String genericArrayTypeToString(final GenericArrayType g) { return String.format("%s[]", toString(g.getGenericComponentType())); } private static void appendTypeBounds(final StringBuilder buf, final Type[] lowerBounds, final Type[] upperBounds, final Set done) { if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) { appendAllTo(buf.append(" super "), " & ", done, lowerBounds); } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) { appendAllTo(buf.append(" extends "), " & ", done, upperBounds); } } /** * Append {@code types} to {@code buf} with separator {@code sep}. * * @param buf destination * @param sep separator * @param done list of already-encountered types * @param types to append * @return {@code buf} * @since 3.2 */ private static StringBuilder appendAllTo(final StringBuilder buf, final String sep, final Set done, final Type... types) { validateNotEmpty(validateNoNullElements(types)); if (types.length > 0) { buf.append(toString(types[0], done)); for (int i = 1; i < types.length; i++) { buf.append(sep).append(toString(types[i], done)); } } return buf; } private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null"; /** Forked from {@code org.apache.commons.lang3.Validate#notNull}. */ private static T validateNotNull(final T object) { return validateNotNull(object, DEFAULT_IS_NULL_EX_MESSAGE); } /** Forked from {@code org.apache.commons.lang3.Validate#notNull}. */ private static T validateNotNull(final T object, final String message, final Object... values) { if (object == null) { throw new NullPointerException(String.format(message, values)); } return object; } /** Forked from {@code org.apache.commons.lang3.Validate#isTrue}. */ private static void validateIsTrue(final boolean expression, final String message, final Object... values) { if (expression == false) { throw new IllegalArgumentException(String.format(message, values)); } } private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE = "The validated array contains null element at index: %d"; /** Forked from {@code org.apache.commons.lang3.Validate#noNullElements}. */ private static T[] validateNoNullElements(final T[] array) { return validateNoNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE); } /** Forked from {@code org.apache.commons.lang3.Validate#noNullElements}. */ private static T[] validateNoNullElements(final T[] array, final String message, final Object... values) { validateNotNull(array); for (int i = 0; i < array.length; i++) { if (array[i] == null) { final Object[] values2 = new Object[values.length + 1]; System.arraycopy(values, 0, values2, 0, values.length); values2[values.length] = Integer.valueOf(i); throw new IllegalArgumentException(String.format(message, values2)); } } return array; } private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty"; /** Forked from {@code org.apache.commons.lang3.Validate#notEmpty}. */ private static T[] validateNotEmpty(final T[] array) { return validateNotEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE); } /** Forked from {@code org.apache.commons.lang3.Validate#notEmpty}. */ private static T[] validateNotEmpty(final T[] array, final String message, final Object... values) { if (array == null) { throw new NullPointerException(String.format(message, values)); } if (array.length == 0) { throw new IllegalArgumentException(String.format(message, values)); } return array; } } // -- END FORK OF APACHE COMMONS LANG 3.4 CODE -- // -- BEGIN FORK OF GENTYREF 1.1.0 CODE -- /** * Utility class for doing reflection on types. * * @author Wouter Coekaerts */ private static class GenericTypeReflector { private static final Type UNBOUND_WILDCARD = new TypeUtils.WildcardTypeImpl( new Type[] { Object.class }, new Type[] {}); /** * Returns the erasure of the given type. */ public static Class erase(final Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return (Class) ((ParameterizedType) type).getRawType(); } else if (type instanceof TypeVariable) { final TypeVariable tv = (TypeVariable) type; if (tv.getBounds().length == 0) return Object.class; return erase(tv.getBounds()[0]); } else if (type instanceof GenericArrayType) { final GenericArrayType aType = (GenericArrayType) type; return array(erase(aType.getGenericComponentType())); } else { // TODO at least support CaptureType here throw new RuntimeException("not supported: " + type.getClass()); } } /** * Maps type parameters in a type to their values. * * @param toMapType Type possibly containing type arguments * @param typeAndParams must be either ParameterizedType, or (in case there * are no type arguments, or it's a raw type) Class * @return toMapType, but with type parameters from typeAndParams replaced. */ private static Type mapTypeParameters(final Type toMapType, final Type typeAndParams) { if (isMissingTypeParameters(typeAndParams)) { return erase(toMapType); } final VarMap varMap = new VarMap(); Type handlingTypeAndParams = typeAndParams; while (handlingTypeAndParams instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) handlingTypeAndParams; // getRawType should always be Class final Class clazz = (Class) pType.getRawType(); varMap.addAll(clazz.getTypeParameters(), pType .getActualTypeArguments()); handlingTypeAndParams = pType.getOwnerType(); } return varMap.map(toMapType); } /** * Checks if the given type is a class that is supposed to have type * parameters, but doesn't. In other words, if it's a really raw type. */ private static boolean isMissingTypeParameters(final Type type) { if (type instanceof Class) { for (Class clazz = (Class) type; clazz != null; clazz = clazz .getEnclosingClass()) { if (clazz.getTypeParameters().length != 0) return true; } return false; } else if (type instanceof ParameterizedType) { return false; } else { throw new AssertionError("Unexpected type " + type.getClass()); } } /** * Returns a type representing the class, with all type parameters the * unbound wildcard ("?"). For example, * addWildcardParameters(Map.class) returns a type representing * Map<?,?>. * * @return *
    *
  • If clazz is a class or interface without type parameters, * clazz itself is returned.
  • *
  • If clazz is a class or interface with type parameters, an * instance of ParameterizedType is returned.
  • *
  • if clazz is an array type, an array type is returned with * unbound wildcard parameters added in the the component type. *
*/ public static Type addWildcardParameters(final Class clazz) { if (clazz.isArray()) { return array(addWildcardParameters(clazz.getComponentType())); } else if (isMissingTypeParameters(clazz)) { final TypeVariable[] vars = clazz.getTypeParameters(); final Type[] arguments = new Type[vars.length]; Arrays.fill(arguments, UNBOUND_WILDCARD); final Type owner = clazz.getDeclaringClass() == null ? null : addWildcardParameters(clazz.getDeclaringClass()); return parameterize(clazz, owner, arguments); } else { return clazz; } } /** * Finds the most specific supertype of type whose erasure is * searchClass. In other words, returns a type representing the * class searchClass plus its exact type parameters in * type. *
    *
  • Returns an instance of {@link ParameterizedType} if * searchClass is a real class or interface and type has * parameters for it
  • *
  • Returns an instance of {@link GenericArrayType} if * searchClass is an array type, and type has type * parameters for it
  • *
  • Returns an instance of {@link Class} if type is a raw type, * or has no type parameters for searchClass
  • *
  • Returns null if searchClass is not a superclass of type. *
  • *
*

* For example, with * class StringList implements List<String>, * getExactSuperType(StringList.class, Collection.class) returns a * {@link ParameterizedType} representing Collection<String>. *

*/ public static Type getExactSuperType(final Type type, final Class searchClass) { if (type instanceof ParameterizedType || type instanceof Class || type instanceof GenericArrayType) { final Class clazz = erase(type); if (searchClass == clazz) { return type; } if (!searchClass.isAssignableFrom(clazz)) return null; } for (final Type superType : getExactDirectSuperTypes(type)) { final Type result = getExactSuperType(superType, searchClass); if (result != null) return result; } return null; } /** * Gets the type parameter for a given type that is the value for a given * type variable. For example, with * class StringList implements List<String>, * getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0]) * returns String. * * @param type The type to inspect. * @param variable The type variable to find the value for. * @return The type parameter for the given variable. Or null if type is not * a subtype of the type that declares the variable, or if the * variable isn't known (because of raw types). */ public static Type getTypeParameter(final Type type, final TypeVariable> variable) { final Class clazz = variable.getGenericDeclaration(); final Type superType = getExactSuperType(type, clazz); if (superType instanceof ParameterizedType) { final int index = Arrays.asList(clazz.getTypeParameters()).indexOf( variable); return ((ParameterizedType) superType).getActualTypeArguments()[index]; } return null; } /** * Checks if the capture of subType is a subtype of superType */ public static boolean isSuperType(final Type superType, final Type subType) { if (superType instanceof ParameterizedType || superType instanceof Class || superType instanceof GenericArrayType) { final Class superClass = erase(superType); final Type mappedSubType = getExactSuperType(capture(subType), superClass); if (mappedSubType == null) { return false; } else if (superType instanceof Class) { return true; } else if (mappedSubType instanceof Class) { // TODO treat supertype by being raw type differently ("supertype, but // with warnings") return true; // class has no parameters, or it's a raw type } else if (mappedSubType instanceof GenericArrayType) { final Type superComponentType = getArrayComponentType(superType); assert superComponentType != null; final Type mappedSubComponentType = getArrayComponentType( mappedSubType); assert mappedSubComponentType != null; return isSuperType(superComponentType, mappedSubComponentType); } else { assert mappedSubType instanceof ParameterizedType; final ParameterizedType pMappedSubType = (ParameterizedType) mappedSubType; assert pMappedSubType.getRawType() == superClass; final ParameterizedType pSuperType = (ParameterizedType) superType; final Type[] superTypeArgs = pSuperType.getActualTypeArguments(); final Type[] subTypeArgs = pMappedSubType.getActualTypeArguments(); assert superTypeArgs.length == subTypeArgs.length; for (int i = 0; i < superTypeArgs.length; i++) { if (!contains(superTypeArgs[i], subTypeArgs[i])) { return false; } } // params of the class itself match, so if the owner types are // supertypes too, it's a supertype. return pSuperType.getOwnerType() == null || isSuperType(pSuperType .getOwnerType(), pMappedSubType.getOwnerType()); } } else if (superType instanceof CaptureType) { if (superType.equals(subType)) return true; for (final Type lowerBound : ((CaptureType) superType) .getLowerBounds()) { if (isSuperType(lowerBound, subType)) { return true; } } return false; } else if (superType instanceof GenericArrayType) { return isArraySupertype(superType, subType); } else { throw new RuntimeException("not implemented: " + superType.getClass()); } } private static boolean isArraySupertype(final Type arraySuperType, final Type subType) { final Type superTypeComponent = getArrayComponentType(arraySuperType); assert superTypeComponent != null; final Type subTypeComponent = getArrayComponentType(subType); if (subTypeComponent == null) { // subType is not an array type return false; } return isSuperType(superTypeComponent, subTypeComponent); } /** * If type is an array type, returns the type of the component of the array. * Otherwise, returns null. */ public static Type getArrayComponentType(final Type type) { if (type instanceof Class) { final Class clazz = (Class) type; return clazz.getComponentType(); } else if (type instanceof GenericArrayType) { final GenericArrayType aType = (GenericArrayType) type; return aType.getGenericComponentType(); } else { return null; } } private static boolean contains(final Type containingType, final Type containedType) { if (containingType instanceof WildcardType) { final WildcardType wContainingType = (WildcardType) containingType; for (final Type upperBound : wContainingType.getUpperBounds()) { if (!isSuperType(upperBound, containedType)) { return false; } } for (final Type lowerBound : wContainingType.getLowerBounds()) { if (!isSuperType(containedType, lowerBound)) { return false; } } return true; } return containingType.equals(containedType); } /** * Returns the direct supertypes of the given type. Resolves type * parameters. */ private static Type[] getExactDirectSuperTypes(final Type type) { if (type instanceof ParameterizedType || type instanceof Class) { Class clazz; if (type instanceof ParameterizedType) { clazz = (Class) ((ParameterizedType) type).getRawType(); } else { // TODO primitive types? clazz = (Class) type; if (clazz.isArray()) return getArrayExactDirectSuperTypes(clazz); } final Type[] superInterfaces = clazz.getGenericInterfaces(); final Type superClass = clazz.getGenericSuperclass(); Type[] result; int resultIndex; if (superClass == null) { result = new Type[superInterfaces.length]; resultIndex = 0; } else { result = new Type[superInterfaces.length + 1]; resultIndex = 1; result[0] = mapTypeParameters(superClass, type); } for (final Type superInterface : superInterfaces) { result[resultIndex++] = mapTypeParameters(superInterface, type); } return result; } else if (type instanceof TypeVariable) { final TypeVariable tv = (TypeVariable) type; return tv.getBounds(); } else if (type instanceof WildcardType) { // This should be a rare case: normally this wildcard is already // captured. // But it does happen if the upper bound of a type variable contains a // wildcard // TODO shouldn't upper bound of type variable have been captured too? // (making this case impossible?) return ((WildcardType) type).getUpperBounds(); } else if (type instanceof CaptureType) { return ((CaptureType) type).getUpperBounds(); } else if (type instanceof GenericArrayType) { return getArrayExactDirectSuperTypes(type); } else { throw new RuntimeException("not implemented type: " + type); } } private static Type[] getArrayExactDirectSuperTypes(final Type arrayType) { // see // http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.3 final Type typeComponent = getArrayComponentType(arrayType); Type[] result; int resultIndex; if (typeComponent instanceof Class && ((Class) typeComponent) .isPrimitive()) { resultIndex = 0; result = new Type[3]; } else { final Type[] componentSupertypes = getExactDirectSuperTypes( typeComponent); result = new Type[componentSupertypes.length + 3]; for (resultIndex = 0; resultIndex < componentSupertypes.length; resultIndex++) { result[resultIndex] = array(componentSupertypes[resultIndex]); } } result[resultIndex++] = Object.class; result[resultIndex++] = Cloneable.class; result[resultIndex++] = Serializable.class; return result; } /** * Returns the exact return type of the given method in the given type. This * may be different from m.getGenericReturnType() when the method * was declared in a superclass, or type has a type parameter that * is used in the return type, or type is a raw type. */ public static Type getExactReturnType(final Method m, final Type type) { final Type returnType = m.getGenericReturnType(); final Type exactDeclaringType = getExactSuperType(capture(type), m .getDeclaringClass()); if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() throw new IllegalArgumentException("The method " + m + " is not a member of type " + type); } return mapTypeParameters(returnType, exactDeclaringType); } /** * Returns the exact type of the given field in the given type. This may be * different from f.getGenericType() when the field was declared in * a superclass, or type has a type parameter that is used in the * type of the field, or type is a raw type. */ public static Type getExactFieldType(final Field f, final Type type) { final Type returnType = f.getGenericType(); final Type exactDeclaringType = getExactSuperType(capture(type), f .getDeclaringClass()); if (exactDeclaringType == null) { // capture(type) is not a subtype of f.getDeclaringClass() throw new IllegalArgumentException("The field " + f + " is not a member of type " + type); } return mapTypeParameters(returnType, exactDeclaringType); } /** * Returns the exact parameter types of the given method in the given type. * This may be different from m.getGenericParameterTypes() when the * method was declared in a superclass, or type has a type * parameter that is used in one of the parameters, or type is a * raw type. */ public static Type[] getExactParameterTypes(final Method m, final Type type) { final Type[] parameterTypes = m.getGenericParameterTypes(); final Type exactDeclaringType = getExactSuperType(capture(type), m .getDeclaringClass()); if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() throw new IllegalArgumentException("The method " + m + " is not a member of type " + type); } final Type[] result = new Type[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { result[i] = mapTypeParameters(parameterTypes[i], exactDeclaringType); } return result; } /** * Applies capture conversion to the given type. */ public static Type capture(final Type type) { final VarMap varMap = new VarMap(); final List toInit = new ArrayList<>(); if (type instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) type; final Class clazz = (Class) pType.getRawType(); final Type[] arguments = pType.getActualTypeArguments(); final TypeVariable[] vars = clazz.getTypeParameters(); final Type[] capturedArguments = new Type[arguments.length]; assert arguments.length == vars.length; for (int i = 0; i < arguments.length; i++) { Type argument = arguments[i]; if (argument instanceof WildcardType) { final CaptureTypeImpl captured = new CaptureTypeImpl( (WildcardType) argument, vars[i]); argument = captured; toInit.add(captured); } capturedArguments[i] = argument; varMap.add(vars[i], argument); } for (final CaptureTypeImpl captured : toInit) { captured.init(varMap); } final Type ownerType = (pType.getOwnerType() == null) ? null : capture( pType.getOwnerType()); return parameterize(clazz, ownerType, capturedArguments); } return type; } /** * Returns list of classes and interfaces that are supertypes of the given * type. For example given this class: * class Foo<A extends Number & Iterable<A>, B extends A> *
* calling this method on type parameters B * (Foo.class.getTypeParameters()[1]) returns a list containing * Number and Iterable. *

* This is mostly useful if you get a type from one of the other methods in * GenericTypeReflector, but you don't want to deal with all the * different sorts of types, and you are only really interested in concrete * classes and interfaces. *

* * @return A List of classes, each of them a supertype of the given type. If * the given type is a class or interface itself, returns a List * with just the given type. The list contains no duplicates, and is * ordered in the order the upper bounds are defined on the type. */ public static List> getUpperBoundClassAndInterfaces( final Type type) { final LinkedHashSet> result = new LinkedHashSet<>(); buildUpperBoundClassAndInterfaces(type, result); return new ArrayList<>(result); } /** * Helper method for getUpperBoundClassAndInterfaces, adding the result to * the given set. */ private static void buildUpperBoundClassAndInterfaces(final Type type, final Set> result) { if (type instanceof ParameterizedType || type instanceof Class) { result.add(erase(type)); return; } for (final Type superType : getExactDirectSuperTypes(type)) { buildUpperBoundClassAndInterfaces(superType, result); } } } /** * CaptureType represents a wildcard that has gone through capture conversion. * It is a custom subinterface of Type, not part of the java builtin Type * hierarchy. * * @author Wouter Coekaerts */ private interface CaptureType extends Type { /** * Returns an array of Type objects representing the upper bound(s) * of this capture. This includes both the upper bound of a * ? extends wildcard, and the bounds declared with the type * variable. References to other (or the same) type variables in bounds * coming from the type variable are replaced by their matching capture. */ Type[] getUpperBounds(); /** * Returns an array of Type objects representing the lower bound(s) * of this type variable. This is the bound of a ? super wildcard. * This normally contains only one or no types; it is an array for * consistency with {@link WildcardType#getLowerBounds()}. */ Type[] getLowerBounds(); } private static class CaptureTypeImpl implements CaptureType { private final WildcardType wildcard; private final TypeVariable variable; private final Type[] lowerBounds; private Type[] upperBounds; /** * Creates an uninitialized CaptureTypeImpl. Before using this type, * {@link #init(VarMap)} must be called. * * @param wildcard The wildcard this is a capture of * @param variable The type variable where the wildcard is a parameter for. */ public CaptureTypeImpl(final WildcardType wildcard, final TypeVariable variable) { this.wildcard = wildcard; this.variable = variable; this.lowerBounds = wildcard.getLowerBounds(); } /** * Initialize this CaptureTypeImpl. This is needed for type variable bounds * referring to each other: we need the capture of the argument. */ void init(final VarMap varMap) { final ArrayList upperBoundsList = new ArrayList<>(); upperBoundsList.addAll(Arrays.asList(varMap.map(variable.getBounds()))); final List wildcardUpperBounds = Arrays.asList(wildcard .getUpperBounds()); if (wildcardUpperBounds.size() > 0 && wildcardUpperBounds.get( 0) == Object.class) { // skip the Object bound, we already have a first upper bound from // 'variable' upperBoundsList.addAll(wildcardUpperBounds.subList(1, wildcardUpperBounds.size())); } else { upperBoundsList.addAll(wildcardUpperBounds); } upperBounds = new Type[upperBoundsList.size()]; upperBoundsList.toArray(upperBounds); } @Override public Type[] getLowerBounds() { return lowerBounds.clone(); } @Override public Type[] getUpperBounds() { assert upperBounds != null; return upperBounds.clone(); } @Override public String toString() { return "capture of " + wildcard; } } /** * Mapping between type variables and actual parameters. * * @author Wouter Coekaerts */ private static class VarMap { private final Map, Type> map = new HashMap<>(); /** * Creates an empty VarMap */ VarMap() {} void add(final TypeVariable variable, final Type value) { map.put(variable, value); } void addAll(final TypeVariable[] variables, final Type[] values) { assert variables.length == values.length; for (int i = 0; i < variables.length; i++) { map.put(variables[i], values[i]); } } Type map(final Type type) { if (type instanceof Class) { return type; } else if (type instanceof TypeVariable) { assert map.containsKey(type); return map.get(type); } else if (type instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) type; return parameterize((Class) pType.getRawType(), pType .getOwnerType() == null ? pType.getOwnerType() : map(pType .getOwnerType()), map(pType.getActualTypeArguments())); } else if (type instanceof WildcardType) { final WildcardType wType = (WildcardType) type; return new TypeUtils.WildcardTypeImpl(map(wType.getUpperBounds()), map( wType.getLowerBounds())); } else if (type instanceof GenericArrayType) { return array(map(((GenericArrayType) type).getGenericComponentType())); } else { throw new RuntimeException("not implemented: mapping " + type .getClass() + " (" + type + ")"); } } Type[] map(final Type[] types) { final Type[] result = new Type[types.length]; for (int i = 0; i < types.length; i++) { result[i] = map(types[i]); } return result; } } // -- END FORK OF GENTYREF 1.1.0 CODE -- }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy