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