1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5package org.mockito.internal.util.reflection;
6
7
8import org.mockito.Incubating;
9import org.mockito.exceptions.base.MockitoException;
10import org.mockito.internal.util.Checks;
11
12import java.lang.reflect.*;
13import java.util.*;
14
15
16/**
17 * This class can retrieve generic meta-data that the compiler stores on classes
18 * and accessible members.
19 *
20 * <p>
21 *     The main idea of this code is to create a Map that will help to resolve return types.
22 *     In order to actually work with nested generics, this map will have to be passed along new instances
23 *     as a type context.
24 * </p>
25 *
26 * <p>
27 *     Hence :
28 *     <ul>
29 *         <li>A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real
30 *         <code>Class</code> or from a <code>ParameterizedType</code>, other types are not yet supported.</li>
31 *
32 *         <li>Then from this metadata, we can extract meta-data for a generic return type of a method, using
33 *         {@link #resolveGenericReturnType(Method)}.</li>
34 *     </ul>
35 * </p>
36 *
37 * <p>
38 * For now this code support the following kind of generic declarations :
39 * <pre class="code"><code class="java">
40 * interface GenericsNest&lt;K extends Comparable&lt;K&gt; & Cloneable&gt; extends Map&lt;K, Set&lt;Number&gt;&gt; {
41 *     Set&lt;Number&gt; remove(Object key); // override with fixed ParameterizedType
42 *     List&lt;? super Integer&gt; returning_wildcard_with_class_lower_bound();
43 *     List&lt;? super K&gt; returning_wildcard_with_typeVar_lower_bound();
44 *     List&lt;? extends K&gt; returning_wildcard_with_typeVar_upper_bound();
45 *     K returningK();
46 *     &lt;O extends K&gt; List&lt;O&gt; paramType_with_type_params();
47 *     &lt;S, T extends S&gt; T two_type_params();
48 *     &lt;O extends K&gt; O typeVar_with_type_params();
49 *     Number returningNonGeneric();
50 * }
51 * </code></pre>
52 *
53 * @see #inferFrom(Type)
54 * @see #resolveGenericReturnType(Method)
55 * @see org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs
56 */
57@Incubating
58public abstract class GenericMetadataSupport {
59
60    // public static MockitoLogger logger = new ConsoleMockitoLogger();
61
62    /**
63     * Represents actual type variables resolved for current class.
64     */
65    protected Map<TypeVariable, Type> contextualActualTypeParameters = new HashMap<TypeVariable, Type>();
66
67
68    protected void registerTypeVariablesOn(Type classType) {
69        if (!(classType instanceof ParameterizedType)) {
70            return;
71        }
72        ParameterizedType parameterizedType = (ParameterizedType) classType;
73        TypeVariable[] typeParameters = ((Class<?>) parameterizedType.getRawType()).getTypeParameters();
74        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
75        for (int i = 0; i < actualTypeArguments.length; i++) {
76            TypeVariable typeParameter = typeParameters[i];
77            Type actualTypeArgument = actualTypeArguments[i];
78
79            if (actualTypeArgument instanceof WildcardType) {
80                contextualActualTypeParameters.put(typeParameter, boundsOf((WildcardType) actualTypeArgument));
81            } else {
82                contextualActualTypeParameters.put(typeParameter, actualTypeArgument);
83            }
84            // logger.log("For '" + parameterizedType + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualTypeArgument + "(" + System.identityHashCode(typeParameter) + ")" + "' }");
85        }
86    }
87
88    protected void registerTypeParametersOn(TypeVariable[] typeParameters) {
89        for (TypeVariable typeParameter : typeParameters) {
90            contextualActualTypeParameters.put(typeParameter, boundsOf(typeParameter));
91            // logger.log("For '" + typeParameter.getGenericDeclaration() + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + boundsOf(typeParameter) + "' }");
92        }
93    }
94
95    /**
96     * @param typeParameter The TypeVariable parameter
97     * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
98     *         then retrieve BoundedType of this TypeVariable
99     */
100    private BoundedType boundsOf(TypeVariable typeParameter) {
101        if (typeParameter.getBounds()[0] instanceof TypeVariable) {
102            return boundsOf((TypeVariable) typeParameter.getBounds()[0]);
103        }
104        return new TypeVarBoundedType(typeParameter);
105    }
106
107    /**
108     * @param wildCard The WildCard type
109     * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
110     *         then retrieve BoundedType of this TypeVariable
111     */
112    private BoundedType boundsOf(WildcardType wildCard) {
113        /*
114         *  According to JLS(http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1):
115         *  - Lower and upper can't coexist: (for instance, this is not allowed: <? extends List<String> & super MyInterface>)
116         *  - Multiple bounds are not supported (for instance, this is not allowed: <? extends List<String> & MyInterface>)
117         */
118
119        WildCardBoundedType wildCardBoundedType = new WildCardBoundedType(wildCard);
120        if (wildCardBoundedType.firstBound() instanceof TypeVariable) {
121            return boundsOf((TypeVariable) wildCardBoundedType.firstBound());
122        }
123
124        return wildCardBoundedType;
125    }
126
127
128
129    /**
130     * @return Raw type of the current instance.
131     */
132    public abstract Class<?> rawType();
133
134
135
136    /**
137     * @return Returns extra interfaces <strong>if relevant</strong>, otherwise empty List.
138     */
139    public List<Type> extraInterfaces() {
140        return Collections.emptyList();
141    }
142
143    /**
144     * @return Returns an array with the raw types of {@link #extraInterfaces()} <strong>if relevant</strong>.
145     */
146    public Class<?>[] rawExtraInterfaces() {
147        return new Class[0];
148    }
149
150
151
152    /**
153     * @return Actual type arguments matching the type variables of the raw type represented by this {@link GenericMetadataSupport} instance.
154     */
155    public Map<TypeVariable, Type> actualTypeArguments() {
156        TypeVariable[] typeParameters = rawType().getTypeParameters();
157        LinkedHashMap<TypeVariable, Type> actualTypeArguments = new LinkedHashMap<TypeVariable, Type>();
158
159        for (TypeVariable typeParameter : typeParameters) {
160
161            Type actualType = getActualTypeArgumentFor(typeParameter);
162
163            actualTypeArguments.put(typeParameter, actualType);
164            // logger.log("For '" + rawType().getCanonicalName() + "' returning explicit TypeVariable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualType +"' }");
165        }
166
167        return actualTypeArguments;
168    }
169
170    protected Type getActualTypeArgumentFor(TypeVariable typeParameter) {
171        Type type = this.contextualActualTypeParameters.get(typeParameter);
172        if (type instanceof TypeVariable) {
173            TypeVariable typeVariable = (TypeVariable) type;
174            return getActualTypeArgumentFor(typeVariable);
175        }
176
177        return type;
178    }
179
180
181
182    /**
183     * Resolve current method generic return type to a {@link GenericMetadataSupport}.
184     *
185     * @param method Method to resolve the return type.
186     * @return {@link GenericMetadataSupport} representing this generic return type.
187     */
188    public GenericMetadataSupport resolveGenericReturnType(Method method) {
189        Type genericReturnType = method.getGenericReturnType();
190        // logger.log("Method '" + method.toGenericString() + "' has return type : " + genericReturnType.getClass().getInterfaces()[0].getSimpleName() + " : " + genericReturnType);
191
192        if (genericReturnType instanceof Class) {
193            return new NotGenericReturnTypeSupport(genericReturnType);
194        }
195        if (genericReturnType instanceof ParameterizedType) {
196            return new ParameterizedReturnType(this, method.getTypeParameters(), (ParameterizedType) method.getGenericReturnType());
197        }
198        if (genericReturnType instanceof TypeVariable) {
199            return new TypeVariableReturnType(this, method.getTypeParameters(), (TypeVariable) genericReturnType);
200        }
201
202        throw new MockitoException("Ouch, it shouldn't happen, type '" + genericReturnType.getClass().getCanonicalName() + "' on method : '" + method.toGenericString() + "' is not supported : " + genericReturnType);
203    }
204
205    /**
206     * Create an new instance of {@link GenericMetadataSupport} inferred from a {@link Type}.
207     *
208     * <p>
209     *     At the moment <code>type</code> can only be a {@link Class} or a {@link ParameterizedType}, otherwise
210     *     it'll throw a {@link MockitoException}.
211     * </p>
212     *
213     * @param type The class from which the {@link GenericMetadataSupport} should be built.
214     * @return The new {@link GenericMetadataSupport}.
215     * @throws MockitoException Raised if type is not a {@link Class} or a {@link ParameterizedType}.
216     */
217    public static GenericMetadataSupport inferFrom(Type type) {
218        Checks.checkNotNull(type, "type");
219        if (type instanceof Class) {
220            return new FromClassGenericMetadataSupport((Class<?>) type);
221        }
222        if (type instanceof ParameterizedType) {
223            return new FromParameterizedTypeGenericMetadataSupport((ParameterizedType) type);
224        }
225
226        throw new MockitoException("Type meta-data for this Type (" + type.getClass().getCanonicalName() + ") is not supported : " + type);
227    }
228
229
230    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
231    //// Below are specializations of GenericMetadataSupport that could handle retrieval of possible Types
232    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
233
234    /**
235     * Generic metadata implementation for {@link Class}.
236     *
237     * Offer support to retrieve generic metadata on a {@link Class} by reading type parameters and type variables on
238     * the class and its ancestors and interfaces.
239     */
240    private static class FromClassGenericMetadataSupport extends GenericMetadataSupport {
241        private Class<?> clazz;
242
243        public FromClassGenericMetadataSupport(Class<?> clazz) {
244            this.clazz = clazz;
245            readActualTypeParametersOnDeclaringClass();
246        }
247
248        private void readActualTypeParametersOnDeclaringClass() {
249            registerTypeParametersOn(clazz.getTypeParameters());
250            registerTypeVariablesOn(clazz.getGenericSuperclass());
251            for (Type genericInterface : clazz.getGenericInterfaces()) {
252                registerTypeVariablesOn(genericInterface);
253            }
254        }
255
256        @Override
257        public Class<?> rawType() {
258            return clazz;
259        }
260    }
261
262
263    /**
264     * Generic metadata implementation for "standalone" {@link ParameterizedType}.
265     *
266     * Offer support to retrieve generic metadata on a {@link ParameterizedType} by reading type variables of
267     * the related raw type and declared type variable of this parameterized type.
268     *
269     * This class is not designed to work on ParameterizedType returned by {@link Method#getGenericReturnType()}, as
270     * the ParameterizedType instance return in these cases could have Type Variables that refer to type declaration(s).
271     * That's what meant the "standalone" word at the beginning of the Javadoc.
272     * Instead use {@link ParameterizedReturnType}.
273     */
274    private static class FromParameterizedTypeGenericMetadataSupport extends GenericMetadataSupport {
275        private ParameterizedType parameterizedType;
276
277        public FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType) {
278            this.parameterizedType = parameterizedType;
279            readActualTypeParameters();
280        }
281
282        private void readActualTypeParameters() {
283            registerTypeVariablesOn(parameterizedType.getRawType());
284            registerTypeVariablesOn(parameterizedType);
285        }
286
287        @Override
288        public Class<?> rawType() {
289            return (Class<?>) parameterizedType.getRawType();
290        }
291    }
292
293
294    /**
295     * Generic metadata specific to {@link ParameterizedType} returned via {@link Method#getGenericReturnType()}.
296     */
297    private static class ParameterizedReturnType extends GenericMetadataSupport {
298        private final ParameterizedType parameterizedType;
299        private final TypeVariable[] typeParameters;
300
301        public ParameterizedReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, ParameterizedType parameterizedType) {
302            this.parameterizedType = parameterizedType;
303            this.typeParameters = typeParameters;
304            this.contextualActualTypeParameters = source.contextualActualTypeParameters;
305
306            readTypeParameters();
307            readTypeVariables();
308        }
309
310        private void readTypeParameters() {
311            registerTypeParametersOn(typeParameters);
312        }
313
314        private void readTypeVariables() {
315            registerTypeVariablesOn(parameterizedType);
316        }
317
318        @Override
319        public Class<?> rawType() {
320            return (Class<?>) parameterizedType.getRawType();
321        }
322
323    }
324
325
326    /**
327     * Generic metadata for {@link TypeVariable} returned via {@link Method#getGenericReturnType()}.
328     */
329    private static class TypeVariableReturnType extends GenericMetadataSupport {
330        private final TypeVariable typeVariable;
331        private final TypeVariable[] typeParameters;
332        private Class<?> rawType;
333
334
335
336        public TypeVariableReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, TypeVariable typeVariable) {
337            this.typeParameters = typeParameters;
338            this.typeVariable = typeVariable;
339            this.contextualActualTypeParameters = source.contextualActualTypeParameters;
340
341            readTypeParameters();
342            readTypeVariables();
343        }
344
345        private void readTypeParameters() {
346            registerTypeParametersOn(typeParameters);
347        }
348
349        private void readTypeVariables() {
350            for (Type type : typeVariable.getBounds()) {
351                registerTypeVariablesOn(type);
352            }
353            registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable));
354        }
355
356        @Override
357        public Class<?> rawType() {
358            if (rawType == null) {
359                rawType = extractRawTypeOf(typeVariable);
360            }
361            return rawType;
362        }
363
364        private Class<?> extractRawTypeOf(Type type) {
365            if (type instanceof Class) {
366                return (Class<?>) type;
367            }
368            if (type instanceof ParameterizedType) {
369                return (Class<?>) ((ParameterizedType) type).getRawType();
370            }
371            if (type instanceof BoundedType) {
372                return extractRawTypeOf(((BoundedType) type).firstBound());
373            }
374            if (type instanceof TypeVariable) {
375                /*
376                 * If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared
377                 * on the class definition, such as such as List<E>.
378                 */
379                return extractRawTypeOf(contextualActualTypeParameters.get(type));
380            }
381            throw new MockitoException("Raw extraction not supported for : '" + type + "'");
382        }
383
384        @Override
385        public List<Type> extraInterfaces() {
386            Type type = extractActualBoundedTypeOf(typeVariable);
387            if (type instanceof BoundedType) {
388                return Arrays.asList(((BoundedType) type).interfaceBounds());
389            }
390            if (type instanceof ParameterizedType) {
391                return Collections.singletonList(type);
392            }
393            if (type instanceof Class) {
394                return Collections.emptyList();
395            }
396            throw new MockitoException("Cannot extract extra-interfaces from '" + typeVariable + "' : '" + type + "'");
397        }
398
399        /**
400         * @return Returns an array with the extracted raw types of {@link #extraInterfaces()}.
401         * @see #extractRawTypeOf(java.lang.reflect.Type)
402         */
403        public Class<?>[] rawExtraInterfaces() {
404            List<Type> extraInterfaces = extraInterfaces();
405            List<Class<?>> rawExtraInterfaces = new ArrayList<Class<?>>();
406            for (Type extraInterface : extraInterfaces) {
407                Class<?> rawInterface = extractRawTypeOf(extraInterface);
408                // avoid interface collision with actual raw type (with typevariables, resolution ca be quite aggressive)
409                if(!rawType().equals(rawInterface)) {
410                    rawExtraInterfaces.add(rawInterface);
411                }
412            }
413            return rawExtraInterfaces.toArray(new Class[rawExtraInterfaces.size()]);
414        }
415
416        private Type extractActualBoundedTypeOf(Type type) {
417            if (type instanceof TypeVariable) {
418                /*
419                If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared
420                on the class definition, such as such as List<E>.
421                */
422                return extractActualBoundedTypeOf(contextualActualTypeParameters.get(type));
423            }
424            if (type instanceof BoundedType) {
425                Type actualFirstBound = extractActualBoundedTypeOf(((BoundedType) type).firstBound());
426                if (!(actualFirstBound instanceof BoundedType)) {
427                    return type; // avoid going one step further, ie avoid : O(TypeVar) -> K(TypeVar) -> Some ParamType
428                }
429                return actualFirstBound;
430            }
431            return type; // irrelevant, we don't manage other types as they are not bounded.
432        }
433    }
434
435
436
437    /**
438     * Non-Generic metadata for {@link Class} returned via {@link Method#getGenericReturnType()}.
439     */
440    private static class NotGenericReturnTypeSupport extends GenericMetadataSupport {
441        private final Class<?> returnType;
442
443        public NotGenericReturnTypeSupport(Type genericReturnType) {
444            returnType = (Class<?>) genericReturnType;
445        }
446
447        @Override
448        public Class<?> rawType() {
449            return returnType;
450        }
451    }
452
453
454
455    /**
456     * Type representing bounds of a type
457     *
458     * @see TypeVarBoundedType
459     * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
460     * @see WildCardBoundedType
461     * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1</a>
462     */
463    public static interface BoundedType extends Type {
464        Type firstBound();
465
466        Type[] interfaceBounds();
467    }
468
469    /**
470     * Type representing bounds of a type variable, allows to keep all bounds information.
471     *
472     * <p>It uses the first bound in the array, as this array is never null and always contains at least
473     * one element (Object is always here if no bounds are declared).</p>
474     *
475     * <p>If upper bounds are declared with SomeClass and additional interfaces, then firstBound will be SomeClass and
476     * interfacesBound will be an array of the additional interfaces.
477     *
478     * i.e. <code>SomeClass</code>.
479     * <pre class="code"><code class="java">
480     *     interface UpperBoundedTypeWithClass<E extends Comparable<E> & Cloneable> {
481     *         E get();
482     *     }
483     *     // will return Comparable type
484     * </code></pre>
485     * </p>
486     *
487     * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
488     */
489    public static class TypeVarBoundedType implements BoundedType {
490        private TypeVariable typeVariable;
491
492
493        public TypeVarBoundedType(TypeVariable typeVariable) {
494            this.typeVariable = typeVariable;
495        }
496
497        /**
498         * @return either a class or an interface (parameterized or not), if no bounds declared Object is returned.
499         */
500        public Type firstBound() {
501            return typeVariable.getBounds()[0]; //
502        }
503
504        /**
505         * On a Type Variable (typeVar extends C_0 & I_1 & I_2 & etc), will return an array
506         * containing I_1 and I_2.
507         *
508         * @return other bounds for this type, these bounds can only be only interfaces as the JLS says,
509         * empty array if no other bound declared.
510         */
511        public Type[] interfaceBounds() {
512            Type[] interfaceBounds = new Type[typeVariable.getBounds().length - 1];
513            System.arraycopy(typeVariable.getBounds(), 1, interfaceBounds, 0, typeVariable.getBounds().length - 1);
514            return interfaceBounds;
515        }
516
517        @Override
518        public boolean equals(Object o) {
519            if (this == o) return true;
520            if (o == null || getClass() != o.getClass()) return false;
521
522            return typeVariable.equals(((TypeVarBoundedType) o).typeVariable);
523
524        }
525
526        @Override
527        public int hashCode() {
528            return typeVariable.hashCode();
529        }
530
531        @Override
532        public String toString() {
533            final StringBuilder sb = new StringBuilder();
534            sb.append("{firstBound=").append(firstBound());
535            sb.append(", interfaceBounds=").append(Arrays.deepToString(interfaceBounds()));
536            sb.append('}');
537            return sb.toString();
538        }
539
540        public TypeVariable typeVariable() {
541            return typeVariable;
542        }
543    }
544
545    /**
546     * Type representing bounds of a wildcard, allows to keep all bounds information.
547     *
548     * <p>The JLS says that lower bound and upper bound are mutually exclusive, and that multiple bounds
549     * are not allowed.
550     *
551     * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
552     */
553    public static class WildCardBoundedType implements BoundedType {
554        private WildcardType wildcard;
555
556
557        public WildCardBoundedType(WildcardType wildcard) {
558            this.wildcard = wildcard;
559        }
560
561        /**
562         * @return The first bound, either a type or a reference to a TypeVariable
563         */
564        public Type firstBound() {
565            Type[] lowerBounds = wildcard.getLowerBounds();
566            Type[] upperBounds = wildcard.getUpperBounds();
567
568            return lowerBounds.length != 0 ? lowerBounds[0] : upperBounds[0];
569        }
570
571        /**
572         * @return An empty array as, wildcard don't support multiple bounds.
573         */
574        public Type[] interfaceBounds() {
575            return new Type[0];
576        }
577
578        @Override
579        public boolean equals(Object o) {
580            if (this == o) return true;
581            if (o == null || getClass() != o.getClass()) return false;
582
583            return wildcard.equals(((TypeVarBoundedType) o).typeVariable);
584
585        }
586
587        @Override
588        public int hashCode() {
589            return wildcard.hashCode();
590        }
591
592        @Override
593        public String toString() {
594            final StringBuilder sb = new StringBuilder();
595            sb.append("{firstBound=").append(firstBound());
596            sb.append(", interfaceBounds=[]}");
597            return sb.toString();
598        }
599
600        public WildcardType wildCard() {
601            return wildcard;
602        }
603    }
604
605}
606
607
608