1
2
3
4
5 package mockit.internal.injection;
6
7 import static mockit.internal.util.AutoBoxing.isWrapperOfPrimitiveType;
8 import static mockit.internal.util.DefaultValues.defaultValueForPrimitiveType;
9
10 import edu.umd.cs.findbugs.annotations.NonNull;
11 import edu.umd.cs.findbugs.annotations.Nullable;
12
13 import java.lang.annotation.Annotation;
14 import java.lang.reflect.Field;
15 import java.lang.reflect.Type;
16 import java.util.List;
17
18 import mockit.Tested;
19 import mockit.internal.injection.field.FieldInjection;
20 import mockit.internal.injection.full.FullInjection;
21
22 abstract class TestedObject {
23 @NonNull
24 private final InjectionState injectionState;
25 @NonNull
26 final Tested metadata;
27 @NonNull
28 private final String testedName;
29 @Nullable
30 private final FullInjection fullInjection;
31 @NonNull
32 private final TestedClass testedClass;
33 @Nullable
34 private final TestedObjectCreation testedObjectCreation;
35 @Nullable
36 private List<Field> targetFields;
37 boolean createAutomatically;
38
39 @Nullable
40 static Tested getTestedAnnotationIfPresent(@NonNull Annotation annotation) {
41 if (annotation instanceof Tested) {
42 return (Tested) annotation;
43 }
44
45 return annotation.annotationType().getAnnotation(Tested.class);
46 }
47
48 TestedObject(@NonNull InjectionState injectionState, @NonNull Tested metadata, @NonNull Class<?> testClass,
49 @NonNull String testedName, @NonNull Type testedType, @NonNull Class<?> testedClass) {
50 this.injectionState = injectionState;
51 this.metadata = metadata;
52 this.testedName = testedName;
53 fullInjection = metadata.fullyInitialized() ? new FullInjection(injectionState, testedClass, testedName) : null;
54
55 if (testedClass.isInterface() || testedClass.isEnum() || testedClass.isPrimitive() || testedClass.isArray()) {
56 testedObjectCreation = null;
57 this.testedClass = new TestedClass(testedType, testedClass);
58 } else {
59 testedObjectCreation = new TestedObjectCreation(injectionState, fullInjection, testedType, testedClass);
60 this.testedClass = testedObjectCreation.testedClass;
61 injectionState.lifecycleMethods.findLifecycleMethods(testedClass);
62 }
63
64 this.testedClass.testClass = testClass;
65 }
66
67 boolean isAvailableDuringSetup() {
68 return metadata.availableDuringSetup();
69 }
70
71 void instantiateWithInjectableValues(@NonNull Object testClassInstance) {
72 if (alreadyInstantiated(testClassInstance)) {
73 return;
74 }
75
76 Object testedObject = getExistingTestedInstanceIfApplicable(testClassInstance);
77 Class<?> testedObjectClass = testedClass.targetClass;
78 InjectionPoint injectionPoint = new InjectionPoint(testedClass.declaredType, testedName);
79
80 if (isNonInstantiableType(testedObjectClass, testedObject)) {
81 reusePreviouslyCreatedInstance(testClassInstance, injectionPoint);
82 return;
83 }
84
85 if (testedObject == null && createAutomatically) {
86 if (reusePreviouslyCreatedInstance(testClassInstance, injectionPoint)) {
87 return;
88 }
89
90 testedObject = createAndRegisterNewObject(testClassInstance, injectionPoint);
91 } else if (testedObject != null) {
92 registerTestedObject(injectionPoint, testedObject);
93 testedObjectClass = testedObject.getClass();
94 }
95
96 if (testedObject != null && testedObjectClass.getClassLoader() != null) {
97 performFieldInjection(testedObjectClass, testedObject);
98
99 if (createAutomatically) {
100 injectionState.lifecycleMethods.executeInitializationMethodsIfAny(testedObjectClass, testedObject);
101 }
102 }
103 }
104
105 boolean alreadyInstantiated(@NonNull Object testClassInstance) {
106 return false;
107 }
108
109 @Nullable
110 abstract Object getExistingTestedInstanceIfApplicable(@NonNull Object testClassInstance);
111
112 static boolean isNonInstantiableType(@NonNull Class<?> targetClass, @Nullable Object currentValue) {
113 return targetClass.isPrimitive() && defaultValueForPrimitiveType(targetClass).equals(currentValue)
114 || currentValue == null && (targetClass.isArray() || targetClass.isEnum() || targetClass.isAnnotation()
115 || isWrapperOfPrimitiveType(targetClass));
116 }
117
118 private boolean reusePreviouslyCreatedInstance(@NonNull Object testClassInstance,
119 @NonNull InjectionPoint injectionPoint) {
120 Object previousInstance = injectionState.getTestedInstance(injectionPoint, metadata.global());
121
122 if (previousInstance != null) {
123 setInstance(testClassInstance, previousInstance);
124 return true;
125 }
126
127 return false;
128 }
129
130 abstract void setInstance(@NonNull Object testClassInstance, @Nullable Object testedInstance);
131
132 @Nullable
133 private Object createAndRegisterNewObject(@NonNull Object testClassInstance,
134 @NonNull InjectionPoint injectionPoint) {
135 Object testedInstance = null;
136
137 if (testedObjectCreation != null) {
138 testedInstance = testedObjectCreation.create(false, true);
139
140 if (testedInstance != null) {
141 setInstance(testClassInstance, testedInstance);
142 registerTestedObject(injectionPoint, testedInstance);
143 }
144 }
145
146 return testedInstance;
147 }
148
149 private void registerTestedObject(@NonNull InjectionPoint injectionPoint, @NonNull Object testedObject) {
150 injectionState.saveTestedObject(injectionPoint, testedObject, metadata.global());
151 }
152
153 private void performFieldInjection(@NonNull Class<?> targetClass, @NonNull Object testedObject) {
154 FieldInjection fieldInjection = new FieldInjection(injectionState, fullInjection);
155
156 if (targetFields == null) {
157 targetFields = Injector.findAllTargetInstanceFieldsInTestedClassHierarchy(targetClass, testedClass);
158 }
159
160 fieldInjection.injectIntoEligibleFields(targetFields, testedObject, testedClass);
161 }
162
163 void clearIfAutomaticCreation(@NonNull Object testClassInstance, boolean duringTearDown) {
164 if (createAutomatically && (duringTearDown || !isAvailableDuringSetup())) {
165 setInstance(testClassInstance, null);
166
167 if (fullInjection != null) {
168 fullInjection.clear();
169 }
170 }
171 }
172 }