AnnotationAnalyzer.java revision b9e4aa96812692a7dcf468445e64bc5b30d3c79a
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 com.google.common.collect.ImmutableMap;
19
20import android.databinding.tool.reflection.ModelAnalyzer;
21import android.databinding.tool.reflection.ModelClass;
22import android.databinding.tool.reflection.TypeUtil;
23import android.databinding.tool.util.L;
24
25import java.util.ArrayList;
26import java.util.Map;
27
28import javax.annotation.processing.ProcessingEnvironment;
29import javax.lang.model.element.TypeElement;
30import javax.lang.model.type.DeclaredType;
31import javax.lang.model.type.TypeKind;
32import javax.lang.model.type.TypeMirror;
33import javax.lang.model.util.Elements;
34import javax.lang.model.util.Types;
35
36public class AnnotationAnalyzer extends ModelAnalyzer {
37
38    public static final Map<String, TypeKind> PRIMITIVE_TYPES =
39            new ImmutableMap.Builder<String, TypeKind>()
40                    .put("boolean", TypeKind.BOOLEAN)
41                    .put("byte", TypeKind.BYTE)
42                    .put("short", TypeKind.SHORT)
43                    .put("char", TypeKind.CHAR)
44                    .put("int", TypeKind.INT)
45                    .put("long", TypeKind.LONG)
46                    .put("float", TypeKind.FLOAT)
47                    .put("double", TypeKind.DOUBLE)
48                    .build();
49
50    public final ProcessingEnvironment mProcessingEnv;
51
52    public AnnotationAnalyzer(ProcessingEnvironment processingEnvironment) {
53        mProcessingEnv = processingEnvironment;
54        setInstance(this);
55    }
56
57    public static AnnotationAnalyzer get() {
58        return (AnnotationAnalyzer) getInstance();
59    }
60
61    @Override
62    public AnnotationClass loadPrimitive(String className) {
63        TypeKind typeKind = PRIMITIVE_TYPES.get(className);
64        if (typeKind == null) {
65            return null;
66        } else {
67            Types typeUtils = getTypeUtils();
68            return new AnnotationClass(typeUtils.getPrimitiveType(typeKind));
69        }
70    }
71
72    @Override
73    public AnnotationClass findClass(String className, Map<String, String> imports) {
74        className = className.trim();
75        int numDimensions = 0;
76        while (className.endsWith("[]")) {
77            numDimensions++;
78            className = className.substring(0, className.length() - 2);
79        }
80        AnnotationClass primitive = loadPrimitive(className);
81        if (primitive != null) {
82            return addDimension(primitive.mTypeMirror, numDimensions);
83        }
84        int templateOpenIndex = className.indexOf('<');
85        DeclaredType declaredType;
86        if (templateOpenIndex < 0) {
87            TypeElement typeElement = getTypeElement(className, imports);
88            if (typeElement == null) {
89                return null;
90            }
91            declaredType = (DeclaredType) typeElement.asType();
92        } else {
93            int templateCloseIndex = className.lastIndexOf('>');
94            String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex);
95
96            String baseClassName = className.substring(0, templateOpenIndex);
97            TypeElement typeElement = getTypeElement(baseClassName, imports);
98            if (typeElement == null) {
99                L.e("cannot find type element for %s", baseClassName);
100                return null;
101            }
102
103            ArrayList<String> templateParameters = splitTemplateParameters(paramStr);
104            TypeMirror[] typeArgs = new TypeMirror[templateParameters.size()];
105            for (int i = 0; i < typeArgs.length; i++) {
106                final AnnotationClass clazz = findClass(templateParameters.get(i), imports);
107                if (clazz == null) {
108                    L.e("cannot find type argument for %s in %s", templateParameters.get(i),
109                            baseClassName);
110                    return null;
111                }
112                typeArgs[i] = clazz.mTypeMirror;
113            }
114            Types typeUtils = getTypeUtils();
115            declaredType = typeUtils.getDeclaredType(typeElement, typeArgs);
116        }
117        return addDimension(declaredType, numDimensions);
118    }
119
120    private AnnotationClass addDimension(TypeMirror type, int numDimensions) {
121        while (numDimensions > 0) {
122            type = getTypeUtils().getArrayType(type);
123            numDimensions--;
124        }
125        return new AnnotationClass(type);
126    }
127
128    private TypeElement getTypeElement(String className, Map<String, String> imports) {
129        Elements elementUtils = getElementUtils();
130        final boolean hasDot = className.indexOf('.') >= 0;
131        if (!hasDot && imports != null) {
132            // try the imports
133            String importedClass = imports.get(className);
134            if (importedClass != null) {
135                className = importedClass;
136            }
137        }
138        if (className.indexOf('.') < 0) {
139            // try java.lang.
140            String javaLangClass = "java.lang." + className;
141            try {
142                TypeElement javaLang = elementUtils.getTypeElement(javaLangClass);
143                if (javaLang != null) {
144                    return javaLang;
145                }
146            } catch (Exception e) {
147                // try the normal way
148            }
149        }
150        try {
151            TypeElement typeElement = elementUtils.getTypeElement(className);
152            if (typeElement == null && hasDot && imports != null) {
153                int lastDot = className.lastIndexOf('.');
154                TypeElement parent = getTypeElement(className.substring(0, lastDot), imports);
155                if (parent == null) {
156                    return null;
157                }
158                String name = parent.getQualifiedName() + "." + className.substring(lastDot + 1);
159                return getTypeElement(name, null);
160            }
161            return typeElement;
162        } catch (Exception e) {
163            return null;
164        }
165    }
166
167    private ArrayList<String> splitTemplateParameters(String templateParameters) {
168        ArrayList<String> list = new ArrayList<String>();
169        int index = 0;
170        int openCount = 0;
171        StringBuilder arg = new StringBuilder();
172        while (index < templateParameters.length()) {
173            char c = templateParameters.charAt(index);
174            if (c == ',' && openCount == 0) {
175                list.add(arg.toString());
176                arg.delete(0, arg.length());
177            } else if (!Character.isWhitespace(c)) {
178                arg.append(c);
179                if (c == '<') {
180                    openCount++;
181                } else if (c == '>') {
182                    openCount--;
183                }
184            }
185            index++;
186        }
187        list.add(arg.toString());
188        return list;
189    }
190
191    @Override
192    public ModelClass findClass(Class classType) {
193        return findClass(classType.getCanonicalName(), null);
194    }
195
196    public Types getTypeUtils() {
197        return mProcessingEnv.getTypeUtils();
198    }
199
200    public Elements getElementUtils() {
201        return mProcessingEnv.getElementUtils();
202    }
203
204    public ProcessingEnvironment getProcessingEnv() {
205        return mProcessingEnv;
206    }
207
208    @Override
209    public TypeUtil createTypeUtil() {
210        return new AnnotationTypeUtil(this);
211    }
212}
213