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