View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.reflection;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.lang.reflect.GenericArrayType;
11  import java.lang.reflect.GenericDeclaration;
12  import java.lang.reflect.Member;
13  import java.lang.reflect.ParameterizedType;
14  import java.lang.reflect.Type;
15  import java.lang.reflect.TypeVariable;
16  import java.lang.reflect.WildcardType;
17  import java.util.ArrayList;
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Map.Entry;
23  
24  @SuppressWarnings("OverlyComplexClass")
25  public final class GenericTypeReflection {
26      @NonNull
27      private final Map<String, Type> typeParametersToTypeArguments;
28      @NonNull
29      private final Map<String, String> typeParametersToTypeArgumentNames;
30      private final boolean withSignatures;
31  
32      public GenericTypeReflection(@NonNull Class<?> ownerClass, @Nullable Type genericType) {
33          this(ownerClass, genericType, true);
34      }
35  
36      public GenericTypeReflection(@NonNull Class<?> ownerClass, @Nullable Type genericType, boolean withSignatures) {
37          typeParametersToTypeArguments = new HashMap<>(4);
38          typeParametersToTypeArgumentNames = withSignatures ? new HashMap<>(4) : Collections.<String, String> emptyMap();
39          this.withSignatures = withSignatures;
40          discoverTypeMappings(ownerClass, genericType);
41      }
42  
43      private void discoverTypeMappings(@NonNull Class<?> rawType, @Nullable Type genericType) {
44          if (genericType instanceof ParameterizedType) {
45              addMappingsFromTypeParametersToTypeArguments(rawType, (ParameterizedType) genericType);
46          }
47  
48          addGenericTypeMappingsForSuperTypes(rawType);
49      }
50  
51      private void addGenericTypeMappingsForSuperTypes(@NonNull Class<?> rawType) {
52          Type superType = rawType;
53  
54          while (superType != null && superType != Object.class) {
55              Class<?> superClass = (Class<?>) superType;
56              superType = superClass.getGenericSuperclass();
57  
58              if (superType != null && superType != Object.class) {
59                  superClass = addGenericTypeMappingsIfParameterized(superType);
60                  superType = superClass;
61              }
62  
63              addGenericTypeMappingsForInterfaces(superClass);
64          }
65      }
66  
67      @NonNull
68      private Class<?> addGenericTypeMappingsIfParameterized(@NonNull Type superType) {
69          if (superType instanceof ParameterizedType) {
70              ParameterizedType genericSuperType = (ParameterizedType) superType;
71              Class<?> rawType = (Class<?>) genericSuperType.getRawType();
72              addMappingsFromTypeParametersToTypeArguments(rawType, genericSuperType);
73              return rawType;
74          }
75  
76          return (Class<?>) superType;
77      }
78  
79      private void addGenericTypeMappingsForInterfaces(@NonNull Class<?> classOrInterface) {
80          for (Type implementedInterface : classOrInterface.getGenericInterfaces()) {
81              Class<?> implementedType = addGenericTypeMappingsIfParameterized(implementedInterface);
82              addGenericTypeMappingsForInterfaces(implementedType);
83          }
84      }
85  
86      private void addMappingsFromTypeParametersToTypeArguments(@NonNull Class<?> rawType,
87              @NonNull ParameterizedType genericType) {
88          String ownerTypeDesc = getOwnerClassDesc(rawType);
89          TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
90          Type[] typeArguments = genericType.getActualTypeArguments();
91  
92          for (int i = 0, n = typeParameters.length; i < n; i++) {
93              TypeVariable<?> typeParam = typeParameters[i];
94              String typeVarName = typeParam.getName();
95  
96              if (typeParametersToTypeArguments.containsKey(ownerTypeDesc + ':' + typeVarName)) {
97                  continue;
98              }
99  
100             Type typeArg = typeArguments[i];
101 
102             if (typeArg instanceof Class<?>) {
103                 addMappingForClassType(ownerTypeDesc, typeVarName, typeArg);
104             } else if (typeArg instanceof TypeVariable<?>) {
105                 addMappingForTypeVariable(ownerTypeDesc, typeVarName, typeArg);
106             } else if (typeArg instanceof ParameterizedType) {
107                 addMappingForParameterizedType(ownerTypeDesc, typeVarName, typeArg);
108             } else if (typeArg instanceof GenericArrayType) {
109                 addMappingForArrayType(ownerTypeDesc, typeVarName, typeArg);
110             } else {
111                 addMappingForFirstTypeBound(ownerTypeDesc, typeParam);
112             }
113         }
114 
115         Type outerType = genericType.getOwnerType();
116 
117         if (outerType instanceof ParameterizedType) {
118             ParameterizedType parameterizedOuterType = (ParameterizedType) outerType;
119             Class<?> rawOuterType = (Class<?>) parameterizedOuterType.getRawType();
120             addMappingsFromTypeParametersToTypeArguments(rawOuterType, parameterizedOuterType);
121         }
122     }
123 
124     private void addMappingForClassType(@NonNull String ownerTypeDesc, @NonNull String typeName,
125             @NonNull Type typeArg) {
126         String mappedTypeArgName = null;
127 
128         if (withSignatures) {
129             Class<?> classArg = (Class<?>) typeArg;
130             String ownerClassDesc = getOwnerClassDesc(classArg);
131             mappedTypeArgName = classArg.isArray() ? ownerClassDesc : 'L' + ownerClassDesc;
132         }
133 
134         addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
135     }
136 
137     private void addMappingForTypeVariable(@NonNull String ownerTypeDesc, @NonNull String typeName,
138             @NonNull Type typeArg) {
139         @Nullable
140         String mappedTypeArgName = null;
141 
142         if (withSignatures) {
143             TypeVariable<?> typeVar = (TypeVariable<?>) typeArg;
144             String ownerClassDesc = getOwnerClassDesc(typeVar);
145             String intermediateTypeArg = ownerClassDesc + ":T" + typeVar.getName();
146             mappedTypeArgName = typeParametersToTypeArgumentNames.get(intermediateTypeArg);
147         }
148 
149         addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
150     }
151 
152     private void addMappingForParameterizedType(@NonNull String ownerTypeDesc, @NonNull String typeName,
153             @NonNull Type typeArg) {
154         String mappedTypeArgName = getMappedTypeArgName(typeArg);
155         addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
156     }
157 
158     @Nullable
159     private String getMappedTypeArgName(@NonNull Type typeArg) {
160         if (withSignatures) {
161             Class<?> classType = getClassType(typeArg);
162             return 'L' + getOwnerClassDesc(classType);
163         }
164 
165         return null;
166     }
167 
168     private void addMappingForArrayType(@NonNull String ownerTypeDesc, @NonNull String typeName,
169             @NonNull Type typeArg) {
170         String mappedTypeArgName = null;
171 
172         if (withSignatures) {
173             mappedTypeArgName = getMappedTypeArgName((GenericArrayType) typeArg);
174         }
175 
176         addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
177     }
178 
179     private void addMappingForFirstTypeBound(@NonNull String ownerTypeDesc, @NonNull TypeVariable<?> typeParam) {
180         Type typeArg = typeParam.getBounds()[0];
181         String mappedTypeArgName = getMappedTypeArgName(typeArg);
182         addTypeMapping(ownerTypeDesc, typeParam.getName(), typeArg, mappedTypeArgName);
183     }
184 
185     @NonNull
186     private static String getOwnerClassDesc(@NonNull Class<?> rawType) {
187         return rawType.getName().replace('.', '/');
188     }
189 
190     @NonNull
191     private Class<?> getClassType(@NonNull Type type) {
192         if (type instanceof ParameterizedType) {
193             ParameterizedType parameterizedType = (ParameterizedType) type;
194             return (Class<?>) parameterizedType.getRawType();
195         }
196 
197         if (type instanceof TypeVariable<?>) {
198             TypeVariable<?> typeVar = (TypeVariable<?>) type;
199             String typeVarKey = getTypeVariableKey(typeVar);
200             @Nullable
201             Type typeArg = typeParametersToTypeArguments.get(typeVarKey);
202 
203             if (typeArg == null) {
204                 throw new IllegalArgumentException("Unable to resolve type variable \"" + typeVar.getName() + '"');
205             }
206 
207             // noinspection TailRecursion
208             return getClassType(typeArg);
209         }
210 
211         return (Class<?>) type;
212     }
213 
214     @NonNull
215     private String getMappedTypeArgName(@NonNull GenericArrayType arrayType) {
216         StringBuilder argName = new StringBuilder(20);
217         argName.append('[');
218 
219         while (true) {
220             Type componentType = arrayType.getGenericComponentType();
221 
222             if (!(componentType instanceof GenericArrayType)) {
223                 Class<?> classType = getClassType(componentType);
224                 argName.append('L').append(getOwnerClassDesc(classType));
225                 return argName.toString();
226             }
227             argName.append('[');
228             // noinspection AssignmentToMethodParameter
229             arrayType = (GenericArrayType) componentType;
230         }
231     }
232 
233     private void addTypeMapping(@NonNull String ownerTypeDesc, @NonNull String typeVarName, @NonNull Type mappedTypeArg,
234             @Nullable String mappedTypeArgName) {
235         typeParametersToTypeArguments.put(ownerTypeDesc + ':' + typeVarName, mappedTypeArg);
236 
237         if (mappedTypeArgName != null) {
238             addTypeMapping(ownerTypeDesc, typeVarName, mappedTypeArgName);
239         }
240     }
241 
242     private void addTypeMapping(@NonNull String ownerTypeDesc, @NonNull String typeVarName,
243             @NonNull String mappedTypeArgName) {
244         String typeMappingKey = ownerTypeDesc + ":T" + typeVarName;
245         typeParametersToTypeArgumentNames.put(typeMappingKey, mappedTypeArgName);
246     }
247 
248     public final class GenericSignature {
249         private final List<String> parameters = new ArrayList<>();
250         private final String parameterTypeDescs;
251         private final int lengthOfParameterTypeDescs;
252         private int currentPos;
253 
254         GenericSignature(@NonNull String signature) {
255             int p = signature.indexOf('(');
256             int q = signature.lastIndexOf(')');
257             parameterTypeDescs = signature.substring(p + 1, q);
258             lengthOfParameterTypeDescs = parameterTypeDescs.length();
259             addTypeDescsToList();
260         }
261 
262         private void addTypeDescsToList() {
263             while (currentPos < lengthOfParameterTypeDescs) {
264                 addNextParameter();
265             }
266         }
267 
268         private void addNextParameter() {
269             int startPos = currentPos;
270             int endPos;
271             char c = parameterTypeDescs.charAt(startPos);
272 
273             switch (c) {
274                 case 'T':
275                     endPos = parameterTypeDescs.indexOf(';', startPos);
276                     currentPos = endPos;
277                     break;
278                 case 'L':
279                     endPos = advanceToEndOfTypeDesc();
280                     break;
281                 case '[': {
282                     char elemTypeStart = firstCharacterOfArrayElementType();
283                     if (elemTypeStart == 'T') {
284                         endPos = parameterTypeDescs.indexOf(';', startPos);
285                         currentPos = endPos;
286                     } else if (elemTypeStart == 'L') {
287                         endPos = advanceToEndOfTypeDesc();
288                     } else {
289                         endPos = currentPos + 1;
290                     }
291                     break;
292                 }
293                 default:
294                     endPos = currentPos + 1;
295                     break;
296             }
297 
298             currentPos++;
299             String parameter = parameterTypeDescs.substring(startPos, endPos);
300             parameters.add(parameter);
301         }
302 
303         private int advanceToEndOfTypeDesc() {
304             char c = '\0';
305 
306             do {
307                 currentPos++;
308                 if (currentPos == lengthOfParameterTypeDescs) {
309                     break;
310                 }
311                 c = parameterTypeDescs.charAt(currentPos);
312             } while (c != ';' && c != '<');
313 
314             int endPos = currentPos;
315 
316             if (c == '<') {
317                 advancePastTypeArguments();
318                 currentPos++;
319             }
320 
321             return endPos;
322         }
323 
324         private char firstCharacterOfArrayElementType() {
325             char c;
326 
327             do {
328                 currentPos++;
329                 c = parameterTypeDescs.charAt(currentPos);
330             } while (c == '[');
331 
332             return c;
333         }
334 
335         private void advancePastTypeArguments() {
336             int angleBracketDepth = 1;
337 
338             do {
339                 currentPos++;
340                 char c = parameterTypeDescs.charAt(currentPos);
341                 if (c == '>') {
342                     angleBracketDepth--;
343                 } else if (c == '<') {
344                     angleBracketDepth++;
345                 }
346             } while (angleBracketDepth > 0);
347         }
348 
349         public boolean satisfiesGenericSignature(@NonNull String otherSignature) {
350             GenericSignature other = new GenericSignature(otherSignature);
351             return areMatchingSignatures(other);
352         }
353 
354         private boolean areMatchingSignatures(@NonNull GenericSignature other) {
355             int n = parameters.size();
356 
357             if (n != other.parameters.size()) {
358                 return false;
359             }
360 
361             for (int i = 0; i < n; i++) {
362                 String p1 = other.parameters.get(i);
363                 String p2 = parameters.get(i);
364 
365                 if (!areParametersOfSameType(p1, p2)) {
366                     return false;
367                 }
368             }
369 
370             return true;
371         }
372 
373         @SuppressWarnings("MethodWithMultipleLoops")
374         private boolean areParametersOfSameType(@NonNull String param1, @NonNull String param2) {
375             if (param1.equals(param2)) {
376                 return true;
377             }
378 
379             int i = -1;
380             char c;
381             do {
382                 i++;
383                 c = param1.charAt(i);
384             } while (c == '[');
385             if (c != 'T') {
386                 return false;
387             }
388 
389             String typeVarName1 = param1.substring(i);
390             String typeVarName2 = param2.substring(i);
391             String typeArg1 = null;
392 
393             for (Entry<String, String> typeParamAndArgName : typeParametersToTypeArgumentNames.entrySet()) {
394                 String typeMappingKey = typeParamAndArgName.getKey();
395                 String typeVarName = typeMappingKey.substring(typeMappingKey.indexOf(':') + 1);
396 
397                 if (typeVarName.equals(typeVarName1)) {
398                     typeArg1 = typeParamAndArgName.getValue();
399                     break;
400                 }
401             }
402 
403             return typeVarName2.equals(typeArg1);
404         }
405 
406         public boolean satisfiesSignature(@NonNull String otherSignature) {
407             GenericSignature other = new GenericSignature(otherSignature);
408             return other.areMatchingSignatures(this);
409         }
410     }
411 
412     @NonNull
413     public GenericSignature parseSignature(@NonNull String genericSignature) {
414         return new GenericSignature(genericSignature);
415     }
416 
417     @NonNull
418     public String resolveSignature(@NonNull String ownerTypeDesc, @NonNull String genericSignature) {
419         addTypeArgumentsIfAvailable(ownerTypeDesc, genericSignature);
420 
421         int p = genericSignature.lastIndexOf(')') + 1;
422         int q = genericSignature.length();
423         String returnType = genericSignature.substring(p, q);
424         String resolvedReturnType = replaceTypeParametersWithActualTypes(ownerTypeDesc, returnType);
425 
426         StringBuilder finalSignature = new StringBuilder(genericSignature);
427         finalSignature.replace(p, q, resolvedReturnType);
428         return finalSignature.toString();
429     }
430 
431     private void addTypeArgumentsIfAvailable(@NonNull String ownerTypeDesc, @NonNull String signature) {
432         int firstParen = signature.indexOf('(');
433         if (firstParen == 0) {
434             return;
435         }
436 
437         int p = 1;
438         boolean lastMappingFound = false;
439 
440         while (!lastMappingFound) {
441             int q = signature.indexOf(':', p);
442             String typeVar = signature.substring(p, q);
443 
444             q++;
445 
446             if (signature.charAt(q) == ':') {
447                 q++; // an unbounded type argument uses ":" as separator, while a bounded one uses "::"
448             }
449 
450             int r = signature.indexOf(':', q);
451 
452             if (r < 0) {
453                 r = firstParen - 2;
454                 lastMappingFound = true;
455             } else {
456                 r = signature.lastIndexOf(';', r);
457                 p = r + 1;
458             }
459 
460             String typeArg = signature.substring(q, r);
461             addTypeMapping(ownerTypeDesc, typeVar, typeArg);
462         }
463     }
464 
465     @NonNull
466     private String replaceTypeParametersWithActualTypes(@NonNull String ownerTypeDesc, @NonNull String typeDesc) {
467         if (typeDesc.charAt(0) == 'T' && !typeParametersToTypeArgumentNames.isEmpty()) {
468             return replaceTypeParameters(ownerTypeDesc, typeDesc);
469         }
470 
471         int p = typeDesc.indexOf('<');
472 
473         if (p < 0) {
474             return typeDesc;
475         }
476 
477         String resolvedTypeDesc = typeDesc;
478 
479         for (Entry<String, String> paramAndArg : typeParametersToTypeArgumentNames.entrySet()) {
480             String typeMappingKey = paramAndArg.getKey();
481             String typeParam = typeMappingKey.substring(typeMappingKey.indexOf(':') + 1) + ';';
482             String typeArg = paramAndArg.getValue() + ';';
483             resolvedTypeDesc = resolvedTypeDesc.replace(typeParam, typeArg);
484         }
485 
486         return resolvedTypeDesc;
487     }
488 
489     @NonNull
490     private String replaceTypeParameters(@NonNull String ownerTypeDesc, @NonNull String typeDesc) {
491         String typeParameter = typeDesc.substring(0, typeDesc.length() - 1);
492 
493         while (true) {
494             @Nullable
495             String typeArg = typeParametersToTypeArgumentNames.get(ownerTypeDesc + ':' + typeParameter);
496 
497             if (typeArg == null) {
498                 return typeDesc;
499             }
500 
501             if (typeArg.charAt(0) != 'T') {
502                 return typeArg + ';';
503             }
504 
505             typeParameter = typeArg;
506         }
507     }
508 
509     @NonNull
510     public Type resolveTypeVariable(@NonNull TypeVariable<?> typeVariable) {
511         String typeVarKey = getTypeVariableKey(typeVariable);
512         @Nullable
513         Type typeArgument = typeParametersToTypeArguments.get(typeVarKey);
514 
515         if (typeArgument == null) {
516             typeArgument = typeVariable.getBounds()[0];
517         }
518 
519         if (typeArgument instanceof TypeVariable<?>) {
520             typeArgument = resolveTypeVariable((TypeVariable<?>) typeArgument);
521         }
522 
523         return typeArgument;
524     }
525 
526     @NonNull
527     private static String getTypeVariableKey(@NonNull TypeVariable<?> typeVariable) {
528         String ownerClassDesc = getOwnerClassDesc(typeVariable);
529         return ownerClassDesc + ':' + typeVariable.getName();
530     }
531 
532     @NonNull
533     private static String getOwnerClassDesc(@NonNull TypeVariable<?> typeVariable) {
534         GenericDeclaration owner = typeVariable.getGenericDeclaration();
535         Class<?> ownerClass = owner instanceof Member ? ((Member) owner).getDeclaringClass() : (Class<?>) owner;
536         return getOwnerClassDesc(ownerClass);
537     }
538 
539     public boolean areMatchingTypes(@NonNull Type declarationType, @NonNull Type realizationType) {
540         if (declarationType.equals(realizationType)) {
541             return true;
542         }
543 
544         if (declarationType instanceof Class<?>) {
545             if (realizationType instanceof Class<?>) {
546                 return ((Class<?>) declarationType).isAssignableFrom((Class<?>) realizationType);
547             }
548         } else if (declarationType instanceof TypeVariable<?>) {
549             if (realizationType instanceof TypeVariable<?>) {
550                 return false;
551             }
552 
553             // noinspection RedundantIfStatement
554             if (areMatchingTypes((TypeVariable<?>) declarationType, realizationType)) {
555                 return true;
556             }
557         } else if (declarationType instanceof ParameterizedType) {
558             ParameterizedType parameterizedDeclarationType = (ParameterizedType) declarationType;
559             ParameterizedType parameterizedRealizationType = getParameterizedType(realizationType);
560 
561             if (parameterizedRealizationType != null) {
562                 return areMatchingTypes(parameterizedDeclarationType, parameterizedRealizationType);
563             }
564         }
565 
566         return false;
567     }
568 
569     @Nullable
570     private static ParameterizedType getParameterizedType(@NonNull Type realizationType) {
571         if (realizationType instanceof ParameterizedType) {
572             return (ParameterizedType) realizationType;
573         }
574 
575         if (realizationType instanceof Class<?>) {
576             return findRealizationSupertype((Class<?>) realizationType);
577         }
578 
579         return null;
580     }
581 
582     @Nullable
583     private static ParameterizedType findRealizationSupertype(@NonNull Class<?> realizationType) {
584         Type realizationSuperclass = realizationType.getGenericSuperclass();
585         ParameterizedType parameterizedRealizationType = null;
586 
587         if (realizationSuperclass instanceof ParameterizedType) {
588             parameterizedRealizationType = (ParameterizedType) realizationSuperclass;
589         } else {
590             for (Type realizationSupertype : realizationType.getGenericInterfaces()) {
591                 if (realizationSupertype instanceof ParameterizedType) {
592                     parameterizedRealizationType = (ParameterizedType) realizationSupertype;
593                     break;
594                 }
595             }
596         }
597 
598         return parameterizedRealizationType;
599     }
600 
601     private boolean areMatchingTypes(@NonNull TypeVariable<?> declarationType, @NonNull Type realizationType) {
602         String typeVarKey = getTypeVariableKey(declarationType);
603         @Nullable
604         Type resolvedType = typeParametersToTypeArguments.get(typeVarKey);
605 
606         return resolvedType != null && (resolvedType.equals(realizationType)
607                 || typeSatisfiesResolvedTypeVariable(resolvedType, realizationType));
608     }
609 
610     private boolean areMatchingTypes(@NonNull ParameterizedType declarationType,
611             @NonNull ParameterizedType realizationType) {
612         return declarationType.getRawType().equals(realizationType.getRawType())
613                 && haveMatchingActualTypeArguments(declarationType, realizationType);
614     }
615 
616     private boolean haveMatchingActualTypeArguments(@NonNull ParameterizedType declarationType,
617             @NonNull ParameterizedType realizationType) {
618         Type[] declaredTypeArguments = declarationType.getActualTypeArguments();
619         Type[] concreteTypeArguments = realizationType.getActualTypeArguments();
620 
621         for (int i = 0, n = declaredTypeArguments.length; i < n; i++) {
622             Type declaredTypeArg = declaredTypeArguments[i];
623             Type concreteTypeArg = concreteTypeArguments[i];
624 
625             if (declaredTypeArg instanceof TypeVariable<?>) {
626                 if (areMatchingTypeArguments((TypeVariable<?>) declaredTypeArg, concreteTypeArg)) {
627                     continue;
628                 }
629             } else if (areMatchingTypes(declaredTypeArg, concreteTypeArg)) {
630                 continue;
631             }
632 
633             return false;
634         }
635 
636         return true;
637     }
638 
639     @SuppressWarnings("RedundantIfStatement")
640     private boolean areMatchingTypeArguments(@NonNull TypeVariable<?> declaredType, @NonNull Type concreteType) {
641         String typeVarKey = getTypeVariableKey(declaredType);
642         @Nullable
643         Type resolvedType = typeParametersToTypeArguments.get(typeVarKey);
644 
645         if (resolvedType != null) {
646             if (resolvedType.equals(concreteType) || concreteType instanceof Class<?>
647                     && typeSatisfiesResolvedTypeVariable(resolvedType, (Class<?>) concreteType)) {
648                 return true;
649             }
650 
651             if (concreteType instanceof WildcardType
652                     && typeSatisfiesUpperBounds(resolvedType, ((WildcardType) concreteType).getUpperBounds())) {
653                 return true;
654             }
655         } else if (typeSatisfiesUpperBounds(concreteType, declaredType.getBounds())) {
656             return true;
657         }
658 
659         return false;
660     }
661 
662     private boolean typeSatisfiesResolvedTypeVariable(@NonNull Type resolvedType, @NonNull Type realizationType) {
663         Class<?> realizationClass = getClassType(realizationType);
664         return typeSatisfiesResolvedTypeVariable(resolvedType, realizationClass);
665     }
666 
667     private boolean typeSatisfiesResolvedTypeVariable(@NonNull Type resolvedType, @NonNull Class<?> realizationType) {
668         Class<?> resolvedClass = getClassType(resolvedType);
669         return resolvedClass.isAssignableFrom(realizationType);
670     }
671 
672     private boolean typeSatisfiesUpperBounds(@NonNull Type type, @NonNull Type[] upperBounds) {
673         Class<?> classType = getClassType(type);
674 
675         for (Type upperBound : upperBounds) {
676             Class<?> classBound = getClassType(upperBound);
677 
678             if (!classBound.isAssignableFrom(classType)) {
679                 return false;
680             }
681         }
682 
683         return true;
684     }
685 }