1 /*
2  * Copyright (c) 2007 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.util.reflection;
6 
7 
8 import org.mockito.Incubating;
9 import org.mockito.exceptions.base.MockitoException;
10 import org.mockito.internal.util.Checks;
11 
12 import java.lang.reflect.*;
13 import 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
58 public 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 
registerTypeVariablesOn(Type classType)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 
registerTypeParametersOn(TypeVariable[] typeParameters)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      */
boundsOf(TypeVariable typeParameter)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      */
boundsOf(WildcardType wildCard)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      */
rawType()132     public abstract Class<?> rawType();
133 
134 
135 
136     /**
137      * @return Returns extra interfaces <strong>if relevant</strong>, otherwise empty List.
138      */
extraInterfaces()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      */
rawExtraInterfaces()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      */
actualTypeArguments()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 
getActualTypeArgumentFor(TypeVariable typeParameter)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      */
resolveGenericReturnType(Method method)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      */
inferFrom(Type type)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 
FromClassGenericMetadataSupport(Class<?> clazz)243         public FromClassGenericMetadataSupport(Class<?> clazz) {
244             this.clazz = clazz;
245             readActualTypeParametersOnDeclaringClass();
246         }
247 
readActualTypeParametersOnDeclaringClass()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
rawType()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 
FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType)277         public FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType) {
278             this.parameterizedType = parameterizedType;
279             readActualTypeParameters();
280         }
281 
readActualTypeParameters()282         private void readActualTypeParameters() {
283             registerTypeVariablesOn(parameterizedType.getRawType());
284             registerTypeVariablesOn(parameterizedType);
285         }
286 
287         @Override
rawType()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 
ParameterizedReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, ParameterizedType parameterizedType)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 
readTypeParameters()310         private void readTypeParameters() {
311             registerTypeParametersOn(typeParameters);
312         }
313 
readTypeVariables()314         private void readTypeVariables() {
315             registerTypeVariablesOn(parameterizedType);
316         }
317 
318         @Override
rawType()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 
TypeVariableReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, TypeVariable typeVariable)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 
readTypeParameters()345         private void readTypeParameters() {
346             registerTypeParametersOn(typeParameters);
347         }
348 
readTypeVariables()349         private void readTypeVariables() {
350             for (Type type : typeVariable.getBounds()) {
351                 registerTypeVariablesOn(type);
352             }
353             registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable));
354         }
355 
356         @Override
rawType()357         public Class<?> rawType() {
358             if (rawType == null) {
359                 rawType = extractRawTypeOf(typeVariable);
360             }
361             return rawType;
362         }
363 
extractRawTypeOf(Type type)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
extraInterfaces()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          */
rawExtraInterfaces()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 
extractActualBoundedTypeOf(Type type)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 
NotGenericReturnTypeSupport(Type genericReturnType)443         public NotGenericReturnTypeSupport(Type genericReturnType) {
444             returnType = (Class<?>) genericReturnType;
445         }
446 
447         @Override
rawType()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 {
firstBound()464         Type firstBound();
465 
interfaceBounds()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 
TypeVarBoundedType(TypeVariable typeVariable)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          */
firstBound()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          */
interfaceBounds()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
equals(Object o)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
hashCode()527         public int hashCode() {
528             return typeVariable.hashCode();
529         }
530 
531         @Override
toString()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 
typeVariable()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 
WildCardBoundedType(WildcardType wildcard)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          */
firstBound()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          */
interfaceBounds()574         public Type[] interfaceBounds() {
575             return new Type[0];
576         }
577 
578         @Override
equals(Object o)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
hashCode()588         public int hashCode() {
589             return wildcard.hashCode();
590         }
591 
592         @Override
toString()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 
wildCard()600         public WildcardType wildCard() {
601             return wildcard;
602         }
603     }
604 
605 }
606 
607 
608