1 package net.florianschoppmann.java.reflect;
2
3 import net.florianschoppmann.java.type.AbstractTypes;
4 import net.florianschoppmann.java.type.AbstractTypesContract;
5 import net.florianschoppmann.java.type.AbstractTypesProvider;
6 import net.florianschoppmann.java.type.IntersectionType;
7 import org.testng.Assert;
8 import org.testng.annotations.Factory;
9 import org.testng.annotations.Test;
10
11 import javax.annotation.Nullable;
12 import javax.lang.model.element.ElementKind;
13 import javax.lang.model.element.ExecutableElement;
14 import javax.lang.model.element.PackageElement;
15 import javax.lang.model.element.TypeElement;
16 import javax.lang.model.element.TypeParameterElement;
17 import javax.lang.model.element.VariableElement;
18 import javax.lang.model.type.DeclaredType;
19 import javax.lang.model.type.NoType;
20 import javax.lang.model.type.NullType;
21 import javax.lang.model.type.PrimitiveType;
22 import javax.lang.model.type.TypeKind;
23 import javax.lang.model.type.TypeMirror;
24 import javax.lang.model.type.TypeVariable;
25 import javax.lang.model.type.WildcardType;
26 import javax.lang.model.util.AbstractElementVisitor7;
27 import java.io.Serializable;
28 import java.lang.annotation.Retention;
29 import java.lang.reflect.InvocationHandler;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.ParameterizedType;
32 import java.lang.reflect.Proxy;
33 import java.lang.reflect.Type;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Map;
37
38
39
40
41 public final class ReflectionTypesTest {
42 private ReflectionTypes types;
43
44 public void setup() {
45 types = ReflectionTypes.getInstance();
46 }
47
48 @Factory
49 public Object[] createTests() {
50 setup();
51
52 Provider provider = new Provider();
53 return new Object[] {
54 new AbstractTypesContract(provider)
55 };
56 }
57
58 private final class Provider implements AbstractTypesProvider {
59 @Override
60 public void preContract() { }
61
62 @Override
63 public AbstractTypes getTypes(Map<Class<?>, TypeElement> classTypeElementMap) {
64 for (Map.Entry<Class<?>, TypeElement> entry: classTypeElementMap.entrySet()) {
65 entry.setValue(types.typeElement(entry.getKey()));
66 }
67 return types;
68 }
69 }
70
71 enum ThrowingInvocationHandler implements InvocationHandler {
72 INSTANCE;
73
74 @Override
75 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
76 throw new UnsupportedOperationException();
77 }
78 }
79
80 @Test
81 public void requireValidElement() {
82 TypeElement typeElement = (TypeElement) Proxy.newProxyInstance(
83 getClass().getClassLoader(), new Class<?>[]{ TypeElement.class }, ThrowingInvocationHandler.INSTANCE);
84 try {
85 types.requireValidElement(typeElement);
86 Assert.fail("Expected exception.");
87 } catch (IllegalArgumentException exception) {
88 Assert.assertTrue(exception.getMessage().contains(ReflectionTypes.class.getName()));
89 }
90 }
91
92 @Test
93 public void requireValidType() {
94 TypeMirror typeMirror = (TypeMirror) Proxy.newProxyInstance(
95 getClass().getClassLoader(), new Class<?>[]{ TypeMirror.class }, ThrowingInvocationHandler.INSTANCE);
96 try {
97 types.requireValidType(typeMirror);
98 Assert.fail("Expected exception.");
99 } catch (IllegalArgumentException exception) {
100 Assert.assertTrue(exception.getMessage().contains(ReflectionTypes.class.getName()));
101 }
102 }
103
104 private static class ClassElementVisitor extends AbstractElementVisitor7<Class<?>, Void> {
105 private static final ClassElementVisitor INSTANCE = new ClassElementVisitor();
106
107 @Override
108 public Class<?> visitPackage(PackageElement e, @Nullable Void ignored) {
109 return PackageElement.class;
110 }
111
112 @Override
113 public Class<?> visitType(TypeElement e, @Nullable Void ignored) {
114 return TypeElement.class;
115 }
116
117 @Override
118 public Class<?> visitVariable(VariableElement e, @Nullable Void ignored) {
119 return VariableElement.class;
120 }
121
122 @Override
123 public Class<?> visitExecutable(ExecutableElement e, @Nullable Void ignored) {
124 return ExecutableElement.class;
125 }
126
127 @Override
128 public Class<?> visitTypeParameter(TypeParameterElement e, @Nullable Void ignored) {
129 return TypeParameterElement.class;
130 }
131 }
132
133 @Test
134 public void typeElement() {
135 TypeElement threadDeclaration = types.typeElement(Thread.class);
136 TypeElement threadStateDeclaration = types.typeElement(Thread.State.class);
137 Assert.assertEquals(
138 threadStateDeclaration.getQualifiedName().toString(),
139 Thread.State.class.getCanonicalName()
140 );
141 Assert.assertEquals(threadStateDeclaration.getKind(), ElementKind.ENUM);
142 Assert.assertEquals(threadStateDeclaration.getEnclosingElement(), types.typeElement(Thread.class));
143 Assert.assertEquals(threadStateDeclaration.toString(), Thread.State.class.toString());
144 Assert.assertEquals(threadStateDeclaration.accept(ClassElementVisitor.INSTANCE, null), TypeElement.class);
145
146 Assert.assertEquals(types.typeElement(Retention.class).getKind(), ElementKind.ANNOTATION_TYPE);
147
148 try {
149 threadDeclaration.getEnclosingElement();
150 Assert.fail("Expected exception.");
151 } catch (UnsupportedOperationException ignored) { }
152
153 try {
154 threadDeclaration.getModifiers();
155 Assert.fail("Expected exception.");
156 } catch (UnsupportedOperationException ignored) { }
157
158 try {
159 threadStateDeclaration.getNestingKind();
160 Assert.fail("Expected exception.");
161 } catch (UnsupportedOperationException ignored) { }
162
163 try {
164 types.typeElement(int.class);
165 Assert.fail("Expected exception");
166 } catch (IllegalArgumentException ignored) { }
167
168 try {
169 types.typeElement(Object[].class);
170 Assert.fail("Expected exception");
171 } catch (IllegalArgumentException ignored) { }
172 }
173
174 @Test
175 public void typeParameterElement() {
176 TypeParameterElement typeParameterElement = types.typeElement(List.class).getTypeParameters().get(0);
177 Assert.assertEquals(typeParameterElement.getKind(), ElementKind.TYPE_PARAMETER);
178 Assert.assertEquals(
179 typeParameterElement.accept(ClassElementVisitor.INSTANCE, null),
180 TypeParameterElement.class
181 );
182 Assert.assertEquals(typeParameterElement.toString(), List.class.getTypeParameters()[0].getName());
183
184 try {
185 typeParameterElement.getModifiers();
186 Assert.fail("Expected exception.");
187 } catch (UnsupportedOperationException ignored) { }
188 }
189
190 private static class OuterClass<T extends Serializable> {
191 class InnerClass<U extends Cloneable> { }
192 }
193
194 private static class TypeToken<T> {
195 private final Type javaType;
196
197 TypeToken() {
198 ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
199 javaType = genericSuperclass.getActualTypeArguments()[0];
200 }
201
202 Type getJavaType() {
203 return javaType;
204 }
205 }
206
207 public <T extends Number> void methodWithTypeParameter(T argument) { }
208
209 @Test
210 public void typeMirror() throws NoSuchMethodException {
211
212 Assert.assertEquals(types.typeMirror(int.class), types.getPrimitiveType(TypeKind.INT));
213
214
215 Assert.assertEquals(
216 types.typeMirror(Serializable[].class),
217 types.getArrayType(types.getDeclaredType(types.typeElement(Serializable.class)))
218 );
219
220
221 Assert.assertEquals(types.typeMirror(Integer.class), types.getDeclaredType(types.typeElement(Integer.class)));
222
223
224 DeclaredType javaTypesTestType = types.getDeclaredType(types.typeElement(ReflectionTypesTest.class));
225 DeclaredType rawOuterClassType = types.getDeclaredType(javaTypesTestType, types.typeElement(OuterClass.class));
226 Assert.assertEquals(
227 types.typeMirror(OuterClass.InnerClass.class),
228 types.getDeclaredType(rawOuterClassType, types.typeElement(OuterClass.InnerClass.class))
229 );
230
231
232 DeclaredType actualInnerClassType = (DeclaredType) types.typeMirror(
233 new TypeToken<OuterClass<Integer>.InnerClass<byte[]>>() { }.getJavaType()
234 );
235 DeclaredType outerClassType = types.getDeclaredType(
236 javaTypesTestType,
237 types.typeElement(OuterClass.class),
238 types.typeMirror(Integer.class)
239 );
240 DeclaredType innerClassType = types.getDeclaredType(
241 outerClassType,
242 types.typeElement(OuterClass.InnerClass.class),
243 types.typeMirror(byte[].class)
244 );
245 Assert.assertEquals(actualInnerClassType, innerClassType);
246
247
248 DeclaredType listOfStringType
249 = types.getDeclaredType(types.typeElement(List.class), types.typeMirror(String.class));
250 Assert.assertEquals(
251 types.typeMirror(new TypeToken<List<String>[]>() { }.getJavaType()),
252 types.getArrayType(listOfStringType)
253 );
254
255
256 WildcardType actualExtendsStringType = (WildcardType) types.typeMirror(
257 ((ParameterizedType) new TypeToken<List<? extends String>>() { }.getJavaType()).getActualTypeArguments()[0]
258 );
259 WildcardType extendsStringType = types.getWildcardType(types.typeMirror(String.class), null);
260 Assert.assertEquals(actualExtendsStringType, extendsStringType);
261
262 WildcardType actualSuperStringType = (WildcardType) types.typeMirror(
263 ((ParameterizedType) new TypeToken<List<? super String>>() { }.getJavaType()).getActualTypeArguments()[0]
264 );
265 WildcardType superStringType = types.getWildcardType(null, types.typeMirror(String.class));
266 Assert.assertEquals(actualSuperStringType, superStringType);
267
268 WildcardType actualWildcardType = (WildcardType) types.typeMirror(
269 ((ParameterizedType) new TypeToken<List<?>>() { }.getJavaType()).getActualTypeArguments()[0]
270 );
271 WildcardType wildcardType = types.getWildcardType(null, null);
272 Assert.assertEquals(actualWildcardType, wildcardType);
273
274
275 TypeVariable actualTypeVariable = (TypeVariable) types.typeMirror(
276 ((ParameterizedType) List.class.getGenericInterfaces()[0]).getActualTypeArguments()[0]
277 );
278 Assert.assertEquals(actualTypeVariable, types.typeElement(List.class).getTypeParameters().get(0).asType());
279
280 try {
281 types.typeMirror(getClass().getMethod("methodWithTypeParameter", Number.class).getTypeParameters()[0]);
282 Assert.fail("Expected exception.");
283 } catch (UnsupportedOperationException ignored) { }
284 }
285
286 @Test
287 public void testToString() {
288
289 for (TypeKind primitive: Arrays.asList(TypeKind.DOUBLE, TypeKind.FLOAT, TypeKind.LONG, TypeKind.INT,
290 TypeKind.SHORT, TypeKind.BYTE, TypeKind.CHAR, TypeKind.BOOLEAN)) {
291 PrimitiveType primitiveType = types.getPrimitiveType(primitive);
292 Assert.assertEquals(types.getPrimitiveType(primitive).toString(), types.toString(primitiveType));
293 }
294
295
296 DeclaredType declaredType = (DeclaredType) types.typeMirror(Integer.class);
297 Assert.assertEquals(declaredType.toString(), types.toString(declaredType));
298
299
300 IntersectionType intersectionType
301 = types.getIntersectionType(types.typeMirror(Serializable.class), types.typeMirror(Cloneable.class));
302 Assert.assertEquals(intersectionType.toString(), types.toString(intersectionType));
303
304
305 WildcardType wildcardType = types.getWildcardType(types.typeMirror(Integer.class), null);
306 Assert.assertEquals(wildcardType.toString(), types.toString(wildcardType));
307
308
309 NullType nullType = types.getNullType();
310 Assert.assertEquals(nullType.toString(), types.toString(nullType));
311
312
313 NoType noType = types.getNoType(TypeKind.VOID);
314 Assert.assertEquals(noType.toString(), types.toString(noType));
315
316
317 TypeVariable typeVariable = (TypeVariable) types.typeElement(List.class).getTypeParameters().get(0).asType();
318 Assert.assertEquals(typeVariable.toString(), types.toString(typeVariable));
319 }
320
321 @Test
322 public void requireCondition() {
323 ReflectionTypes.requireCondition(true, "Message: %s", 24);
324 try {
325 ReflectionTypes.requireCondition(false, "Message: %s", 24);
326 Assert.fail("Expected exception.");
327 } catch (IllegalStateException exception) {
328 Assert.assertEquals(exception.getMessage(), "Message: 24");
329 }
330 }
331
332 @Test(expectedExceptions = UnsupportedOperationException.class)
333 public void testIsAssignable() {
334 types.isAssignable(types.getPrimitiveType(TypeKind.INT), types.getPrimitiveType(TypeKind.LONG));
335 }
336
337 @Test(expectedExceptions = UnsupportedOperationException.class)
338 public void directSupertypes() {
339 types.directSupertypes(types.getPrimitiveType(TypeKind.INT));
340 }
341
342 @Test(expectedExceptions = UnsupportedOperationException.class)
343 public void asMemberOf() {
344 TypeElement listDeclaration = types.typeElement(List.class);
345 types.asMemberOf(
346 types.getDeclaredType(listDeclaration, types.typeMirror(String.class)),
347 listDeclaration.getTypeParameters().get(0)
348 );
349 }
350 }