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}