1
2
3
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 }