View Javadoc
1   package net.florianschoppmann.java.reflect;
2   
3   import javax.annotation.Nullable;
4   import javax.lang.model.element.ElementKind;
5   import javax.lang.model.element.ElementVisitor;
6   import javax.lang.model.element.Name;
7   import javax.lang.model.element.NestingKind;
8   import javax.lang.model.element.TypeElement;
9   import javax.lang.model.type.TypeMirror;
10  import java.lang.reflect.Type;
11  import java.lang.reflect.TypeVariable;
12  import java.util.ArrayList;
13  import java.util.List;
14  import java.util.Objects;
15  
16  final class TypeElementImpl extends ElementImpl implements TypeElement, ReflectionParameterizable {
17      private final Class<?> clazz;
18      private final ImmutableList<TypeParameterElementImpl> typeParameters;
19      @Nullable private ReflectionElement enclosingElement;
20      @Nullable private ReflectionTypeMirror superClass;
21      @Nullable private List<ReflectionTypeMirror> interfaces;
22      @Nullable private List<ElementImpl> enclosedElements;
23  
24      /**
25       * Cache the type returned by {@link #asType()}.
26       *
27       * <p>Similarly to {@link String#hashCode()}, caching is not synchronized. This means that different threads may
28       * (at least theoretically) see different values for this field. However, this is not a problem because all such
29       * values would compare equal. Moreover, ยง17.7 JLS specifies that "Writes to and reads of references are always
30       * atomic, regardless of whether they are implemented as 32-bit or 64-bit values". Hence, even if caches were
31       * updated, every access to this field would yield a well-defined result.
32       */
33      @Nullable private DeclaredTypeImpl type;
34  
35      TypeElementImpl(Class<?> clazz) {
36          this.clazz = Objects.requireNonNull(clazz);
37  
38          List<TypeParameterElementImpl> newTypeParameters = new ArrayList<>();
39          for (TypeVariable<?> parameter: clazz.getTypeParameters()) {
40              newTypeParameters.add(new TypeParameterElementImpl(parameter, this));
41          }
42          typeParameters = ImmutableList.copyOf(newTypeParameters);
43      }
44  
45      @Override
46      public boolean equals(@Nullable Object otherObject) {
47          if (this == otherObject) {
48              return true;
49          } else if (otherObject == null || getClass() != otherObject.getClass()) {
50              return false;
51          }
52  
53          return clazz.equals(((TypeElementImpl) otherObject).clazz);
54      }
55  
56      @Override
57      public int hashCode() {
58          return clazz.hashCode();
59      }
60  
61      @Override
62      public String toString() {
63          return clazz.toString();
64      }
65  
66      @Override
67      public <R, P> R accept(ElementVisitor<R, P> visitor, @Nullable P parameter) {
68          return visitor.visitType(this, parameter);
69      }
70  
71      @Override
72      public List<ElementImpl> getEnclosedElements() {
73          requireFinished();
74          assert enclosedElements != null : "must be non-null when finished";
75          return enclosedElements;
76      }
77  
78      @Override
79      public NestingKind getNestingKind() {
80          throw new UnsupportedOperationException(String.format(
81              "Nesting kind not currently supported by %s.", ReflectionTypes.class
82          ));
83      }
84  
85      @Override
86      public NameImpl getQualifiedName() {
87          return new NameImpl(clazz.getCanonicalName());
88      }
89  
90      @Override
91      public Name getSimpleName() {
92          return new NameImpl(clazz.getSimpleName());
93      }
94  
95      @Override
96      public TypeMirror getSuperclass() {
97          requireFinished();
98          assert superClass != null : "must be non-null when finished";
99          return superClass;
100     }
101 
102     @Override
103     public List<? extends TypeMirror> getInterfaces() {
104         requireFinished();
105         assert interfaces != null : "must be non-null when finished";
106         return interfaces;
107     }
108 
109     @Override
110     public List<TypeParameterElementImpl> getTypeParameters() {
111         return typeParameters;
112     }
113 
114     @Override
115     public ReflectionElement getEnclosingElement() {
116         requireFinished();
117 
118         if (enclosingElement == null) {
119             throw new UnsupportedOperationException("getEnclosingElement() not supported for top-level classes.");
120         } else {
121             return enclosingElement;
122         }
123     }
124 
125     @Override
126     public DeclaredTypeImpl asType() {
127         requireFinished();
128 
129         @Nullable DeclaredTypeImpl localType = type;
130         if (localType == null) {
131             List<TypeVariableImpl> prototypicalTypeArguments = new ArrayList<>(typeParameters.size());
132             for (TypeParameterElementImpl typeParameter: typeParameters) {
133                 prototypicalTypeArguments.add(typeParameter.asType());
134             }
135 
136             ReflectionTypeMirror enclosingType = enclosingElement == null
137                 ? NoTypeImpl.NONE
138                 : enclosingElement.asType();
139             localType = new DeclaredTypeImpl(enclosingType, this, prototypicalTypeArguments);
140             type = localType;
141         }
142         return localType;
143     }
144 
145     @Override
146     public ElementKind getKind() {
147         if (clazz.isEnum()) {
148             return ElementKind.ENUM;
149         } else if (clazz.isAnnotation()) {
150             return ElementKind.ANNOTATION_TYPE;
151         } else if (clazz.isInterface()) {
152             return ElementKind.INTERFACE;
153         } else {
154             return ElementKind.CLASS;
155         }
156     }
157 
158     @Override
159     protected void finishDerivedFromElement(MirrorContext mirrorContext) {
160         @Nullable Class<?> enclosingClass = clazz.getEnclosingClass();
161         enclosingElement = enclosingClass == null
162             ? null
163             : mirrorContext.typeDeclaration(enclosingClass);
164 
165         Class<?>[] declaredClasses = clazz.getDeclaredClasses();
166         List<ElementImpl> newEnclosedElements = new ArrayList<>(typeParameters.size() + declaredClasses.length);
167         newEnclosedElements.addAll(typeParameters);
168         for (Class<?> declaredClass: declaredClasses) {
169             newEnclosedElements.add(mirrorContext.typeDeclaration(declaredClass));
170         }
171         enclosedElements = ImmutableList.copyOf(newEnclosedElements);
172 
173         @Nullable Type genericSuperClass = clazz.getGenericSuperclass();
174         superClass = genericSuperClass == null
175             ? NoTypeImpl.NONE
176             : mirrorContext.mirror(genericSuperClass);
177         interfaces = mirrorContext.mirror(clazz.getGenericInterfaces());
178         for (TypeParameterElementImpl typeParameter: typeParameters) {
179             typeParameter.finish(mirrorContext);
180         }
181 
182         // Field 'type' is lazily initialized in order to break a dependency chain: Constructing type requires
183         // enclosingElement.asType(), which at this point may not yet be available.
184     }
185 }