    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.ioc.internal.util;
    014
    015import java.lang.reflect.*;
    016import java.util.LinkedList;
    017
    018/**
    019 * Static methods related to the use of JDK 1.5 generics.
    020 */
    021@SuppressWarnings("unchecked")
    022public class GenericsUtils
    023{
    024    /**
    025     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    026     * the method's generic return type. Any parameter information in the generic return type is lost. If you want
    027     * to preserve the type parameters of the return type consider using
    028     * {@link #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)}.
    029     *
    030     * @param containingClass class which either contains or inherited the method
    031     * @param method          method from which to extract the return type
    032     * @return the class represented by the methods generic return type, resolved based on the context .
    033     * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)
    034     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    035     * @see #asClass(java.lang.reflect.Type)
    036     */
    037    public static Class<?> extractGenericReturnType(Class<?> containingClass, Method method)
    038    {
    039        return asClass(resolve(method.getGenericReturnType(), containingClass));
    040    }
    041
    042
    043    /**
    044     * Analyzes the field in the context of containingClass and returns the Class that is represented by
    045     * the field's generic type. Any parameter information in the generic type is lost, if you want
    046     * to preserve the type parameters of the return type consider using
    047     * {@link #getTypeVariableIndex(java.lang.reflect.TypeVariable)}.
    048     *
    049     * @param containingClass class which either contains or inherited the field
    050     * @param field           field from which to extract the type
    051     * @return the class represented by the field's generic type, resolved based on the containingClass.
    052     * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Field)
    053     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    054     * @see #asClass(java.lang.reflect.Type)
    055     */
    056    public static Class extractGenericFieldType(Class containingClass, Field field)
    057    {
    058        return asClass(resolve(field.getGenericType(), containingClass));
    059    }
    060
    061    /**
    062     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    063     * the method's generic return type. Any parameter information in the generic return type is lost.
    064     *
    065     * @param containingType Type which is/represents the class that either contains or inherited the method
    066     * @param method         method from which to extract the generic return type
    067     * @return the generic type represented by the methods generic return type, resolved based on the containingType.
    068     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    069     */
    070    public static Type extractActualType(Type containingType, Method method)
    071    {
    072        return resolve(method.getGenericReturnType(), containingType);
    073    }
    074
    075    /**
    076     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    077     * the method's generic return type. Any parameter information in the generic return type is lost.
    078     *
    079     * @param containingType Type which is/represents the class that either contains or inherited the field
    080     * @param field          field from which to extract the generic return type
    081     * @return the generic type represented by the methods generic return type, resolved based on the containingType.
    082     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    083     */
    084    public static Type extractActualType(Type containingType, Field field)
    085    {
    086        return resolve(field.getGenericType(), containingType);
    087    }
    088
    089    /**
    090     * Resolves the type parameter based on the context of the containingType.
    091     *
    092     * {@link java.lang.reflect.TypeVariable} will be unwrapped to the type argument resolved form the class
    093     * hierarchy. This may be something other than a simple Class if the type argument is a ParameterizedType for
    094     * instance (e.g. {@code List<E>; List<Map<Long, String>>}, E would be returned as a ParameterizedType with the raw
    095     * type Map and type arguments Long and String.
    096     *
    097     *
    098     * @param type
    099     *          the generic type (ParameterizedType, GenericArrayType, WildcardType, TypeVariable) to be resolved
    100     * @param containingType
    101     *          the type which his
    102     * @return
    103     *          the type resolved to the best of our ability.
    104     * @since 5.2.?
    105     */
    106    public static Type resolve(final Type type, final Type containingType)
    107    {
    108        // The type isn't generic. (String, Long, etc)
    109        if (type instanceof Class)
    110            return type;
    111
    112        // List<T>, List<String>, List<T extends Number>
    113        if (type instanceof ParameterizedType)
    114            return resolve((ParameterizedType) type, containingType);
    115
    116        // T[], List<String>[], List<T>[]
    117        if (type instanceof GenericArrayType)
    118            return resolve((GenericArrayType) type, containingType);
    119
    120        // List<? extends T>, List<? extends Object & Comparable & Serializable>
    121        if (type instanceof WildcardType)
    122            return resolve((WildcardType) type, containingType);
    123
    124        // T
    125        if (type instanceof TypeVariable)
    126            return resolve((TypeVariable) type, containingType);
    127
    128        // I'm leaning towards an exception here.
    129        return type;
    130    }
    131
    132
    133    /**
    134     * Determines if the suspected super type is assignable from the suspected sub type.
    135     *
    136     * @param suspectedSuperType
    137     *          e.g. {@code GenericDAO<Pet, String>}
    138     * @param suspectedSubType
    139     *          e.g. {@code PetDAO extends GenericDAO<Pet,String>}
    140     * @return
    141     *          true if (sourceType)targetClass is a valid cast
    142     */
    143    public static boolean isAssignableFrom(Type suspectedSuperType, Type suspectedSubType)
    144    {
    145        final Class suspectedSuperClass = asClass(suspectedSuperType);
    146        final Class suspectedSubClass = asClass(suspectedSubType);
    147
    148        // The raw types need to be compatible.
    149        if (!suspectedSuperClass.isAssignableFrom(suspectedSubClass))
    150        {
    151            return false;
    152        }
    153
    154        // From this point we know that the raw types are assignable.
    155        // We need to figure out what the generic parameters in the targetClass are
    156        // as they pertain to the sourceType.
    157
    158        if (suspectedSuperType instanceof WildcardType)
    159        {
    160            // ? extends Number
    161            // needs to match all the bounds (there will only be upper bounds or lower bounds
    162            for (Type t : ((WildcardType) suspectedSuperType).getUpperBounds())
    163            {
    164                if (!isAssignableFrom(t, suspectedSubType)) return false;
    165            }
    166            for (Type t : ((WildcardType) suspectedSuperType).getLowerBounds())
    167            {
    168                if (!isAssignableFrom(suspectedSubType, t)) return false;
    169            }
    170            return true;
    171        }
    172
    173        Type curType = suspectedSubType;
    174        Class curClass;
    175
    176        while (curType != null && !curType.equals(Object.class))
    177        {
    178            curClass = asClass(curType);
    179
    180            if (curClass.equals(suspectedSuperClass))
    181            {
    182                final Type resolved = resolve(curType, suspectedSubType);
    183
    184                if (suspectedSuperType instanceof Class)
    185                {
    186                    if ( resolved instanceof Class )
    187                        return suspectedSuperType.equals(resolved);
    188
    189                    // They may represent the same class, but the suspectedSuperType is not parameterized. The parameter
    190                    // types default to Object so they must be a match.
    191                    // e.g. Pair p = new StringLongPair();
    192                    //      Pair p = new Pair<? extends Number, String>
    193
    194                    return true;
    195                }
    196
    197                if (suspectedSuperType instanceof ParameterizedType)
    198                {
    199                    if (resolved instanceof ParameterizedType)
    200                    {
    201                        final Type[] type1Arguments = ((ParameterizedType) suspectedSuperType).getActualTypeArguments();
    202                        final Type[] type2Arguments = ((ParameterizedType) resolved).getActualTypeArguments();
    203                        if (type1Arguments.length != type2Arguments.length) return false;
    204
    205                        for (int i = 0; i < type1Arguments.length; ++i)
    206                        {
    207                            if (!isAssignableFrom(type1Arguments[i], type2Arguments[i])) return false;
    208                        }
    209                        return true;
    210                    }
    211                }
    212                else if (suspectedSuperType instanceof GenericArrayType)
    213                {
    214                    if (resolved instanceof GenericArrayType)
    215                    {
    216                        return isAssignableFrom(
    217                                ((GenericArrayType) suspectedSuperType).getGenericComponentType(),
    218                                ((GenericArrayType) resolved).getGenericComponentType()
    219                        );
    220                    }
    221                }
    222
    223                return false;
    224            }
    225
    226            final Type[] types = curClass.getGenericInterfaces();
    227            for (Type t : types)
    228            {
    229                final Type resolved = resolve(t, suspectedSubType);
    230                if (isAssignableFrom(suspectedSuperType, resolved))
    231                    return true;
    232            }
    233
    234            curType = curClass.getGenericSuperclass();
    235        }
    236        return false;
    237    }
    238
    239    /**
    240     * Get the class represented by the reflected type.
    241     * This method is lossy; You cannot recover the type information from the class that is returned.
    242     *
    243     * {@code TypeVariable} the first bound is returned. If your type variable extends multiple interfaces that information
    244     * is lost.
    245     *
    246     * {@code WildcardType} the first lower bound is returned. If the wildcard is defined with upper bounds
    247     * then {@code Object} is returned.
    248     *
    249     * @param actualType
    250     *           a Class, ParameterizedType, GenericArrayType
    251     * @return the un-parameterized class associated with the type.
    252     */
    253    public static Class asClass(Type actualType)
    254    {
    255        if (actualType instanceof Class) return (Class) actualType;
    256
    257        if (actualType instanceof ParameterizedType)
    258        {
    259            final Type rawType = ((ParameterizedType) actualType).getRawType();
    260            // The sun implementation returns getRawType as Class<?>, but there is room in the interface for it to be
    261            // some other Type. We'll assume it's a Class.
    262            // TODO: consider logging or throwing our own exception for that day when "something else" causes some confusion
    263            return (Class) rawType;
    264        }
    265
    266        if (actualType instanceof GenericArrayType)
    267        {
    268            final Type type = ((GenericArrayType) actualType).getGenericComponentType();
    269            return Array.newInstance(asClass(type), 0).getClass();
    270        }
    271
    272        if (actualType instanceof TypeVariable)
    273        {
    274            // Support for List<T extends Number>
    275            // There is always at least one bound. If no bound is specified in the source then it will be Object.class
    276            return asClass(((TypeVariable) actualType).getBounds()[0]);
    277        }
    278
    279        if (actualType instanceof WildcardType)
    280        {
    281            final WildcardType wildcardType = (WildcardType) actualType;
    282            final Type[] bounds = wildcardType.getLowerBounds();
    283            if (bounds != null && bounds.length > 0)
    284            {
    285                return asClass(bounds[0]);
    286            }
    287            // If there is no lower bounds then the only thing that makes sense is Object.
    288            return Object.class;
    289        }
    290
    291        throw new RuntimeException(String.format("Unable to convert %s to Class.", actualType));
    292    }
    293
    294    /**
    295     * Convert the type into a string. The string representation approximates the code that would be used to define the
    296     * type.
    297     *
    298     * @param type - the type.
    299     * @return a string representation of the type, similar to how it was declared.
    300     */
    301    public static String toString(Type type)
    302    {
    303        if ( type instanceof ParameterizedType ) return toString((ParameterizedType)type);
    304        if ( type instanceof WildcardType ) return toString((WildcardType)type);
    305        if ( type instanceof GenericArrayType) return toString((GenericArrayType)type);
    306        if ( type instanceof Class )
    307        {
    308            final Class theClass = (Class) type;
    309            return (theClass.isArray() ? theClass.getName() + "[]" : theClass.getName());
    310        }
    311        return type.toString();
    312    }
    313
    314    /**
    315     * Method to resolve a TypeVariable to its most
    316     * <a href="http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#112582">reifiable</a> form.
    317     *
    318     *
    319     * How to resolve a TypeVariable:<br/>
    320     * All of the TypeVariables defined by a generic class will be given a Type by any class that extends it. The Type
    321     * given may or may not be reifiable; it may be another TypeVariable for instance.
    322     *
    323     * Consider <br/>
    324     * <i>class Pair&gt;A,B> { A getA(){...}; ...}</i><br/>
    325     * <i>class StringLongPair extends Pair&gt;String, Long> { }</i><br/>
    326     *
    327     * To resolve the actual return type of Pair.getA() you must first resolve the TypeVariable "A".
    328     * We can do that by first finding the index of "A" in the Pair.class.getTypeParameters() array of TypeVariables.
    329     *
    330     * To get to the Type provided by StringLongPair you access the generics information by calling
    331     * StringLongPair.class.getGenericSuperclass; this will be a ParameterizedType. ParameterizedType gives you access
    332     * to the actual type arguments provided to Pair by StringLongPair. The array is in the same order as the array in
    333     * Pair.class.getTypeParameters so you can use the index we discovered earlier to extract the Type; String.class.
    334     *
    335     * When extracting Types we only have to consider the superclass hierarchy and not the interfaces implemented by
    336     * the class. When a class implements a generic interface it must provide types for the interface and any generic
    337     * methods implemented from the interface will be re-defined by the class with its generic type variables.
    338     *
    339     * @param typeVariable   - the type variable to resolve.
    340     * @param containingType - the shallowest class in the class hierarchy (furthest from Object) where typeVariable is defined.
    341     * @return a Type that has had all possible TypeVariables resolved that have been defined between the type variable
    342     *         declaration and the containingType.
    343     */
    344    private static Type resolve(TypeVariable typeVariable, Type containingType)
    345    {
    346        // The generic declaration is either a Class, Method or Constructor
    347        final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
    348
    349        if (!(genericDeclaration instanceof Class))
    350        {
    351            // It's a method or constructor. The best we can do here is try to resolve the bounds
    352            // e.g. <T extends E> T getT(T param){} where E is defined by the class.
    353            final Type bounds0 = typeVariable.getBounds()[0];
    354            return resolve(bounds0, containingType);
    355        }
    356
    357        final Class typeVariableOwner = (Class) genericDeclaration;
    358
    359        // find the typeOwner in the containingType's hierarchy
    360        final LinkedList<Type> stack = new LinkedList<Type>();
    361
    362        // If you pass a List<Long> as the containingType then the TypeVariable is going to be resolved by the
    363        // containingType and not the super class.
    364        if (containingType instanceof ParameterizedType)
    365        {
    366            stack.add(containingType);
    367        }
    368
    369        Class theClass = asClass(containingType);
    370        Type genericSuperclass = theClass.getGenericSuperclass();
    371        while (genericSuperclass != null && // true for interfaces with no superclass
    372                !theClass.equals(Object.class) &&
    373                !theClass.equals(typeVariableOwner))
    374        {
    375            stack.addFirst(genericSuperclass);
    376            theClass = asClass(genericSuperclass);
    377            genericSuperclass = theClass.getGenericSuperclass();
    378        }
    379
    380        int i = getTypeVariableIndex(typeVariable);
    381        Type resolved = typeVariable;
    382        for (Type t : stack)
    383        {
    384            if (t instanceof ParameterizedType)
    385            {
    386                resolved = ((ParameterizedType) t).getActualTypeArguments()[i];
    387                if (resolved instanceof Class) return resolved;
    388                if (resolved instanceof TypeVariable)
    389                {
    390                    // Need to look at the next class in the hierarchy
    391                    i = getTypeVariableIndex((TypeVariable) resolved);
    392                    continue;
    393                }
    394                return resolve(resolved, containingType);
    395            }
    396        }
    397
    398        // the only way we get here is if resolved is still a TypeVariable, otherwise an
    399        // exception is thrown or a value is returned.
    400        return ((TypeVariable) resolved).getBounds()[0];
    401    }
    402
    403    /**
    404     * @param type           - something like List&lt;T>[] or List&lt;? extends T>[] or T[]
    405     * @param containingType - the shallowest type in the hierarchy where type is defined.
    406     * @return either the passed type if no changes required or a copy with a best effort resolve of the component type.
    407     */
    408    private static GenericArrayType resolve(GenericArrayType type, Type containingType)
    409    {
    410        final Type componentType = type.getGenericComponentType();
    411
    412        if (!(componentType instanceof Class))
    413        {
    414            final Type resolved = resolve(componentType, containingType);
    415            return create(resolved);
    416        }
    417
    418        return type;
    419    }
    420
    421    /**
    422     * @param type           - something like List&lt;T>, List&lt;T extends Number>
    423     * @param containingType - the shallowest type in the hierarchy where type is defined.
    424     * @return the passed type if nothing to resolve or a copy of the type with the type arguments resolved.
    425     */
    426    private static ParameterizedType resolve(ParameterizedType type, Type containingType)
    427    {
    428        // Use a copy because we're going to modify it.
    429        final Type[] types = type.getActualTypeArguments().clone();
    430
    431        boolean modified = resolve(types, containingType);
    432        return modified ? create(type.getRawType(), type.getOwnerType(), types) : type;
    433    }
    434
    435    /**
    436     * @param type           - something like List&lt;? super T>, List<&lt;? extends T>, List&lt;? extends T & Comparable&lt? super T>>
    437     * @param containingType - the shallowest type in the hierarchy where type is defined.
    438     * @return the passed type if nothing to resolve or a copy of the type with the upper and lower bounds resolved.
    439     */
    440    private static WildcardType resolve(WildcardType type, Type containingType)
    441    {
    442        // Use a copy because we're going to modify them.
    443        final Type[] upper = type.getUpperBounds().clone();
    444        final Type[] lower = type.getLowerBounds().clone();
    445
    446        boolean modified = resolve(upper, containingType);
    447        modified = modified || resolve(lower, containingType);
    448
    449        return modified ? create(upper, lower) : type;
    450    }
    451
    452    /**
    453     * @param types          - Array of types to resolve. The unresolved type is replaced in the array with the resolved type.
    454     * @param containingType - the shallowest type in the hierarchy where type is defined.
    455     * @return true if any of the types were resolved.
    456     */
    457    private static boolean resolve(Type[] types, Type containingType)
    458    {
    459        boolean modified = false;
    460        for (int i = 0; i < types.length; ++i)
    461        {
    462            Type t = types[i];
    463            if (!(t instanceof Class))
    464            {
    465                modified = true;
    466                final Type resolved = resolve(t, containingType);
    467                if (!resolved.equals(t))
    468                {
    469                    types[i] = resolved;
    470                    modified = true;
    471                }
    472            }
    473        }
    474        return modified;
    475    }
    476
    477    /**
    478     * @param rawType       - the un-parameterized type.
    479     * @param ownerType     - the outer class or null if the class is not defined within another class.
    480     * @param typeArguments - type arguments.
    481     * @return a copy of the type with the typeArguments replaced.
    482     */
    483    static ParameterizedType create(final Type rawType, final Type ownerType, final Type[] typeArguments)
    484    {
    485        return new ParameterizedType()
    486        {
    487            @Override
    488            public Type[] getActualTypeArguments()
    489            {
    490                return typeArguments;
    491            }
    492
    493            @Override
    494            public Type getRawType()
    495            {
    496                return rawType;
    497            }
    498
    499            @Override
    500            public Type getOwnerType()
    501            {
    502                return ownerType;
    503            }
    504
    505            @Override
    506            public String toString()
    507            {
    508                return GenericsUtils.toString(this);
    509            }
    510        };
    511    }
    512
    513    static GenericArrayType create(final Type componentType)
    514    {
    515        return new GenericArrayType()
    516        {
    517            @Override
    518            public Type getGenericComponentType()
    519            {
    520                return componentType;
    521            }
    522
    523            @Override
    524            public String toString()
    525            {
    526                return GenericsUtils.toString(this);
    527            }
    528        };
    529    }
    530
    531    /**
    532     * @param upperBounds - e.g. ? extends Number
    533     * @param lowerBounds - e.g. ? super Long
    534     * @return An new copy of the type with the upper and lower bounds replaced.
    535     */
    536    static WildcardType create(final Type[] upperBounds, final Type[] lowerBounds)
    537    {
    538
    539        return new WildcardType()
    540        {
    541            @Override
    542            public Type[] getUpperBounds()
    543            {
    544                return upperBounds;
    545            }
    546
    547            @Override
    548            public Type[] getLowerBounds()
    549            {
    550                return lowerBounds;
    551            }
    552
    553            @Override
    554            public String toString()
    555            {
    556                return GenericsUtils.toString(this);
    557            }
    558        };
    559    }
    560
    561    static String toString(ParameterizedType pt)
    562    {
    563        String s = toString(pt.getActualTypeArguments());
    564        return String.format("%s<%s>", toString(pt.getRawType()), s);
    565    }
    566
    567    static String toString(GenericArrayType gat)
    568    {
    569        return String.format("%s[]", toString(gat.getGenericComponentType()));
    570    }
    571
    572    static String toString(WildcardType wt)
    573    {
    574        final boolean isSuper = wt.getLowerBounds().length > 0;
    575        return String.format("? %s %s",
    576                isSuper ? "super" : "extends",
    577                isSuper ? toString(wt.getLowerBounds()) : toString(wt.getLowerBounds()));
    578    }
    579
    580    static String toString(Type[] types)
    581    {
    582        StringBuilder sb = new StringBuilder();
    583        for ( Type t : types )
    584        {
    585            sb.append(toString(t)).append(", ");
    586        }
    587        return sb.substring(0, sb.length() - 2);// drop last ,
    588    }
    589
    590    /**
    591     * Find the index of the TypeVariable in the classes parameters. The offset can be used on a subclass to find
    592     * the actual type.
    593     *
    594     * @param typeVariable - the type variable in question.
    595     * @return the index of the type variable in its declaring class/method/constructor's type parameters.
    596     */
    597    private static int getTypeVariableIndex(final TypeVariable typeVariable)
    598    {
    599        // the label from the class (the T in List<T>, the K or V in Map<K,V>, etc)
    600        final String typeVarName = typeVariable.getName();
    601        final TypeVariable[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters();
    602        for (int typeArgumentIndex = 0; typeArgumentIndex < typeParameters.length; typeArgumentIndex++)
    603        {
    604            // The .equals for TypeVariable may not be compatible, a name check should be sufficient.
    605            if (typeParameters[typeArgumentIndex].getName().equals(typeVarName))
    606                return typeArgumentIndex;
    607        }
    608
    609        // The only way this could happen is if the TypeVariable is hand built incorrectly, or it's corrupted.
    610        throw new RuntimeException(
    611                String.format("%s does not have a TypeVariable matching %s", typeVariable.getGenericDeclaration(), typeVariable));
    612    }
    613}