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