1
2
3
4
5 package mockit.internal.injection;
6
7 import static mockit.internal.injection.InjectionPoint.getQualifiedName;
8
9 import edu.umd.cs.findbugs.annotations.NonNull;
10 import edu.umd.cs.findbugs.annotations.Nullable;
11
12 import java.lang.reflect.Field;
13 import java.lang.reflect.Type;
14 import java.util.LinkedHashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.concurrent.ConcurrentHashMap;
19
20 import mockit.internal.reflection.FieldReflection;
21 import mockit.internal.reflection.GenericTypeReflection;
22
23
24
25
26 public final class InjectionState {
27 @NonNull
28 private static final Map<InjectionPoint, Object> globalObjects = new ConcurrentHashMap<>(2);
29
30 @NonNull
31 private final Map<InjectionPoint, Object> testedObjects;
32 @NonNull
33 private final Map<InjectionPoint, Object> instantiatedDependencies;
34 @NonNull
35 public final InjectionProviders injectionProviders;
36 @NonNull
37 public final LifecycleMethods lifecycleMethods;
38 @NonNull
39 final InterfaceResolution interfaceResolution;
40 @Nullable
41 private BeanExporter beanExporter;
42 private Object currentTestClassInstance;
43
44 InjectionState() {
45 testedObjects = new LinkedHashMap<>();
46 instantiatedDependencies = new LinkedHashMap<>();
47 lifecycleMethods = new LifecycleMethods();
48 injectionProviders = new InjectionProviders(lifecycleMethods);
49 interfaceResolution = new InterfaceResolution();
50 }
51
52 void setInjectables(@NonNull Object testClassInstance, @NonNull List<? extends InjectionProvider> injectables) {
53 currentTestClassInstance = testClassInstance;
54 injectionProviders.setInjectables(injectables);
55 lifecycleMethods.getServletConfigForInitMethodsIfAny(injectables, testClassInstance);
56 }
57
58 void addInjectables(@NonNull Object testClassInstance,
59 @NonNull List<? extends InjectionProvider> injectablesToAdd) {
60 currentTestClassInstance = testClassInstance;
61 List<InjectionProvider> injectables = injectionProviders.addInjectables(injectablesToAdd);
62 lifecycleMethods.getServletConfigForInitMethodsIfAny(injectables, testClassInstance);
63 }
64
65 Object getCurrentTestClassInstance() {
66 return currentTestClassInstance;
67 }
68
69 @Nullable
70 public Object getValueToInject(@NonNull InjectionProvider injectionProvider) {
71 return injectionProviders.getValueToInject(injectionProvider, currentTestClassInstance);
72 }
73
74 void saveTestedObject(@NonNull InjectionPoint key, @NonNull Object testedObject, boolean global) {
75 Map<InjectionPoint, Object> objects = global ? globalObjects : testedObjects;
76 objects.put(key, testedObject);
77 }
78
79 @Nullable
80 Object getTestedInstance(@NonNull InjectionPoint injectionPoint, boolean global) {
81 Object testedInstance = instantiatedDependencies.isEmpty() ? null
82 : findPreviouslyInstantiatedDependency(injectionPoint);
83
84 if (testedInstance == null) {
85 testedInstance = testedObjects.isEmpty() ? null : getValueFromExistingTestedObject(injectionPoint);
86 }
87
88 if (testedInstance == null && global) {
89 testedInstance = globalObjects.get(injectionPoint);
90 }
91
92 return testedInstance;
93 }
94
95 @Nullable
96 private Object findPreviouslyInstantiatedDependency(@NonNull InjectionPoint injectionPoint) {
97 Object dependency = instantiatedDependencies.get(injectionPoint);
98
99 if (dependency == null) {
100 InjectionPoint injectionPointWithTypeOnly = new InjectionPoint(injectionPoint.type);
101 dependency = instantiatedDependencies.get(injectionPointWithTypeOnly);
102
103 if (dependency == null) {
104 dependency = findMatchingObject(instantiatedDependencies, null, injectionPointWithTypeOnly);
105 }
106 }
107
108 return dependency;
109 }
110
111 @Nullable
112 private Object getValueFromExistingTestedObject(@NonNull InjectionPoint injectionPoint) {
113 for (Object testedObject : testedObjects.values()) {
114 Object fieldValue = getValueFromFieldOfEquivalentTypeAndName(injectionPoint, testedObject);
115
116 if (fieldValue != null) {
117 return fieldValue;
118 }
119 }
120
121 return null;
122 }
123
124 @Nullable
125 private static Object getValueFromFieldOfEquivalentTypeAndName(@NonNull InjectionPoint injectionPoint,
126 @NonNull Object testedObject) {
127 for (Field internalField : testedObject.getClass().getDeclaredFields()) {
128 Type fieldType = internalField.getGenericType();
129 String qualifiedName = getQualifiedName(internalField.getDeclaredAnnotations());
130 boolean qualified = qualifiedName != null;
131 String fieldName = qualified ? qualifiedName : internalField.getName();
132 InjectionPoint internalInjectionPoint = new InjectionPoint(fieldType, fieldName, qualified);
133
134 if (internalInjectionPoint.equals(injectionPoint)) {
135 return FieldReflection.getFieldValue(internalField, testedObject);
136 }
137 }
138
139 return null;
140 }
141
142 @Nullable
143 @SuppressWarnings("unchecked")
144 public static <D> D getGlobalDependency(@NonNull InjectionPoint key) {
145 return (D) globalObjects.get(key);
146 }
147
148 @Nullable
149 public Object getTestedValue(@NonNull TestedClass testedClass, @NonNull InjectionPoint injectionPoint) {
150 Object testedValue = testedObjects.get(injectionPoint);
151
152 if (testedValue == null) {
153 testedValue = findMatchingObject(testedObjects, testedClass, injectionPoint);
154 }
155
156 return testedValue;
157 }
158
159 @Nullable
160 public Object getInstantiatedDependency(@Nullable TestedClass testedClass, @NonNull InjectionPoint dependencyKey) {
161 Object dependency = testedObjects.get(dependencyKey);
162
163 if (dependency == null) {
164 dependency = findMatchingObject(testedObjects, testedClass, dependencyKey);
165
166 if (dependency == null) {
167 dependency = instantiatedDependencies.get(dependencyKey);
168
169 if (dependency == null) {
170 dependency = findMatchingObject(instantiatedDependencies, testedClass, dependencyKey);
171
172 if (dependency == null) {
173 dependency = findMatchingObject(globalObjects, testedClass, dependencyKey);
174 }
175 }
176 }
177 }
178
179 return dependency;
180 }
181
182 @Nullable
183 private static Object findMatchingObject(@NonNull Map<InjectionPoint, Object> availableObjects,
184 @Nullable TestedClass testedClass, @NonNull InjectionPoint injectionPoint) {
185 if (availableObjects.isEmpty()) {
186 return null;
187 }
188
189 GenericTypeReflection reflection = testedClass == null ? null : testedClass.reflection;
190 Type dependencyType = injectionPoint.type;
191 Object found = null;
192
193 for (Entry<InjectionPoint, Object> injectionPointAndObject : availableObjects.entrySet()) {
194 InjectionPoint dependencyIP = injectionPointAndObject.getKey();
195 Object dependencyObject = injectionPointAndObject.getValue();
196
197 if (injectionPoint.equals(dependencyIP)) {
198 return dependencyObject;
199 }
200
201 if (reflection != null) {
202 if (!reflection.areMatchingTypes(dependencyType, dependencyIP.type)) {
203 continue;
204 }
205 found = dependencyObject;
206 }
207
208 if (injectionPoint.hasSameName(dependencyIP)) {
209 return dependencyObject;
210 }
211 }
212
213 return injectionPoint.qualified ? null : found;
214 }
215
216 public void saveInstantiatedDependency(@NonNull InjectionPoint dependencyKey, @NonNull Object dependency) {
217 instantiatedDependencies.put(dependencyKey, dependency);
218 }
219
220 public static void saveGlobalDependency(@NonNull InjectionPoint dependencyKey, @NonNull Object dependency) {
221 globalObjects.put(dependencyKey, dependency);
222 }
223
224 void clearTestedObjectsAndInstantiatedDependencies() {
225 testedObjects.clear();
226 instantiatedDependencies.clear();
227 }
228
229 @NonNull
230 BeanExporter getBeanExporter() {
231 if (beanExporter == null) {
232 beanExporter = new BeanExporter(this);
233 }
234
235 return beanExporter;
236 }
237
238 @Nullable
239 public Class<?> resolveInterface(@NonNull Class<?> anInterface) {
240 return interfaceResolution.resolveInterface(anInterface, currentTestClassInstance);
241 }
242 }