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 android.databinding.tool.reflection.ModelAnalyzer; 19import android.databinding.tool.reflection.ModelClass; 20import android.databinding.tool.reflection.TypeUtil; 21import android.databinding.tool.util.L; 22 23import java.util.ArrayList; 24import java.util.HashMap; 25import java.util.Map; 26 27import javax.annotation.processing.Messager; 28import javax.annotation.processing.ProcessingEnvironment; 29import javax.lang.model.element.Element; 30import javax.lang.model.element.TypeElement; 31import javax.lang.model.type.DeclaredType; 32import javax.lang.model.type.TypeKind; 33import javax.lang.model.type.TypeMirror; 34import javax.lang.model.util.Elements; 35import javax.lang.model.util.Types; 36import javax.tools.Diagnostic; 37 38public 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 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 71 public static AnnotationAnalyzer get() { 72 return (AnnotationAnalyzer) getInstance(); 73 } 74 75 @Override 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 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 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 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 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 210 public ModelClass findClass(Class classType) { 211 return findClass(classType.getCanonicalName(), null); 212 } 213 214 public Types getTypeUtils() { 215 return mProcessingEnv.getTypeUtils(); 216 } 217 218 public Elements getElementUtils() { 219 return mProcessingEnv.getElementUtils(); 220 } 221 222 public ProcessingEnvironment getProcessingEnv() { 223 return mProcessingEnv; 224 } 225 226 @Override 227 public TypeUtil createTypeUtil() { 228 return new AnnotationTypeUtil(this); 229 } 230} 231