1
2
3
4
5 package mockit.internal.injection;
6
7 import static mockit.internal.injection.InjectionPoint.INJECT_CLASS;
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.ParameterizedType;
14 import java.lang.reflect.Type;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18
19 import javax.inject.Provider;
20
21 import mockit.internal.injection.InjectionPoint.KindOfInjectionPoint;
22 import mockit.internal.reflection.GenericTypeReflection;
23
24 public final class InjectionProviders {
25 @NonNull
26 private List<InjectionProvider> injectables;
27 @NonNull
28 private List<InjectionProvider> consumedInjectionProviders;
29 private Type typeOfInjectionPoint;
30 private KindOfInjectionPoint kindOfInjectionPoint;
31
32 InjectionProviders(@NonNull LifecycleMethods lifecycleMethods) {
33 injectables = Collections.emptyList();
34 consumedInjectionProviders = new ArrayList<>();
35 }
36
37 boolean setInjectables(
38 @SuppressWarnings("ParameterHidesMemberVariable") @NonNull List<? extends InjectionProvider> injectables) {
39 if (injectables.isEmpty()) {
40 this.injectables = Collections.emptyList();
41 return false;
42 }
43
44 this.injectables = new ArrayList<>(injectables);
45 return true;
46 }
47
48 @NonNull
49 List<InjectionProvider> addInjectables(@NonNull List<? extends InjectionProvider> injectablesToAdd) {
50 if (!injectablesToAdd.isEmpty()) {
51 if (injectables.isEmpty()) {
52 injectables = new ArrayList<>(injectablesToAdd);
53 } else {
54 injectables.addAll(injectablesToAdd);
55 }
56 }
57
58 return injectables;
59 }
60
61 public void setTypeOfInjectionPoint(@NonNull Type typeOfInjectionPoint,
62 @NonNull KindOfInjectionPoint kindOfInjectionPoint) {
63 this.typeOfInjectionPoint = typeOfInjectionPoint;
64 this.kindOfInjectionPoint = kindOfInjectionPoint;
65 }
66
67 @Nullable
68 public InjectionProvider getProviderByTypeAndOptionallyName(@NonNull String nameOfInjectionPoint,
69 @NonNull TestedClass testedClass) {
70 if (kindOfInjectionPoint == KindOfInjectionPoint.Required) {
71 Type elementTypeOfIterable = getElementTypeIfIterable(typeOfInjectionPoint);
72
73 if (elementTypeOfIterable != null) {
74 return findInjectablesByTypeOnly(elementTypeOfIterable, testedClass);
75 }
76 }
77
78 return findInjectableByTypeAndOptionallyName(nameOfInjectionPoint, testedClass);
79 }
80
81 @Nullable
82 private static Type getElementTypeIfIterable(@NonNull Type injectableType) {
83 if (injectableType instanceof ParameterizedType) {
84 ParameterizedType parameterizedType = (ParameterizedType) injectableType;
85 Class<?> classOfInjectionPoint = (Class<?>) parameterizedType.getRawType();
86
87 if (Iterable.class.isAssignableFrom(classOfInjectionPoint)) {
88 return parameterizedType.getActualTypeArguments()[0];
89 }
90 }
91
92 return null;
93 }
94
95 @Nullable
96 public InjectionProvider findNextInjectableForInjectionPoint(@NonNull TestedClass testedClass) {
97 for (InjectionProvider injectable : injectables) {
98 if (hasTypeAssignableToInjectionPoint(injectable, testedClass)
99 && !consumedInjectionProviders.contains(injectable)) {
100 return injectable;
101 }
102 }
103
104 return null;
105 }
106
107 private boolean hasTypeAssignableToInjectionPoint(@NonNull InjectionProvider injectable,
108 @NonNull TestedClass testedClass) {
109 Type declaredType = injectable.getDeclaredType();
110 return isAssignableToInjectionPoint(declaredType, testedClass);
111 }
112
113 boolean isAssignableToInjectionPoint(@NonNull Type injectableType, @NonNull TestedClass testedClass) {
114 if (testedClass.reflection.areMatchingTypes(typeOfInjectionPoint, injectableType)) {
115 return true;
116 }
117
118 if (typeOfInjectionPoint instanceof ParameterizedType) {
119 ParameterizedType parameterizedType = (ParameterizedType) typeOfInjectionPoint;
120 Class<?> classOfInjectionPoint = (Class<?>) parameterizedType.getRawType();
121
122 if (kindOfInjectionPoint == KindOfInjectionPoint.Required
123 && Iterable.class.isAssignableFrom(classOfInjectionPoint)
124 || INJECT_CLASS != null && Provider.class.isAssignableFrom(classOfInjectionPoint)) {
125 Type providedType = parameterizedType.getActualTypeArguments()[0];
126
127 if (providedType.equals(injectableType)) {
128 return true;
129 }
130
131 Class<?> injectableClass = getClassType(injectableType);
132 Class<?> providedClass = getClassType(providedType);
133
134 return providedClass.isAssignableFrom(injectableClass);
135 }
136 }
137
138 return false;
139 }
140
141 @Nullable
142 private InjectionProvider findInjectablesByTypeOnly(@NonNull Type elementType, @NonNull TestedClass testedClass) {
143 GenericTypeReflection typeReflection = testedClass.reflection;
144 MultiValuedProvider found = null;
145
146 for (InjectionProvider injectable : injectables) {
147 Type injectableType = injectable.getDeclaredType();
148 Type elementTypeOfIterable = getElementTypeIfIterable(injectableType);
149
150 if (elementTypeOfIterable != null && typeReflection.areMatchingTypes(elementType, elementTypeOfIterable)) {
151 return injectable;
152 }
153
154 if (isAssignableToInjectionPoint(injectableType, testedClass)) {
155 if (found == null) {
156 found = new MultiValuedProvider(elementType);
157 }
158
159 found.addInjectable(injectable);
160 }
161 }
162
163 return found;
164 }
165
166 @Nullable
167 private InjectionProvider findInjectableByTypeAndOptionallyName(@NonNull String nameOfInjectionPoint,
168 @NonNull TestedClass testedClass) {
169 InjectionProvider foundInjectable = null;
170
171 for (InjectionProvider injectable : injectables) {
172 if (hasTypeAssignableToInjectionPoint(injectable, testedClass)) {
173 if (nameOfInjectionPoint.equals(injectable.getName())) {
174 return injectable;
175 }
176
177 if (foundInjectable == null) {
178 foundInjectable = injectable;
179 }
180 }
181 }
182
183 return foundInjectable;
184 }
185
186 @Nullable
187 InjectionProvider findInjectableByTypeAndName(@NonNull String nameOfInjectionPoint,
188 @NonNull TestedClass testedClass) {
189 for (InjectionProvider injectable : injectables) {
190 if (hasTypeAssignableToInjectionPoint(injectable, testedClass)
191 && nameOfInjectionPoint.equals(injectable.getName())) {
192 return injectable;
193 }
194 }
195
196 return null;
197 }
198
199 @Nullable
200 Object getValueToInject(@NonNull InjectionProvider injectionProvider, @Nullable Object currentTestClassInstance) {
201 if (consumedInjectionProviders.contains(injectionProvider)) {
202 return null;
203 }
204
205 Object value = injectionProvider.getValue(currentTestClassInstance);
206
207 if (value != null) {
208 consumedInjectionProviders.add(injectionProvider);
209 }
210
211 return value;
212 }
213
214 void resetConsumedInjectionProviders() {
215 consumedInjectionProviders.clear();
216 }
217
218 @NonNull
219 public List<InjectionProvider> saveConsumedInjectionProviders() {
220 List<InjectionProvider> previouslyConsumed = consumedInjectionProviders;
221 consumedInjectionProviders = new ArrayList<>();
222 return previouslyConsumed;
223 }
224
225 public void restoreConsumedInjectionProviders(@NonNull List<InjectionProvider> previouslyConsumed) {
226 consumedInjectionProviders = previouslyConsumed;
227 }
228 }