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 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 72 public static AnnotationAnalyzer get() { 73 return (AnnotationAnalyzer) getInstance(); 74 } 75 76 @Override 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 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 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 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 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 207 public ModelClass findClass(Class classType) { 208 return findClass(classType.getCanonicalName(), null); 209 } 210 211 public Types getTypeUtils() { 212 return mProcessingEnv.getTypeUtils(); 213 } 214 215 public Elements getElementUtils() { 216 return mProcessingEnv.getElementUtils(); 217 } 218 219 public ProcessingEnvironment getProcessingEnv() { 220 return mProcessingEnv; 221 } 222 223 @Override 224 public TypeUtil createTypeUtil() { 225 return new AnnotationTypeUtil(this); 226 } 227 } 228