| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.lang3.reflect; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.lang.reflect.WildcardType; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.lang3.ClassUtils; |
| |
| /** |
| * <p> Utility methods focusing on type inspection, particularly with regard to |
| * generics. </p> |
| * |
| * @since 3.0 |
| * @version $Id$ |
| */ |
| public class TypeUtils { |
| |
| /** |
| * <p> TypeUtils instances should NOT be constructed in standard |
| * programming. Instead, the class should be used as |
| * <code>TypeUtils.isAssignable(cls, toClass)</code>. </p> <p> This |
| * constructor is public to permit tools that require a JavaBean instance to |
| * operate. </p> |
| */ |
| public TypeUtils() { |
| super(); |
| } |
| |
| /** |
| * <p> 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 ClassUtils#isAssignable(Class, Class)}. </p> |
| * |
| * @param type the subject type to be assigned to the target type |
| * @param toType the target type |
| * @return <code>true</code> if <code>type</code> is assignable to <code>toType</code>. |
| */ |
| public static boolean isAssignable(Type type, Type toType) { |
| return isAssignable(type, toType, null); |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target type |
| * following the Java generics rules. </p> |
| * |
| * @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</code> if <code>type</code> is assignable to <code>toType</code>. |
| */ |
| private static boolean isAssignable(Type type, Type toType, |
| Map<TypeVariable<?>, 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); |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target class |
| * following the Java generics rules. </p> |
| * |
| * @param type the subject type to be assigned to the target type |
| * @param toClass the target class |
| * @return true if <code>type</code> is assignable to <code>toClass</code>. |
| */ |
| private static boolean isAssignable(Type type, 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 ClassUtils.isAssignable((Class<?>) type, toClass); |
| } |
| |
| 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 (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); |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target |
| * parameterized type following the Java generics rules. </p> |
| * |
| * @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 true if <code>type</code> is assignable to <code>toType</code>. |
| */ |
| private static boolean isAssignable(Type type, ParameterizedType toParameterizedType, |
| Map<TypeVariable<?>, 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 |
| 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. |
| Map<TypeVariable<?>, 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 |
| Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, |
| toClass, typeVarAssigns); |
| |
| // now to check each type argument |
| for (Map.Entry<TypeVariable<?>, Type> entry : toTypeVarAssigns.entrySet()) { |
| Type toTypeArg = entry.getValue(); |
| Type fromTypeArg = fromTypeVarAssigns.get(entry.getKey()); |
| |
| // 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; |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target |
| * generic array type following the Java generics rules. </p> |
| * |
| * @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 true if <code>type</code> is assignable to |
| * <code>toGenericArrayType</code>. |
| */ |
| private static boolean isAssignable(Type type, GenericArrayType toGenericArrayType, |
| Map<TypeVariable<?>, 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; |
| } |
| |
| Type toComponentType = toGenericArrayType.getGenericComponentType(); |
| |
| if (type instanceof Class<?>) { |
| 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 (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 (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); |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target |
| * wildcard type following the Java generics rules. </p> |
| * |
| * @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 true if <code>type</code> is assignable to |
| * <code>toWildcardType</code>. |
| */ |
| private static boolean isAssignable(Type type, WildcardType toWildcardType, |
| Map<TypeVariable<?>, 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; |
| } |
| |
| Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType); |
| Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType); |
| |
| if (type instanceof WildcardType) { |
| WildcardType wildcardType = (WildcardType) type; |
| Type[] upperBounds = getImplicitUpperBounds(wildcardType); |
| 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 (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 (Type bound : lowerBounds) { |
| if (!isAssignable(toBound, bound, typeVarAssigns)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| for (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 (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; |
| } |
| |
| /** |
| * <p> Checks if the subject type may be implicitly cast to the target type |
| * variable following the Java generics rules. </p> |
| * |
| * @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 true if <code>type</code> is assignable to |
| * <code>toTypeVariable</code>. |
| */ |
| private static boolean isAssignable(Type type, TypeVariable<?> toTypeVariable, |
| Map<TypeVariable<?>, 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. |
| Type[] bounds = getImplicitBounds((TypeVariable<?>) type); |
| |
| for (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); |
| } |
| |
| /** |
| * <p> </p> |
| * |
| * @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(Type type, Map<TypeVariable<?>, Type> typeVarAssigns) { |
| if (type instanceof TypeVariable<?> && typeVarAssigns != null) { |
| Type replacementType = typeVarAssigns.get(type); |
| |
| if (replacementType == null) { |
| throw new IllegalArgumentException("missing assignment type for type variable " |
| + type); |
| } |
| |
| return replacementType; |
| } |
| |
| return type; |
| } |
| |
| /** |
| * <p> Retrieves all the type arguments for this parameterized type |
| * including owner hierarchy arguments such as <code> |
| * Outer<K,V>.Inner<T>.DeepInner<E></code> . The arguments are returned in a |
| * {@link Map} specifying the argument type for each {@link TypeVariable}. |
| * </p> |
| * |
| * @param type specifies the subject parameterized type from which to |
| * harvest the parameters. |
| * @return a map of the type arguments to their respective type variables. |
| */ |
| public static Map<TypeVariable<?>, Type> getTypeArguments(ParameterizedType type) { |
| return getTypeArguments(type, getRawType(type), null); |
| } |
| |
| /** |
| * <p> 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</code> interface. <p> </p> This method |
| * returns <code>null</code> if <code>type</code> is not assignable to |
| * <code>toClass</code>. It returns an empty map if none of the classes or |
| * interfaces in its inheritance hierarchy specify any type arguments. </p> |
| * <p> 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</code> and <code>toClass</code>. 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</code>. |
| * In cases where the interface specified by <code>toClass</code> is |
| * (indirectly) implemented more than once (e.g. where <code>toClass</code> |
| * specifies the interface {@link java.lang.Iterable Iterable} and |
| * <code>type</code> 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</code> to |
| * <code>toClass</code> hierarchy. </p> |
| * |
| * @param type the type from which to determine the type parameters of |
| * <code>toClass</code> |
| * @param toClass the class whose type parameters are to be determined based |
| * on the subtype <code>type</code> |
| * @return a map of the type assignments for the type variables in each type |
| * in the inheritance hierarchy from <code>type</code> to |
| * <code>toClass</code> inclusive. |
| */ |
| public static Map<TypeVariable<?>, Type> getTypeArguments(Type type, Class<?> toClass) { |
| return getTypeArguments(type, toClass, null); |
| } |
| |
| /** |
| * <p> Return a map of the type arguments of <code>type</code> in the context of <code>toClass</code>. </p> |
| * |
| * @param type the type in question |
| * @param toClass the class |
| * @param subtypeVarAssigns a map with type variables |
| * @return the map with type arguments |
| */ |
| private static Map<TypeVariable<?>, Type> getTypeArguments(Type type, Class<?> toClass, |
| Map<TypeVariable<?>, 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 (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 (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); |
| } |
| |
| /** |
| * <p> Return a map of the type arguments of a parameterized type in the context of <code>toClass</code>. </p> |
| * |
| * @param parameterizedType the parameterized type |
| * @param toClass the class |
| * @param subtypeVarAssigns a map with type variables |
| * @return the map with type arguments |
| */ |
| private static Map<TypeVariable<?>, Type> getTypeArguments( |
| ParameterizedType parameterizedType, Class<?> toClass, |
| Map<TypeVariable<?>, Type> subtypeVarAssigns) { |
| Class<?> cls = getRawType(parameterizedType); |
| |
| // make sure they're assignable |
| if (!isAssignable(cls, toClass)) { |
| return null; |
| } |
| |
| Type ownerType = parameterizedType.getOwnerType(); |
| Map<TypeVariable<?>, Type> typeVarAssigns; |
| |
| if (ownerType instanceof ParameterizedType) { |
| // get the owner type arguments first |
| ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType; |
| typeVarAssigns = getTypeArguments(parameterizedOwnerType, |
| getRawType(parameterizedOwnerType), subtypeVarAssigns); |
| } else { |
| // no owner, prep the type variable assignments map |
| typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>() |
| : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns); |
| } |
| |
| // get the subject parameterized type's arguments |
| Type[] typeArgs = parameterizedType.getActualTypeArguments(); |
| // and get the corresponding type variables from the raw class |
| TypeVariable<?>[] typeParams = cls.getTypeParameters(); |
| |
| // map the arguments to their respective type variables |
| for (int i = 0; i < typeParams.length; i++) { |
| 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); |
| } |
| |
| /** |
| * <p> Return a map of the type arguments of a class in the context of <code>toClass</code>. </p> |
| * |
| * @param cls the class in question |
| * @param toClass the context class |
| * @param subtypeVarAssigns a map with type variables |
| * @return the map with type arguments |
| */ |
| private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, Class<?> toClass, |
| Map<TypeVariable<?>, 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<TypeVariable<?>, Type>(); |
| } |
| |
| // work with wrapper the wrapper class instead of the primitive |
| cls = ClassUtils.primitiveToWrapper(cls); |
| } |
| |
| // create a copy of the incoming map, or an empty one if it's null |
| HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>() |
| : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns); |
| |
| // no arguments for the parameters, or target class has been reached |
| if (cls.getTypeParameters().length > 0 || toClass.equals(cls)) { |
| return typeVarAssigns; |
| } |
| |
| // walk the inheritance hierarchy until the target class is reached |
| return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); |
| } |
| |
| /** |
| * <p> 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. </p> <p> 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</code>'s parameter maps |
| * (indirectly) to <code>Iterable</code>'s parameter, it will be able to |
| * determine that based on the super type <code>Iterable<? extends |
| * Map<Integer,? extends Collection<?>>></code>, the parameter of |
| * <code>TreeSet</code> is <code>? extends Map<Integer,? extends |
| * Collection<?>></code>. </p> |
| * |
| * @param cls the class whose type parameters are to be determined |
| * @param superType the super type from which <code>cls</code>'s type |
| * arguments are to be determined |
| * @return a map of the type assignments that could be determined for the |
| * type variables in each type in the inheritance hierarchy from |
| * <code>type</code> to <code>toClass</code> inclusive. |
| */ |
| public static Map<TypeVariable<?>, Type> determineTypeArguments(Class<?> cls, |
| ParameterizedType superType) { |
| 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 |
| Type midType = getClosestParentType(cls, superClass); |
| |
| // can only be a class or a parameterized type |
| if (midType instanceof Class<?>) { |
| return determineTypeArguments((Class<?>) midType, superType); |
| } |
| |
| ParameterizedType midParameterizedType = (ParameterizedType) midType; |
| Class<?> midClass = getRawType(midParameterizedType); |
| // get the type variables of the mid class that map to the type |
| // arguments of the super class |
| Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superType); |
| // map the arguments of the mid type to the class type variables |
| mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns); |
| |
| return typeVarAssigns; |
| } |
| |
| /** |
| * <p>Performs a mapping of type variables.</p> |
| * |
| * @param <T> 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 <T> void mapTypeVariablesToArguments(Class<T> cls, |
| ParameterizedType parameterizedType, Map<TypeVariable<?>, Type> typeVarAssigns) { |
| // capture the type variables from the owner type that have assignments |
| 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: |
| 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 |
| TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters(); |
| |
| // use List view of type parameters of cls so the contains() method can be used: |
| List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls |
| .getTypeParameters()); |
| |
| for (int i = 0; i < typeArgs.length; i++) { |
| TypeVariable<?> typeVar = typeVars[i]; |
| 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)); |
| } |
| } |
| } |
| |
| /** |
| * <p> Closest parent type? Closest to what? The closest parent type to the |
| * super class specified by <code>superClass</code>. </p> |
| * |
| * @param cls the class in question |
| * @param superClass the super class |
| * @return the closes parent type |
| */ |
| private static Type getClosestParentType(Class<?> cls, 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 |
| Type[] interfaceTypes = cls.getGenericInterfaces(); |
| // will hold the best generic interface match found |
| Type genericInterface = null; |
| |
| // find the interface closest to the super class |
| for (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(); |
| } |
| |
| /** |
| * <p> Checks if the given value can be assigned to the target type |
| * following the Java generics rules. </p> |
| * |
| * @param value the value to be checked |
| * @param type the target type |
| * @return true of <code>value</code> is an instance of <code>type</code>. |
| */ |
| public static boolean isInstance(Object value, Type type) { |
| if (type == null) { |
| return false; |
| } |
| |
| return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive() |
| : isAssignable(value.getClass(), type, null); |
| } |
| |
| /** |
| * <p> 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). </p> <p> Example: with the variable |
| * type declaration: |
| * |
| * <pre> <K extends java.util.Collection<String> & |
| * java.util.List<String>> </pre> |
| * |
| * since <code>List</code> is a subinterface of <code>Collection</code>, |
| * this method will return the bounds as if the declaration had been: |
| * |
| * <pre> <K extends java.util.List<String>> </pre> |
| * |
| * </p> |
| * |
| * @param bounds an array of types representing the upper bounds of either |
| * <code>WildcardType</code> or <code>TypeVariable</code>. |
| * @return an array containing the values from <code>bounds</code> minus the |
| * redundant types. |
| */ |
| public static Type[] normalizeUpperBounds(Type[] bounds) { |
| // don't bother if there's only one (or none) type |
| if (bounds.length < 2) { |
| return bounds; |
| } |
| |
| Set<Type> types = new HashSet<Type>(bounds.length); |
| |
| for (Type type1 : bounds) { |
| boolean subtypeFound = false; |
| |
| for (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()]); |
| } |
| |
| /** |
| * <p> Returns an array containing the sole type of {@link Object} if |
| * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it |
| * returns the result of <code>TypeVariable.getBounds()</code> passed into |
| * {@link #normalizeUpperBounds}. </p> |
| * |
| * @param typeVariable the subject type variable |
| * @return a non-empty array containing the bounds of the type variable. |
| */ |
| public static Type[] getImplicitBounds(TypeVariable<?> typeVariable) { |
| Type[] bounds = typeVariable.getBounds(); |
| |
| return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); |
| } |
| |
| /** |
| * <p> Returns an array containing the sole value of {@link Object} if |
| * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, |
| * it returns the result of <code>WildcardType.getUpperBounds()</code> |
| * passed into {@link #normalizeUpperBounds}. </p> |
| * |
| * @param wildcardType the subject wildcard type |
| * @return a non-empty array containing the upper bounds of the wildcard |
| * type. |
| */ |
| public static Type[] getImplicitUpperBounds(WildcardType wildcardType) { |
| Type[] bounds = wildcardType.getUpperBounds(); |
| |
| return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); |
| } |
| |
| /** |
| * <p> Returns an array containing a single value of <code>null</code> if |
| * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, |
| * it returns the result of <code>WildcardType.getLowerBounds()</code>. </p> |
| * |
| * @param wildcardType the subject wildcard type |
| * @return a non-empty array containing the lower bounds of the wildcard |
| * type. |
| */ |
| public static Type[] getImplicitLowerBounds(WildcardType wildcardType) { |
| Type[] bounds = wildcardType.getLowerBounds(); |
| |
| return bounds.length == 0 ? new Type[] { null } : bounds; |
| } |
| |
| /** |
| * <p> Determines whether or not specified types satisfy the bounds of their |
| * mapped type variables. When a type parameter extends another (such as |
| * <code><T, S extends T></code>), uses another as a type parameter (such as |
| * <code><T, S extends Comparable<T></code>), or otherwise depends on |
| * another type variable to be specified, the dependencies must be included |
| * in <code>typeVarAssigns</code>. </p> |
| * |
| * @param typeVarAssigns specifies the potential types to be assigned to the |
| * type variables. |
| * @return whether or not the types can be assigned to their respective type |
| * variables. |
| */ |
| public static boolean typesSatisfyVariables(Map<TypeVariable<?>, Type> typeVarAssigns) { |
| // all types must be assignable to all the bounds of the their mapped |
| // type variable. |
| for (Map.Entry<TypeVariable<?>, Type> entry : typeVarAssigns.entrySet()) { |
| TypeVariable<?> typeVar = entry.getKey(); |
| Type type = entry.getValue(); |
| |
| for (Type bound : getImplicitBounds(typeVar)) { |
| if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns), |
| typeVarAssigns)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * <p> Transforms the passed in type to a {@code Class} object. Type-checking method of convenience. </p> |
| * |
| * @param parameterizedType the type to be converted |
| * @return the corresponding {@code Class} object |
| * @throws IllegalStateException if the conversion fails |
| */ |
| private static Class<?> getRawType(ParameterizedType parameterizedType) { |
| 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; |
| } |
| |
| /** |
| * <p> 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</code>: 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. </p> |
| * |
| * @param type to resolve |
| * @param assigningType type to be resolved against |
| * @return the resolved <code>Class</code> object or <code>null</code> if |
| * the type could not be resolved |
| */ |
| public static Class<?> getRawType(Type type, 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 |
| 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 |
| Map<TypeVariable<?>, 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 |
| 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 |
| Class<?> rawComponentType = getRawType(((GenericArrayType) type) |
| .getGenericComponentType(), assigningType); |
| |
| // create array type from raw component type and return its class |
| return Array.newInstance(rawComponentType, 0).getClass(); |
| } |
| |
| // (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</code> if <code>type</code> is an array class or a {@link GenericArrayType}. |
| */ |
| public static boolean isArrayType(Type type) { |
| return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray(); |
| } |
| |
| /** |
| * Get the array component type of <code>type</code>. |
| * @param type the type to be checked |
| * @return component type or null if type is not an array type |
| */ |
| public static Type getArrayComponentType(Type type) { |
| if (type instanceof Class<?>) { |
| Class<?> clazz = (Class<?>) type; |
| return clazz.isArray() ? clazz.getComponentType() : null; |
| } |
| if (type instanceof GenericArrayType) { |
| return ((GenericArrayType) type).getGenericComponentType(); |
| } |
| return null; |
| } |
| |
| } |