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