AnnotationAnalyzer.java revision 0cb9fbb96197af013f4f879ed6cddf2681b88fd6
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;
35import javax.tools.Diagnostic;
36
37public class AnnotationAnalyzer extends ModelAnalyzer {
38
39    public static final Map<String, TypeKind> PRIMITIVE_TYPES =
40            new ImmutableMap.Builder<String, TypeKind>()
41                    .put("boolean", TypeKind.BOOLEAN)
42                    .put("byte", TypeKind.BYTE)
43                    .put("short", TypeKind.SHORT)
44                    .put("char", TypeKind.CHAR)
45                    .put("int", TypeKind.INT)
46                    .put("long", TypeKind.LONG)
47                    .put("float", TypeKind.FLOAT)
48                    .put("double", TypeKind.DOUBLE)
49                    .build();
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