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