• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package android.databinding.tool.reflection.annotation;
17 
18 import android.databinding.tool.reflection.ModelAnalyzer;
19 import android.databinding.tool.reflection.ModelClass;
20 import android.databinding.tool.reflection.TypeUtil;
21 import android.databinding.tool.util.L;
22 
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Map;
26 
27 import javax.annotation.processing.ProcessingEnvironment;
28 import javax.lang.model.element.TypeElement;
29 import javax.lang.model.type.DeclaredType;
30 import javax.lang.model.type.TypeKind;
31 import javax.lang.model.type.TypeMirror;
32 import javax.lang.model.util.Elements;
33 import javax.lang.model.util.Types;
34 import javax.tools.Diagnostic;
35 
36 public class AnnotationAnalyzer extends ModelAnalyzer {
37 
38     public static final Map<String, TypeKind> PRIMITIVE_TYPES;
39     static {
40         PRIMITIVE_TYPES = new HashMap<>();
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 
AnnotationAnalyzer(ProcessingEnvironment processingEnvironment)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 
get()64     public static AnnotationAnalyzer get() {
65         return (AnnotationAnalyzer) getInstance();
66     }
67 
68     @Override
loadPrimitive(String className)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
findClass(String className, Map<String, String> imports)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 
addDimension(TypeMirror type, int numDimensions)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 
getTypeElement(String className, Map<String, String> imports)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 
splitTemplateParameters(String templateParameters)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
findClass(Class classType)199     public ModelClass findClass(Class classType) {
200         return findClass(classType.getCanonicalName(), null);
201     }
202 
getTypeUtils()203     public Types getTypeUtils() {
204         return mProcessingEnv.getTypeUtils();
205     }
206 
getElementUtils()207     public Elements getElementUtils() {
208         return mProcessingEnv.getElementUtils();
209     }
210 
getProcessingEnv()211     public ProcessingEnvironment getProcessingEnv() {
212         return mProcessingEnv;
213     }
214 
215     @Override
createTypeUtil()216     public TypeUtil createTypeUtil() {
217         return new AnnotationTypeUtil(this);
218     }
219 }
220