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}