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