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.injection;
6   
7   import static mockit.internal.reflection.MethodReflection.invoke;
8   import static mockit.internal.util.Utilities.getClassType;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.lang.reflect.Method;
14  import java.lang.reflect.ParameterizedType;
15  import java.lang.reflect.Type;
16  import java.lang.reflect.WildcardType;
17  import java.util.Map.Entry;
18  import java.util.NavigableMap;
19  import java.util.TreeMap;
20  
21  final class InterfaceResolution {
22      @NonNull
23      private final NavigableMap<ParameterizedType, Method> interfaceResolutionMethods;
24  
25      InterfaceResolution() {
26          interfaceResolutionMethods = new TreeMap<>((t1, t2) -> {
27              if (t1 == t2) {
28                  return 0;
29              }
30  
31              Type targetType1 = t1.getActualTypeArguments()[0];
32              Type targetType2 = t2.getActualTypeArguments()[0];
33  
34              if (targetType1 == targetType2) {
35                  return 0;
36              }
37  
38              if (targetType1 instanceof WildcardType) {
39                  if (targetType2 instanceof WildcardType) {
40                      return compareTypesFromResolutionMethods((WildcardType) targetType1, (WildcardType) targetType2);
41                  }
42  
43                  return 1;
44              }
45  
46              return -1;
47          });
48      }
49  
50      private static int compareTypesFromResolutionMethods(@NonNull WildcardType type1, @NonNull WildcardType type2) {
51          Type upperBound1 = type1.getUpperBounds()[0];
52          Class<?> classOfUpperBound1 = getClassType(upperBound1);
53  
54          Type upperBound2 = type2.getUpperBounds()[0];
55          Class<?> classOfUpperBound2 = getClassType(upperBound2);
56  
57          if (classOfUpperBound1.isAssignableFrom(classOfUpperBound2)) {
58              return 1;
59          }
60  
61          if (classOfUpperBound2.isAssignableFrom(classOfUpperBound1)) {
62              return -1;
63          }
64  
65          return classOfUpperBound1.getName().compareTo(classOfUpperBound2.getName());
66      }
67  
68      boolean canResolveInterfaces() {
69          return !interfaceResolutionMethods.isEmpty();
70      }
71  
72      void addInterfaceResolutionMethod(@NonNull ParameterizedType interfaceType, @NonNull Method resolutionMethod) {
73          interfaceResolutionMethods.put(interfaceType, resolutionMethod);
74      }
75  
76      @Nullable
77      Class<?> resolveInterface(@NonNull Class<?> anInterface, @NonNull Object testClassInstance) {
78          if (interfaceResolutionMethods.isEmpty()) {
79              return null;
80          }
81  
82          for (Entry<ParameterizedType, Method> typeAndMethod : interfaceResolutionMethods.entrySet()) {
83              ParameterizedType acceptedType = typeAndMethod.getKey();
84              Method method = typeAndMethod.getValue();
85              Type targetType = acceptedType.getActualTypeArguments()[0];
86  
87              if (targetType == anInterface || targetType instanceof WildcardType
88                      && satisfiesUpperBounds(anInterface, (WildcardType) targetType)) {
89                  Class<?> implementationClass = invoke(testClassInstance, method, anInterface);
90  
91                  if (implementationClass != null) {
92                      return implementationClass;
93                  }
94              }
95          }
96  
97          return null;
98      }
99  
100     private static boolean satisfiesUpperBounds(@NonNull Class<?> interfaceType, @NonNull WildcardType targetType) {
101         for (Type upperBound : targetType.getUpperBounds()) {
102             Class<?> classOfUpperBound = getClassType(upperBound);
103 
104             if (!classOfUpperBound.isAssignableFrom(interfaceType)) {
105                 return false;
106             }
107         }
108 
109         return true;
110     }
111 }