1
2
3
4
5 package mockit.internal.expectations.mocking;
6
7 import static java.lang.reflect.Modifier.isFinal;
8
9 import edu.umd.cs.findbugs.annotations.NonNull;
10 import edu.umd.cs.findbugs.annotations.Nullable;
11
12 import java.lang.annotation.Annotation;
13 import java.lang.reflect.Field;
14 import java.lang.reflect.ParameterizedType;
15 import java.lang.reflect.Type;
16 import java.lang.reflect.TypeVariable;
17
18 import mockit.Capturing;
19 import mockit.Injectable;
20 import mockit.Mocked;
21 import mockit.internal.expectations.MockingFilters;
22 import mockit.internal.expectations.state.CascadingTypes;
23 import mockit.internal.expectations.state.ExecutingTest;
24 import mockit.internal.injection.InjectionProvider;
25 import mockit.internal.reflection.FieldReflection;
26 import mockit.internal.state.ParameterNames;
27 import mockit.internal.state.TestRun;
28 import mockit.internal.util.DefaultValues;
29 import mockit.internal.util.TestMethod;
30 import mockit.internal.util.TypeConversion;
31 import mockit.internal.util.Utilities;
32
33 import org.checkerframework.checker.index.qual.NonNegative;
34
35 @SuppressWarnings("EqualsAndHashcode")
36 public final class MockedType extends InjectionProvider {
37 @Mocked
38 private static final Object DUMMY = null;
39 private static final int DUMMY_HASHCODE;
40
41 static {
42 int h = 0;
43
44 try {
45 Field dummyField = MockedType.class.getDeclaredField("DUMMY");
46 Mocked mocked = dummyField.getAnnotation(Mocked.class);
47 h = mocked.hashCode();
48 } catch (NoSuchFieldException ignore) {
49 }
50
51 DUMMY_HASHCODE = h;
52 }
53
54 @Nullable
55 public final Field field;
56 final boolean fieldFromTestClass;
57 private final int accessModifiers;
58 @Nullable
59 private final Mocked mocked;
60 @Nullable
61 private final Capturing capturing;
62 @Nullable
63 private final Class<?> parameterImplementationClass;
64 public final boolean injectable;
65 @Nullable
66 Object providedValue;
67
68 public MockedType(@NonNull Field field) {
69 super(field.getGenericType(), field.getName());
70 this.field = field;
71 fieldFromTestClass = true;
72 accessModifiers = field.getModifiers();
73 mocked = field.getAnnotation(Mocked.class);
74 capturing = field.getAnnotation(Capturing.class);
75 parameterImplementationClass = null;
76 Injectable injectableAnnotation = field.getAnnotation(Injectable.class);
77 injectable = injectableAnnotation != null;
78 providedValue = getProvidedInjectableValue(injectableAnnotation);
79 registerCascadingAsNeeded();
80 }
81
82 @Nullable
83 private Object getProvidedInjectableValue(@Nullable Injectable annotation) {
84 if (annotation != null) {
85 String value = annotation.value();
86
87 if (!value.isEmpty()) {
88 Class<?> injectableClass = getClassType();
89
90 if (injectableClass != TypeVariable.class) {
91 return TypeConversion.convertFromString(injectableClass, value);
92 }
93 }
94 }
95
96 return null;
97 }
98
99 private void registerCascadingAsNeeded() {
100 if (isMockableType()) {
101 Type mockedType = declaredType;
102
103 if (!(mockedType instanceof TypeVariable<?>)) {
104 ExecutingTest executingTest = TestRun.getExecutingTest();
105 CascadingTypes types = executingTest.getCascadingTypes();
106 types.add(fieldFromTestClass, mockedType);
107 }
108 }
109 }
110
111 MockedType(@NonNull TestMethod testMethod, @NonNegative int paramIndex, @NonNull Type parameterType,
112 @NonNull Annotation[] annotationsOnParameter, @Nullable Class<?> parameterImplementationClass) {
113 super(parameterType, ParameterNames.getName(testMethod, paramIndex));
114 field = null;
115 fieldFromTestClass = false;
116 accessModifiers = 0;
117 mocked = getAnnotation(annotationsOnParameter, Mocked.class);
118 capturing = getAnnotation(annotationsOnParameter, Capturing.class);
119 this.parameterImplementationClass = parameterImplementationClass;
120 Injectable injectableAnnotation = getAnnotation(annotationsOnParameter, Injectable.class);
121 injectable = injectableAnnotation != null;
122 providedValue = getProvidedInjectableValue(injectableAnnotation);
123
124 if (providedValue == null && parameterType instanceof Class<?>) {
125 Class<?> parameterClass = (Class<?>) parameterType;
126
127 if (parameterClass.isPrimitive()) {
128 providedValue = DefaultValues.defaultValueForPrimitiveType(parameterClass);
129 }
130 }
131
132 registerCascadingAsNeeded();
133 }
134
135 @Nullable
136 private static <A extends Annotation> A getAnnotation(@NonNull Annotation[] annotations,
137 @NonNull Class<A> annotation) {
138 for (Annotation paramAnnotation : annotations) {
139 if (paramAnnotation.annotationType() == annotation) {
140
141 return (A) paramAnnotation;
142 }
143 }
144
145 return null;
146 }
147
148 MockedType(@NonNull String cascadingMethodName, @NonNull Type cascadedType) {
149 super(cascadedType, cascadingMethodName);
150 field = null;
151 fieldFromTestClass = false;
152 accessModifiers = 0;
153 mocked = null;
154 capturing = null;
155 injectable = true;
156 parameterImplementationClass = null;
157 }
158
159 @NonNull
160 @Override
161 public Class<?> getClassOfDeclaredType() {
162 return getClassType();
163 }
164
165
166
167
168
169
170 @NonNull
171 public Class<?> getClassType() {
172 if (parameterImplementationClass != null) {
173 return parameterImplementationClass;
174 }
175
176 Type mockedType = declaredType;
177
178 if (mockedType instanceof Class<?>) {
179 return (Class<?>) mockedType;
180 }
181
182 if (mockedType instanceof ParameterizedType) {
183 ParameterizedType parameterizedType = (ParameterizedType) mockedType;
184 return (Class<?>) parameterizedType.getRawType();
185 }
186
187
188
189 return TypeVariable.class;
190 }
191
192 boolean isMockableType() {
193 if (mocked == null && !injectable && capturing == null) {
194 return false;
195 }
196
197 Class<?> classType = Utilities.getClassType(declaredType);
198
199 if (isUnmockableJREType(classType)) {
200 return false;
201 }
202
203 MockingFilters.validateAsMockable(classType);
204
205 if (injectable) {
206 return !isJREValueType(classType) && !classType.isEnum();
207 }
208
209 return true;
210 }
211
212 private static boolean isUnmockableJREType(@NonNull Class<?> type) {
213 return type.isPrimitive() || type.isArray() || type == Integer.class || type == String.class;
214 }
215
216 private static boolean isJREValueType(@NonNull Class<?> type) {
217 return type == String.class || type == Boolean.class || type == Character.class
218 || Number.class.isAssignableFrom(type);
219 }
220
221 boolean isFinalFieldOrParameter() {
222 return field == null || isFinal(accessModifiers);
223 }
224
225 boolean isClassInitializationToBeStubbedOut() {
226 return mocked != null && mocked.stubOutClassInitialization();
227 }
228
229 boolean withInstancesToCapture() {
230 return getMaxInstancesToCapture() > 0;
231 }
232
233 public int getMaxInstancesToCapture() {
234 return capturing == null ? 0 : capturing.maxInstances();
235 }
236
237 @Nullable
238 @Override
239 public Object getValue(@Nullable Object owner) {
240 if (field == null) {
241 return providedValue;
242 }
243
244 Object value = FieldReflection.getFieldValue(field, owner);
245
246 if (!injectable) {
247 return value;
248 }
249
250 Class<?> fieldType = field.getType();
251
252 if (value == null) {
253 if (providedValue != null) {
254 return providedValue;
255 }
256
257 if (isFinalFieldOrParameter()) {
258 return NULL;
259 }
260
261 if (fieldType == String.class) {
262 return "";
263 }
264
265 return null;
266 }
267
268 if (providedValue == null || !fieldType.isPrimitive()) {
269 return value;
270 }
271
272 Object defaultValue = DefaultValues.defaultValueForPrimitiveType(fieldType);
273
274 return value.equals(defaultValue) ? providedValue : value;
275 }
276
277 @Override
278 public int hashCode() {
279 int result = declaredType.hashCode();
280
281 if (isFinal(accessModifiers)) {
282 result *= 31;
283 }
284
285 if (injectable) {
286 result *= 37;
287 }
288
289 if (mocked != null) {
290 int h = mocked.hashCode();
291
292 if (h != DUMMY_HASHCODE) {
293 result = 31 * result + h;
294 }
295 }
296
297 return result;
298 }
299 }