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