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