View Javadoc
1   package net.florianschoppmann.java.reflect;
2   
3   import net.florianschoppmann.java.type.AbstractTypes;
4   import net.florianschoppmann.java.type.IntersectionType;
5   
6   import javax.annotation.Nullable;
7   import javax.lang.model.element.Element;
8   import javax.lang.model.element.Name;
9   import javax.lang.model.element.TypeElement;
10  import javax.lang.model.element.TypeParameterElement;
11  import javax.lang.model.type.ArrayType;
12  import javax.lang.model.type.DeclaredType;
13  import javax.lang.model.type.ExecutableType;
14  import javax.lang.model.type.NoType;
15  import javax.lang.model.type.NullType;
16  import javax.lang.model.type.PrimitiveType;
17  import javax.lang.model.type.TypeKind;
18  import javax.lang.model.type.TypeMirror;
19  import java.lang.reflect.GenericArrayType;
20  import java.lang.reflect.GenericDeclaration;
21  import java.lang.reflect.ParameterizedType;
22  import java.lang.reflect.Type;
23  import java.lang.reflect.TypeVariable;
24  import java.lang.reflect.WildcardType;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Objects;
32  
33  /**
34   * Implementation of {@link javax.lang.model.util.Types} backed by the Java Reflection API.
35   *
36   * <p>All {@link Element} and {@link TypeMirror} objects returned by this class are immutable and therefore thread-safe.
37   * Likewise, only a stateless (and thus thread-safe) singleton instance of this class is available via
38   * {@link #getInstance()}.
39   *
40   * <p>Currently unsupported are (resulting in an {@link UnsupportedOperationException}):
41   * <ul><li>
42   *     Type parameters in method declarations. See {@link #typeMirror(Type)} for details.
43   * </li><li>
44   *     {@link #directSupertypes(TypeMirror)}
45   * </li><li>
46   *     {@link #asMemberOf(DeclaredType, Element)}
47   * </li><li>
48   *     {@link #isAssignable(TypeMirror, TypeMirror)}
49   * </li><li>
50   *     {@link #isSubsignature(ExecutableType, ExecutableType)}
51   * </li></ul>
52   */
53  public final class ReflectionTypes extends AbstractTypes {
54      private static final ReflectionTypes INSTANCE = new ReflectionTypes();
55  
56      private final ImmutableList<PrimitiveTypeImpl> primitiveTypes;
57      private final ImmutableList<TypeElementImpl> boxedTypeDeclarations;
58      {
59          List<PrimitiveTypeImpl> newTypes = new ArrayList<>(TypeKind.values().length);
60          List<TypeElementImpl> newDeclarations = new ArrayList<>(TypeKind.values().length);
61  
62          newTypes.addAll(Collections.<PrimitiveTypeImpl>nCopies(TypeKind.values().length, null));
63          newDeclarations.addAll(Collections.<TypeElementImpl>nCopies(TypeKind.values().length, null));
64  
65          addPrimitive(newTypes, newDeclarations, TypeKind.DOUBLE, PrimitiveTypeImpl.DOUBLE, Double.class);
66          addPrimitive(newTypes, newDeclarations, TypeKind.FLOAT, PrimitiveTypeImpl.FLOAT, Float.class);
67          addPrimitive(newTypes, newDeclarations, TypeKind.LONG, PrimitiveTypeImpl.LONG, Long.class);
68          addPrimitive(newTypes, newDeclarations, TypeKind.INT, PrimitiveTypeImpl.INT, Integer.class);
69          addPrimitive(newTypes, newDeclarations, TypeKind.SHORT, PrimitiveTypeImpl.SHORT, Short.class);
70          addPrimitive(newTypes, newDeclarations, TypeKind.BYTE, PrimitiveTypeImpl.BYTE, Byte.class);
71          addPrimitive(newTypes, newDeclarations, TypeKind.CHAR, PrimitiveTypeImpl.CHAR, Character.class);
72          addPrimitive(newTypes, newDeclarations, TypeKind.BOOLEAN, PrimitiveTypeImpl.BOOLEAN, Boolean.class);
73  
74          primitiveTypes = ImmutableList.copyOf(newTypes);
75          boxedTypeDeclarations = ImmutableList.copyOf(newDeclarations);
76      }
77  
78      private ReflectionTypes() { }
79  
80      /**
81       * Returns the singleton instance of this class.
82       *
83       * <p>Since this class does not contain any state, and since it is immutable, the returned instance is thread-safe.
84       *
85       * @return the singleton instance of this class
86       */
87      public static ReflectionTypes getInstance() {
88          return INSTANCE;
89      }
90  
91      @Override
92      protected void requireValidElement(Element element) {
93          Objects.requireNonNull(element);
94          if (!(element instanceof ReflectionElement)) {
95              throw new IllegalArgumentException(String.format(
96                  "Expected %s instance that was created by %s, but got instance of %s.",
97                  Element.class.getSimpleName(), ReflectionTypes.class, element.getClass()
98              ));
99          }
100     }
101 
102     @Override
103     protected void requireValidType(@Nullable TypeMirror type) {
104         if (!(type instanceof ReflectionTypeMirror) && type != null) {
105             throw new IllegalArgumentException(String.format(
106                 "Expected %s instance that was created by %s, but got instance of %s.",
107                 TypeMirror.class.getSimpleName(), ReflectionTypes.class, type.getClass()
108             ));
109         }
110     }
111 
112     private void addPrimitive(List<PrimitiveTypeImpl> newPrimitiveTypes, List<TypeElementImpl> newBoxedTypeDeclarations,
113             TypeKind kind, PrimitiveTypeImpl primitiveType, Class<?> clazz) {
114         newPrimitiveTypes.set(kind.ordinal(), primitiveType);
115         newBoxedTypeDeclarations.set(kind.ordinal(), ((DeclaredTypeImpl) typeMirror(clazz)).asElement());
116     }
117 
118     @Override
119     public TypeElement boxedClass(PrimitiveType primitiveType) {
120         requireValidType(Objects.requireNonNull(primitiveType));
121         @Nullable TypeElementImpl typeElement = boxedTypeDeclarations.get(primitiveType.getKind().ordinal());
122         assert typeElement != null : "Array with boxed type declarations incorrectly initialized.";
123         return typeElement;
124     }
125 
126     @Override
127     public PrimitiveType unboxedType(TypeMirror type) {
128         requireValidType(Objects.requireNonNull(type));
129 
130         if (type.getKind() != TypeKind.DECLARED) {
131             throw new IllegalArgumentException(String.format(
132                 "Expected type mirror of kind %s, but got %s.", TypeKind.DECLARED, type
133             ));
134         }
135 
136         Name name = ((DeclaredTypeImpl) type).asElement().getQualifiedName();
137         if (name.contentEquals(Double.class.getName())) {
138             return PrimitiveTypeImpl.DOUBLE;
139         } else if (name.contentEquals(Float.class.getName())) {
140             return PrimitiveTypeImpl.FLOAT;
141         } else if (name.contentEquals(Long.class.getName())) {
142             return PrimitiveTypeImpl.LONG;
143         } else if (name.contentEquals(Integer.class.getName())) {
144             return PrimitiveTypeImpl.INT;
145         } else if (name.contentEquals(Short.class.getName())) {
146             return PrimitiveTypeImpl.SHORT;
147         } else if (name.contentEquals(Byte.class.getName())) {
148             return PrimitiveTypeImpl.BYTE;
149         } else if (name.contentEquals(Character.class.getName())) {
150             return PrimitiveTypeImpl.CHAR;
151         } else if (name.contentEquals(Boolean.class.getName())) {
152             return PrimitiveTypeImpl.BOOLEAN;
153         } else {
154             throw new IllegalArgumentException(String.format("Expected boxed type, but got %s.", type));
155         }
156     }
157 
158     /**
159      * Returns a type mirror for the given {@link Class} object.
160      */
161     private ReflectionTypeMirror mirrorClass(Class<?> clazz, MirrorContext mirrorContext) {
162         if (clazz.isArray()) {
163             return new ArrayTypeImpl(mirrorContext.mirror(clazz.getComponentType()));
164         } else if (clazz.isPrimitive()) {
165             return (ReflectionTypeMirror) getPrimitiveType(TypeKind.valueOf(clazz.getName().toUpperCase()));
166         } else {
167             // raw type
168             @Nullable Class<?> enclosingClass = clazz.getEnclosingClass();
169             ReflectionTypeMirror enclosingType = enclosingClass == null
170                 ? NoTypeImpl.NONE
171                 : mirrorContext.mirror(enclosingClass);
172             return new DeclaredTypeImpl(enclosingType, mirrorContext.typeDeclaration(clazz),
173                 Collections.<ReflectionTypeMirror>emptyList());
174         }
175     }
176 
177     /**
178      * Returns a type mirror for the given {@link ParameterizedType} object.
179      */
180     private static DeclaredTypeImpl mirrorParameterizedType(ParameterizedType parameterizedType,
181             MirrorContext mirrorContext) {
182         Class<?> rawClass = (Class<?>) parameterizedType.getRawType();
183         TypeElementImpl typeDeclaration = mirrorContext.typeDeclaration(rawClass);
184         @Nullable Type ownerType = parameterizedType.getOwnerType();
185         ReflectionTypeMirror ownerTypeMirror = ownerType == null
186             ? NoTypeImpl.NONE
187             : mirrorContext.mirror(ownerType);
188         return new DeclaredTypeImpl(ownerTypeMirror, typeDeclaration,
189             mirrorContext.mirror(parameterizedType.getActualTypeArguments()));
190     }
191 
192     /**
193      * Returns a type mirror for the given {@link WildcardType} object.
194      *
195      * <p>The following preconditions are guaranteed by the JLS and the JavaDoc of package {@link java.lang.reflect}:
196      * <ul><li>
197      *     {@link WildcardType#getUpperBounds()} specifies: "Note that if no upper bound is explicitly
198      *     declared, the upper bound is {@code Object}."
199      * </li><li>
200      *     While {@link WildcardType#getUpperBounds()} and
201      *     {@link WildcardType#getLowerBounds()} return an arrays, JLS ยง4.5.1 (at least up to
202      *     version 8) only supports a single ReferenceType for both bounds.
203      * </li></ul>
204      */
205     private static WildcardTypeImpl mirrorWildcardType(WildcardType wildcardType, MirrorContext mirrorContext) {
206         Type[] upperBounds = wildcardType.getUpperBounds();
207         Type[] lowerBounds = wildcardType.getLowerBounds();
208 
209         // See JavaDoc for an explanation of the following assert statement.
210         assert upperBounds.length == 1 && lowerBounds.length <= 1
211             && (lowerBounds.length == 0 || Object.class.equals(upperBounds[0]));
212 
213         @Nullable ReflectionTypeMirror extendsBounds;
214         if (Object.class.equals(upperBounds[0])) {
215             extendsBounds = null;
216         } else {
217             extendsBounds = mirrorContext.mirror(upperBounds[0]);
218         }
219 
220         @Nullable ReflectionTypeMirror superBound;
221         if (lowerBounds.length == 0) {
222             superBound = null;
223         } else {
224             superBound = mirrorContext.mirror(lowerBounds[0]);
225         }
226         return new WildcardTypeImpl(extendsBounds, superBound);
227     }
228 
229     static void requireCondition(boolean condition, String formatString, Object argument) {
230         if (!condition) {
231             throw new IllegalStateException(String.format(formatString, argument));
232         }
233     }
234 
235     private static TypeVariableImpl mirrorTypeVariable(TypeVariable<?> typeVariable, MirrorContext mirrorContext) {
236         GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
237         if (genericDeclaration instanceof Class<?>) {
238             TypeElementImpl typeDeclaration = mirrorContext.typeDeclaration((Class<?>) genericDeclaration);
239             @Nullable TypeParameterElementImpl element = null;
240             for (TypeParameterElementImpl typeParameter: typeDeclaration.getTypeParameters()) {
241                 if (typeParameter.getSimpleName().contentEquals(typeVariable.getName())) {
242                     element = typeParameter;
243                     break;
244                 }
245             }
246             requireCondition(element != null,
247                 "Could not find the type-parameter element that corresponds to type variable %s.", typeVariable);
248             assert element != null : "redundant check for FindBugs";
249             return element.asType();
250         } else {
251             throw new UnsupportedOperationException("Method or constructor type parameters not supported.");
252         }
253     }
254 
255     ReflectionTypeMirror mirrorInternal(Type type, MirrorContext mirrorContext) {
256         @Nullable ReflectionTypeMirror typeMirror = null;
257         if (type instanceof Class<?>) {
258             typeMirror = mirrorClass((Class<?>) type, mirrorContext);
259         } else if (type instanceof ParameterizedType) {
260             typeMirror = mirrorParameterizedType((ParameterizedType) type, mirrorContext);
261         } else if (type instanceof GenericArrayType) {
262             typeMirror = new ArrayTypeImpl(
263                 mirrorContext.mirror(((GenericArrayType) type).getGenericComponentType())
264             );
265         } else if (type instanceof WildcardType) {
266             typeMirror = mirrorWildcardType((WildcardType) type, mirrorContext);
267         } else if (type instanceof TypeVariable<?>) {
268             typeMirror = mirrorTypeVariable((TypeVariable<?>) type, mirrorContext);
269         }
270         requireCondition(typeMirror != null,
271             "Expected Class, ParameterizedType, GenericArrayType, WildcardType, or TypeVariable instance, but got %s.",
272             type);
273         assert typeMirror != null : "redundant check for FindBugs";
274         return typeMirror;
275     }
276 
277     /**
278      * Returns a type element corresponding to the given {@link Class} object.
279      *
280      * @param clazz class object
281      * @return type element corresponding to the given {@link Class} object
282      * @throws IllegalArgumentException if the given class represents a primitive or array type
283      * @throws UnsupportedOperationException if a generic declaration is referenced that is not of type {@link Class},
284      *     see {@link #typeMirror(Type)} for details
285      */
286     public TypeElement typeElement(Class<?> clazz) {
287         if (clazz.isArray() || clazz.isPrimitive()) {
288             throw new IllegalArgumentException(String.format("Expected class or interface type, but got %s.", clazz));
289         }
290 
291         return ((DeclaredTypeImpl) typeMirror(clazz)).asElement();
292     }
293 
294     /**
295      * Returns a type mirror corresponding to the given Java reflection type.
296      *
297      * <p>Type parameters in method declarations are not currently supported. That is, if the given type references a
298      * {@link TypeVariable} instance that has a {@link java.lang.reflect.Constructor} or
299      * {@link java.lang.reflect.Method} as generic declaration, an {@link UnsupportedOperationException}
300      * will be thrown.
301      *
302      * @param type type as represented by Java Reflection API
303      * @return type mirror corresponding to the given Java reflection type
304      * @throws UnsupportedOperationException if a generic declaration is referenced that is not of type {@link Class}
305      */
306     @Override
307     public TypeMirror typeMirror(Type type) {
308         Map<Class<?>, TypeElementImpl> typeDeclarations = new LinkedHashMap<>();
309         Map<Class<?>, TypeElementImpl> newTypeDeclarations = new LinkedHashMap<>();
310         MirrorContext mirrorContext = new MirrorContext(this, typeDeclarations, newTypeDeclarations);
311 
312         TypeMirror typeMirror = mirrorInternal(type, mirrorContext);
313         while (!newTypeDeclarations.isEmpty()) {
314             List<TypeElementImpl> typeDeclarationsToFinish = new ArrayList<>(newTypeDeclarations.values());
315             typeDeclarations.putAll(newTypeDeclarations);
316             newTypeDeclarations.clear();
317             for (TypeElementImpl typeDeclaration: typeDeclarationsToFinish) {
318                 typeDeclaration.finish(mirrorContext);
319             }
320         }
321         return typeMirror;
322     }
323 
324     private static ImmutableList<ReflectionTypeMirror> toList(TypeMirror[] types) {
325         List<ReflectionTypeMirror> list = new ArrayList<>(types.length);
326         for (TypeMirror type: types) {
327             list.add((ReflectionTypeMirror) type);
328         }
329         return ImmutableList.copyOf(list);
330     }
331 
332     @Override
333     public DeclaredType getDeclaredType(@Nullable DeclaredType containing, TypeElement typeElem,
334             TypeMirror... typeArgs) {
335         requireValidType(containing);
336         requireValidElement(typeElem);
337         requireValidTypes(typeArgs);
338 
339         ReflectionTypeMirror newContainingType = containing == null
340             ? NoTypeImpl.NONE
341             : (ReflectionTypeMirror) containing;
342         return new DeclaredTypeImpl(newContainingType, (TypeElementImpl) typeElem, toList(typeArgs));
343     }
344 
345     @Override
346     public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
347         requireValidElement(typeElem);
348         requireValidTypes(typeArgs);
349 
350         return new DeclaredTypeImpl(NoTypeImpl.NONE, (TypeElementImpl) typeElem, toList(typeArgs));
351     }
352 
353     @Override
354     public NoType getNoType(TypeKind kind) {
355         Objects.requireNonNull(kind);
356         if (kind == TypeKind.VOID) {
357             return NoTypeImpl.VOID;
358         } else if (kind == TypeKind.NONE) {
359             return NoTypeImpl.NONE;
360         } else {
361             throw new IllegalArgumentException(String.format("Expected one of %s, but got %s.",
362                 Arrays.asList(TypeKind.VOID, TypeKind.NONE), kind));
363         }
364     }
365 
366     @Override
367     public NullType getNullType() {
368         return NullTypeImpl.INSTANCE;
369     }
370 
371     @Override
372     public ArrayType getArrayType(TypeMirror componentType) {
373         Objects.requireNonNull(componentType);
374         requireValidType(componentType);
375 
376         return new ArrayTypeImpl((ReflectionTypeMirror) componentType);
377     }
378 
379     @Override
380     protected javax.lang.model.type.TypeVariable createTypeVariable(TypeParameterElement typeParameter,
381             @Nullable javax.lang.model.type.WildcardType capturedTypeArgument) {
382         requireValidElement(Objects.requireNonNull(typeParameter));
383         requireValidType(capturedTypeArgument);
384 
385         return new TypeVariableImpl((TypeParameterElementImpl) typeParameter, (WildcardTypeImpl) capturedTypeArgument);
386     }
387 
388     @Override
389     protected javax.lang.model.type.WildcardType capturedTypeArgument(javax.lang.model.type.TypeVariable typeVariable) {
390         requireValidType(Objects.requireNonNull(typeVariable));
391 
392         return ((TypeVariableImpl) typeVariable).getCapturedTypeArgument();
393     }
394 
395     @Override
396     public IntersectionType getIntersectionType(TypeMirror... bounds) {
397         Objects.requireNonNull(bounds);
398         if (bounds.length == 0) {
399             throw new IllegalArgumentException("Expected at least one bound.");
400         }
401         requireValidTypes(bounds);
402 
403         List<ReflectionTypeMirror> newBounds = new ArrayList<>(bounds.length);
404         for (TypeMirror bound: bounds) {
405             newBounds.add((ReflectionTypeMirror) bound);
406         }
407 
408         return new IntersectionTypeImpl(newBounds);
409     }
410 
411     @Override
412     protected void setTypeVariableBounds(javax.lang.model.type.TypeVariable typeVariable, TypeMirror upperBound,
413             TypeMirror lowerBound) {
414         requireValidType(Objects.requireNonNull(typeVariable));
415         requireValidType(Objects.requireNonNull(upperBound));
416         requireValidType(Objects.requireNonNull(lowerBound));
417 
418         ((TypeVariableImpl) typeVariable).setUpperAndLowerBounds(
419             (ReflectionTypeMirror) upperBound,
420             (ReflectionTypeMirror) lowerBound
421         );
422     }
423 
424     @Override
425     public PrimitiveType getPrimitiveType(TypeKind kind) {
426         @Nullable PrimitiveTypeImpl primitiveType = primitiveTypes.get(kind.ordinal());
427         if (primitiveType == null) {
428             throw new IllegalArgumentException(String.format("Expected primitive kind, but got %s.", kind));
429         }
430         return primitiveType;
431     }
432 
433     @Override
434     public javax.lang.model.type.WildcardType getWildcardType(@Nullable TypeMirror extendsBound,
435             @Nullable TypeMirror superBound) {
436         requireValidType(extendsBound);
437         requireValidType(superBound);
438 
439         return new WildcardTypeImpl((ReflectionTypeMirror) extendsBound, (ReflectionTypeMirror) superBound);
440     }
441 
442     private static UnsupportedOperationException unsupportedException() {
443         return new UnsupportedOperationException(
444             "isAssignable(), isSubsignature(), asMemberOf(), and directSupertypes() not currently supported."
445         );
446     }
447 
448     @Override
449     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
450         throw unsupportedException();
451     }
452 
453     @Override
454     public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
455         throw unsupportedException();
456     }
457 
458     @Override
459     public TypeMirror asMemberOf(DeclaredType containing, Element element) {
460         throw unsupportedException();
461     }
462 
463     /**
464      * @throws UnsupportedOperationException whenever this method is called
465      */
466     @Override
467     public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
468         throw unsupportedException();
469     }
470 }