Home | History | Annotate | Download | only in annotation
      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