View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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 }