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}