AnnotationClass.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.databinding.tool.reflection.annotation;
17
18import org.antlr.v4.codegen.model.decl.Decl;
19
20import android.databinding.tool.reflection.ModelAnalyzer;
21import android.databinding.tool.reflection.ModelClass;
22import android.databinding.tool.reflection.ModelField;
23import android.databinding.tool.reflection.ModelMethod;
24import android.databinding.tool.reflection.TypeUtil;
25import android.databinding.tool.util.L;
26
27import java.util.ArrayList;
28import java.util.List;
29
30import javax.lang.model.element.AnnotationMirror;
31import javax.lang.model.element.AnnotationValue;
32import javax.lang.model.element.Element;
33import javax.lang.model.element.ElementKind;
34import javax.lang.model.element.ExecutableElement;
35import javax.lang.model.element.TypeElement;
36import javax.lang.model.element.VariableElement;
37import javax.lang.model.type.ArrayType;
38import javax.lang.model.type.DeclaredType;
39import javax.lang.model.type.PrimitiveType;
40import javax.lang.model.type.TypeKind;
41import javax.lang.model.type.TypeMirror;
42import javax.lang.model.util.ElementFilter;
43import javax.lang.model.util.Elements;
44import javax.lang.model.util.Types;
45
46/**
47 * This is the implementation of ModelClass for the annotation
48 * processor. It relies on AnnotationAnalyzer.
49 */
50class AnnotationClass extends ModelClass {
51
52    final TypeMirror mTypeMirror;
53
54    public AnnotationClass(TypeMirror typeMirror) {
55        mTypeMirror = typeMirror;
56    }
57
58    @Override
59    public String toJavaCode() {
60        return mTypeMirror.toString();
61    }
62
63    @Override
64    public boolean isArray() {
65        return mTypeMirror.getKind() == TypeKind.ARRAY;
66    }
67
68    @Override
69    public AnnotationClass getComponentType() {
70        TypeMirror component = null;
71        if (isArray()) {
72            component = ((ArrayType) mTypeMirror).getComponentType();
73        } else if (isList()) {
74            for (ModelMethod method : getMethods("get", 1)) {
75                ModelClass parameter = method.getParameterTypes()[0];
76                if (parameter.isInt() || parameter.isLong()) {
77                    ArrayList<ModelClass> parameters = new ArrayList<ModelClass>(1);
78                    parameters.add(parameter);
79                    return (AnnotationClass) method.getReturnType(parameters);
80                }
81            }
82            // no "get" call found!
83            return null;
84        } else {
85            AnnotationClass mapClass = (AnnotationClass) ModelAnalyzer.getInstance().getMapType();
86            DeclaredType mapType = findInterface(mapClass.mTypeMirror);
87            if (mapType == null) {
88                return null;
89            }
90            component = mapType.getTypeArguments().get(1);
91        }
92
93        return new AnnotationClass(component);
94    }
95
96    private DeclaredType findInterface(TypeMirror interfaceType) {
97        Types typeUtil = getTypeUtils();
98        TypeMirror foundInterface = null;
99        if (typeUtil.isSameType(interfaceType, typeUtil.erasure(mTypeMirror))) {
100            foundInterface = mTypeMirror;
101        } else {
102            ArrayList<TypeMirror> toCheck = new ArrayList<TypeMirror>();
103            toCheck.add(mTypeMirror);
104            while (!toCheck.isEmpty()) {
105                TypeMirror typeMirror = toCheck.remove(0);
106                if (typeUtil.isSameType(interfaceType, typeUtil.erasure(typeMirror))) {
107                    foundInterface = typeMirror;
108                    break;
109                } else {
110                    toCheck.addAll(typeUtil.directSupertypes(typeMirror));
111                }
112            }
113            if (foundInterface == null) {
114                L.e("Detected " + interfaceType + " type for " + mTypeMirror +
115                        ", but not able to find the implemented interface.");
116                return null;
117            }
118        }
119        if (foundInterface.getKind() != TypeKind.DECLARED) {
120            L.e("Found " + interfaceType + " type for " + mTypeMirror +
121                    ", but it isn't a declared type: " + foundInterface);
122            return null;
123        }
124        return (DeclaredType) foundInterface;
125    }
126
127    @Override
128    public boolean isNullable() {
129        switch (mTypeMirror.getKind()) {
130            case ARRAY:
131            case DECLARED:
132            case NULL:
133                return true;
134            default:
135                return false;
136        }
137    }
138
139    @Override
140    public boolean isPrimitive() {
141        switch (mTypeMirror.getKind()) {
142            case BOOLEAN:
143            case BYTE:
144            case SHORT:
145            case INT:
146            case LONG:
147            case CHAR:
148            case FLOAT:
149            case DOUBLE:
150                return true;
151            default:
152                return false;
153        }
154    }
155
156    @Override
157    public boolean isBoolean() {
158        return mTypeMirror.getKind() == TypeKind.BOOLEAN;
159    }
160
161    @Override
162    public boolean isChar() {
163        return mTypeMirror.getKind() == TypeKind.CHAR;
164    }
165
166    @Override
167    public boolean isByte() {
168        return mTypeMirror.getKind() == TypeKind.BYTE;
169    }
170
171    @Override
172    public boolean isShort() {
173        return mTypeMirror.getKind() == TypeKind.SHORT;
174    }
175
176    @Override
177    public boolean isInt() {
178        return mTypeMirror.getKind() == TypeKind.INT;
179    }
180
181    @Override
182    public boolean isLong() {
183        return mTypeMirror.getKind() == TypeKind.LONG;
184    }
185
186    @Override
187    public boolean isFloat() {
188        return mTypeMirror.getKind() == TypeKind.FLOAT;
189    }
190
191    @Override
192    public boolean isDouble() {
193        return mTypeMirror.getKind() == TypeKind.DOUBLE;
194    }
195
196    @Override
197    public boolean isGeneric() {
198        boolean isGeneric = false;
199        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
200            DeclaredType declaredType = (DeclaredType) mTypeMirror;
201            List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
202            isGeneric = typeArguments != null && !typeArguments.isEmpty();
203        }
204        return isGeneric;
205    }
206
207    @Override
208    public int getMinApi() {
209        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
210            DeclaredType declaredType = (DeclaredType) mTypeMirror;
211            List<? extends AnnotationMirror> annotations =
212                    getElementUtils().getAllAnnotationMirrors(declaredType.asElement());
213
214            TypeElement targetApi = getElementUtils().getTypeElement("android.annotation.TargetApi");
215            TypeMirror targetApiType = targetApi.asType();
216            Types typeUtils = getTypeUtils();
217            for (AnnotationMirror annotation : annotations) {
218                if (typeUtils.isAssignable(annotation.getAnnotationType(), targetApiType)) {
219                    for (AnnotationValue value : annotation.getElementValues().values()) {
220                        return (Integer) value.getValue();
221                    }
222                }
223            }
224        }
225        return super.getMinApi();
226    }
227
228    @Override
229    public List<ModelClass> getTypeArguments() {
230        List<ModelClass> types = null;
231        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
232            DeclaredType declaredType = (DeclaredType) mTypeMirror;
233            List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
234            if (typeArguments != null && !typeArguments.isEmpty()) {
235                types = new ArrayList<ModelClass>();
236                for (TypeMirror typeMirror : typeArguments) {
237                    types.add(new AnnotationClass(typeMirror));
238                }
239            }
240        }
241        return types;
242    }
243
244    @Override
245    public boolean isTypeVar() {
246        return mTypeMirror.getKind() == TypeKind.TYPEVAR;
247    }
248
249    @Override
250    public boolean isInterface() {
251        return mTypeMirror.getKind() == TypeKind.DECLARED &&
252                ((DeclaredType)mTypeMirror).asElement().getKind() == ElementKind.INTERFACE;
253    }
254
255    @Override
256    public boolean isVoid() {
257        return mTypeMirror.getKind() == TypeKind.VOID;
258    }
259
260    @Override
261    public AnnotationClass unbox() {
262        if (!isNullable()) {
263            return this;
264        }
265        try {
266            return new AnnotationClass(getTypeUtils().unboxedType(mTypeMirror));
267        } catch (IllegalArgumentException e) {
268            // I'm being lazy. This is much easier than checking every type.
269            return this;
270        }
271    }
272
273    @Override
274    public AnnotationClass box() {
275        if (!isPrimitive()) {
276            return this;
277        }
278        return new AnnotationClass(getTypeUtils().boxedClass((PrimitiveType) mTypeMirror).asType());
279    }
280
281    @Override
282    public boolean isAssignableFrom(ModelClass that) {
283        if (that == null) {
284            return false;
285        }
286        AnnotationClass thatAnnotationClass = (AnnotationClass) that;
287        return getTypeUtils().isAssignable(thatAnnotationClass.mTypeMirror, this.mTypeMirror);
288    }
289
290    @Override
291    public ModelMethod[] getDeclaredMethods() {
292        final ModelMethod[] declaredMethods;
293        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
294            DeclaredType declaredType = (DeclaredType) mTypeMirror;
295            Elements elementUtils = getElementUtils();
296            TypeElement typeElement = (TypeElement) declaredType.asElement();
297            List<? extends Element> members = elementUtils.getAllMembers(typeElement);
298            List<ExecutableElement> methods = ElementFilter.methodsIn(members);
299            declaredMethods = new ModelMethod[methods.size()];
300            for (int i = 0; i < declaredMethods.length; i++) {
301                declaredMethods[i] = new AnnotationMethod(declaredType, methods.get(i));
302            }
303        } else {
304            declaredMethods = new ModelMethod[0];
305        }
306        return declaredMethods;
307    }
308
309    @Override
310    public AnnotationClass getSuperclass() {
311        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
312            DeclaredType declaredType = (DeclaredType) mTypeMirror;
313            TypeElement typeElement = (TypeElement) declaredType.asElement();
314            TypeMirror superClass = typeElement.getSuperclass();
315            if (superClass.getKind() == TypeKind.DECLARED) {
316                return new AnnotationClass(superClass);
317            }
318        }
319        return null;
320    }
321
322    @Override
323    public String getCanonicalName() {
324        return getTypeUtils().erasure(mTypeMirror).toString();
325    }
326
327    @Override
328    public ModelClass erasure() {
329        final TypeMirror erasure = getTypeUtils().erasure(mTypeMirror);
330        if (erasure == mTypeMirror) {
331            return this;
332        } else {
333            return new AnnotationClass(erasure);
334        }
335    }
336
337    @Override
338    public String getJniDescription() {
339        return TypeUtil.getInstance().getDescription(this);
340    }
341
342    @Override
343    protected ModelField[] getDeclaredFields() {
344        final ModelField[] declaredFields;
345        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
346            DeclaredType declaredType = (DeclaredType) mTypeMirror;
347            Elements elementUtils = getElementUtils();
348            TypeElement typeElement = (TypeElement) declaredType.asElement();
349            List<? extends Element> members = elementUtils.getAllMembers(typeElement);
350            List<VariableElement> fields = ElementFilter.fieldsIn(members);
351            declaredFields = new ModelField[fields.size()];
352            for (int i = 0; i < declaredFields.length; i++) {
353                declaredFields[i] = new AnnotationField(typeElement, fields.get(i));
354            }
355        } else {
356            declaredFields = new ModelField[0];
357        }
358        return declaredFields;
359    }
360
361    @Override
362    public boolean equals(Object obj) {
363        if (obj instanceof AnnotationClass) {
364            return getTypeUtils().isSameType(mTypeMirror, ((AnnotationClass) obj).mTypeMirror);
365        } else {
366            return false;
367        }
368    }
369
370    @Override
371    public int hashCode() {
372        return mTypeMirror.toString().hashCode();
373    }
374
375    private static Types getTypeUtils() {
376        return AnnotationAnalyzer.get().mProcessingEnv.getTypeUtils();
377    }
378
379    private static Elements getElementUtils() {
380        return AnnotationAnalyzer.get().mProcessingEnv.getElementUtils();
381    }
382
383    @Override
384    public String toString() {
385        return mTypeMirror.toString();
386    }
387}
388