View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
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 }