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