1
2
3
4
5
6 package mockit.internal.injection;
7
8 import static mockit.internal.injection.InjectionPoint.JAKARTA_INJECT_CLASS;
9 import static mockit.internal.injection.InjectionPoint.JAVAX_INJECT_CLASS;
10 import static mockit.internal.util.Utilities.getClassType;
11
12 import edu.umd.cs.findbugs.annotations.NonNull;
13 import edu.umd.cs.findbugs.annotations.Nullable;
14
15 import java.lang.reflect.ParameterizedType;
16 import java.lang.reflect.Type;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
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 || (JAKARTA_INJECT_CLASS != null
125 && jakarta.inject.Provider.class.isAssignableFrom(classOfInjectionPoint)
126 || JAVAX_INJECT_CLASS != null
127 && javax.inject.Provider.class.isAssignableFrom(classOfInjectionPoint))) {
128 Type providedType = parameterizedType.getActualTypeArguments()[0];
129
130 if (providedType.equals(injectableType)) {
131 return true;
132 }
133
134 Class<?> injectableClass = getClassType(injectableType);
135 Class<?> providedClass = getClassType(providedType);
136
137 return providedClass.isAssignableFrom(injectableClass);
138 }
139 }
140
141 return false;
142 }
143
144 @Nullable
145 private InjectionProvider findInjectablesByTypeOnly(@NonNull Type elementType, @NonNull TestedClass testedClass) {
146 GenericTypeReflection typeReflection = testedClass.reflection;
147 MultiValuedProvider found = null;
148
149 for (InjectionProvider injectable : injectables) {
150 Type injectableType = injectable.getDeclaredType();
151 Type elementTypeOfIterable = getElementTypeIfIterable(injectableType);
152
153 if (elementTypeOfIterable != null && typeReflection.areMatchingTypes(elementType, elementTypeOfIterable)) {
154 return injectable;
155 }
156
157 if (isAssignableToInjectionPoint(injectableType, testedClass)) {
158 if (found == null) {
159 found = new MultiValuedProvider(elementType);
160 }
161
162 found.addInjectable(injectable);
163 }
164 }
165
166 return found;
167 }
168
169 @Nullable
170 private InjectionProvider findInjectableByTypeAndOptionallyName(@NonNull String nameOfInjectionPoint,
171 @NonNull TestedClass testedClass) {
172 InjectionProvider foundInjectable = null;
173
174 for (InjectionProvider injectable : injectables) {
175 if (hasTypeAssignableToInjectionPoint(injectable, testedClass)) {
176 if (nameOfInjectionPoint.equals(injectable.getName())) {
177 return injectable;
178 }
179
180 if (foundInjectable == null) {
181 foundInjectable = injectable;
182 }
183 }
184 }
185
186 return foundInjectable;
187 }
188
189 @Nullable
190 InjectionProvider findInjectableByTypeAndName(@NonNull String nameOfInjectionPoint,
191 @NonNull TestedClass testedClass) {
192 for (InjectionProvider injectable : injectables) {
193 if (hasTypeAssignableToInjectionPoint(injectable, testedClass)
194 && nameOfInjectionPoint.equals(injectable.getName())) {
195 return injectable;
196 }
197 }
198
199 return null;
200 }
201
202 @Nullable
203 Object getValueToInject(@NonNull InjectionProvider injectionProvider, @Nullable Object currentTestClassInstance) {
204 if (consumedInjectionProviders.contains(injectionProvider)) {
205 return null;
206 }
207
208 Object value = injectionProvider.getValue(currentTestClassInstance);
209
210 if (value != null) {
211 consumedInjectionProviders.add(injectionProvider);
212 }
213
214 return value;
215 }
216
217 void resetConsumedInjectionProviders() {
218 consumedInjectionProviders.clear();
219 }
220
221 @NonNull
222 public List<InjectionProvider> saveConsumedInjectionProviders() {
223 List<InjectionProvider> previouslyConsumed = consumedInjectionProviders;
224 consumedInjectionProviders = new ArrayList<>();
225 return previouslyConsumed;
226 }
227
228 public void restoreConsumedInjectionProviders(@NonNull List<InjectionProvider> previouslyConsumed) {
229 consumedInjectionProviders = previouslyConsumed;
230 }
231 }