View Javadoc
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   * Unit tests for {@link ReflectionTypes}.
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         // Class<?> -> PrimitiveType
212         Assert.assertEquals(types.typeMirror(int.class), types.getPrimitiveType(TypeKind.INT));
213 
214         // Class<?> -> ArrayType
215         Assert.assertEquals(
216             types.typeMirror(Serializable[].class),
217             types.getArrayType(types.getDeclaredType(types.typeElement(Serializable.class)))
218         );
219 
220         // Class<?> -> DeclaredType (non-generic)
221         Assert.assertEquals(types.typeMirror(Integer.class), types.getDeclaredType(types.typeElement(Integer.class)));
222 
223         // Class<?> -> DeclaredType (raw)
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         // ParameterizedType -> DeclaredType
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         // GenericArrayType -> ArrayType
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         // WildcardType -> WildcardType
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         // TypeVariable -> TypeVariable
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         // PrimitiveType
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         // DeclaredType
296         DeclaredType declaredType = (DeclaredType) types.typeMirror(Integer.class);
297         Assert.assertEquals(declaredType.toString(), types.toString(declaredType));
298 
299         // IntersectionType
300         IntersectionType intersectionType
301             = types.getIntersectionType(types.typeMirror(Serializable.class), types.typeMirror(Cloneable.class));
302         Assert.assertEquals(intersectionType.toString(), types.toString(intersectionType));
303 
304         // WildcardType
305         WildcardType wildcardType = types.getWildcardType(types.typeMirror(Integer.class), null);
306         Assert.assertEquals(wildcardType.toString(), types.toString(wildcardType));
307 
308         // NullType
309         NullType nullType = types.getNullType();
310         Assert.assertEquals(nullType.toString(), types.toString(nullType));
311 
312         // NoType
313         NoType noType = types.getNoType(TypeKind.VOID);
314         Assert.assertEquals(noType.toString(), types.toString(noType));
315 
316         // TypeVariable
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 }