Home | History | Annotate | Download | only in reflection
      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;
     17 
     18 import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
     19 import android.databinding.tool.util.L;
     20 import android.databinding.tool.util.Preconditions;
     21 
     22 import java.util.Map;
     23 
     24 import javax.annotation.processing.ProcessingEnvironment;
     25 
     26 /**
     27  * This is the base class for several implementations of something that
     28  * acts like a ClassLoader. Different implementations work with the Annotation
     29  * Processor, ClassLoader, and an Android Studio plugin.
     30  */
     31 public abstract class ModelAnalyzer {
     32 
     33     public static final String[] LIST_CLASS_NAMES = {
     34             "java.util.List",
     35             "android.util.SparseArray",
     36             "android.util.SparseBooleanArray",
     37             "android.util.SparseIntArray",
     38             "android.util.SparseLongArray",
     39             "android.util.LongSparseArray",
     40             "android.support.v4.util.LongSparseArray",
     41     };
     42 
     43     public static final String MAP_CLASS_NAME = "java.util.Map";
     44 
     45     public static final String STRING_CLASS_NAME = "java.lang.String";
     46 
     47     public static final String OBJECT_CLASS_NAME = "java.lang.Object";
     48 
     49     public static final String OBSERVABLE_CLASS_NAME = "android.databinding.Observable";
     50 
     51     public static final String OBSERVABLE_LIST_CLASS_NAME = "android.databinding.ObservableList";
     52 
     53     public static final String OBSERVABLE_MAP_CLASS_NAME = "android.databinding.ObservableMap";
     54 
     55     public static final String[] OBSERVABLE_FIELDS = {
     56             "android.databinding.ObservableBoolean",
     57             "android.databinding.ObservableByte",
     58             "android.databinding.ObservableChar",
     59             "android.databinding.ObservableShort",
     60             "android.databinding.ObservableInt",
     61             "android.databinding.ObservableLong",
     62             "android.databinding.ObservableFloat",
     63             "android.databinding.ObservableDouble",
     64             "android.databinding.ObservableField",
     65             "android.databinding.ObservableParcelable",
     66     };
     67 
     68     public static final String VIEW_DATA_BINDING =
     69             "android.databinding.ViewDataBinding";
     70 
     71     public static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub";
     72 
     73     private ModelClass[] mListTypes;
     74     private ModelClass mMapType;
     75     private ModelClass mStringType;
     76     private ModelClass mObjectType;
     77     private ModelClass mObservableType;
     78     private ModelClass mObservableListType;
     79     private ModelClass mObservableMapType;
     80     private ModelClass[] mObservableFieldTypes;
     81     private ModelClass mViewBindingType;
     82     private ModelClass mViewStubType;
     83 
     84     private static ModelAnalyzer sAnalyzer;
     85 
     86     protected void setInstance(ModelAnalyzer analyzer) {
     87         sAnalyzer = analyzer;
     88     }
     89 
     90     public ModelClass findCommonParentOf(ModelClass modelClass1,
     91             ModelClass modelClass2) {
     92         ModelClass curr = modelClass1;
     93         while (curr != null && !curr.isAssignableFrom(modelClass2)) {
     94             curr = curr.getSuperclass();
     95         }
     96         if (curr == null) {
     97             ModelClass primitive1 = modelClass1.unbox();
     98             ModelClass primitive2 = modelClass2.unbox();
     99             if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) {
    100                 return findCommonParentOf(primitive1, primitive2);
    101             }
    102         }
    103         Preconditions.checkNotNull(curr,
    104                 "must be able to find a common parent for " + modelClass1 + " and " + modelClass2);
    105         return curr;
    106 
    107     }
    108 
    109     public abstract ModelClass loadPrimitive(String className);
    110 
    111     public static ModelAnalyzer getInstance() {
    112         return sAnalyzer;
    113     }
    114 
    115     public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) {
    116         if (sAnalyzer != null) {
    117             throw new IllegalStateException("processing env is already created, you cannot "
    118                     + "change class loader after that");
    119         }
    120         L.d("setting processing env to %s", processingEnvironment);
    121         AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment);
    122         sAnalyzer = annotationAnalyzer;
    123     }
    124 
    125     /**
    126      * Takes a raw className (potentially w/ generics and arrays) and expands definitions using
    127      * the import statements.
    128      * <p>
    129      * For instance, this allows user to define variables
    130      * <variable type="User" name="user"/>
    131      * if they previously imported User.
    132      * <import name="com.example.User"/>
    133      */
    134     public String applyImports(String className, Map<String, String> imports) {
    135         className = className.trim();
    136         int numDimensions = 0;
    137         String generic = null;
    138         // handle array
    139         while (className.endsWith("[]")) {
    140             numDimensions++;
    141             className = className.substring(0, className.length() - 2);
    142         }
    143         // handle generics
    144         final int lastCharIndex = className.length() - 1;
    145         if ('>' == className.charAt(lastCharIndex)) {
    146             // has generic.
    147             int open = className.indexOf('<');
    148             if (open == -1) {
    149                 L.e("un-matching generic syntax for %s", className);
    150                 return className;
    151             }
    152             generic = applyImports(className.substring(open + 1, lastCharIndex), imports);
    153             className = className.substring(0, open);
    154         }
    155         int dotIndex = className.indexOf('.');
    156         final String qualifier;
    157         final String rest;
    158         if (dotIndex == -1) {
    159             qualifier = className;
    160             rest = null;
    161         } else {
    162             qualifier = className.substring(0, dotIndex);
    163             rest = className.substring(dotIndex); // includes dot
    164         }
    165         final String expandedQualifier = imports.get(qualifier);
    166         String result;
    167         if (expandedQualifier != null) {
    168             result = rest == null ? expandedQualifier : expandedQualifier + rest;
    169         } else {
    170             result = className; // no change
    171         }
    172         // now append back dimension and generics
    173         if (generic != null) {
    174             result = result + "<" + applyImports(generic, imports) + ">";
    175         }
    176         while (numDimensions-- > 0) {
    177             result = result + "[]";
    178         }
    179         return result;
    180     }
    181 
    182     public String getDefaultValue(String className) {
    183         if ("int".equals(className)) {
    184             return "0";
    185         }
    186         if ("short".equals(className)) {
    187             return "0";
    188         }
    189         if ("long".equals(className)) {
    190             return "0L";
    191         }
    192         if ("float".equals(className)) {
    193             return "0f";
    194         }
    195         if ("double".equals(className)) {
    196             return "0.0";
    197         }
    198         if ("boolean".equals(className)) {
    199             return "false";
    200         }
    201         if ("char".equals(className)) {
    202             return "'\\u0000'";
    203         }
    204         if ("byte".equals(className)) {
    205             return "0";
    206         }
    207         return "null";
    208     }
    209 
    210     public abstract ModelClass findClass(String className, Map<String, String> imports);
    211 
    212     public abstract ModelClass findClass(Class classType);
    213 
    214     public abstract TypeUtil createTypeUtil();
    215 
    216     ModelClass[] getListTypes() {
    217         if (mListTypes == null) {
    218             mListTypes = new ModelClass[LIST_CLASS_NAMES.length];
    219             for (int i = 0; i < mListTypes.length; i++) {
    220                 final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null);
    221                 if (modelClass != null) {
    222                     mListTypes[i] = modelClass.erasure();
    223                 }
    224             }
    225         }
    226         return mListTypes;
    227     }
    228 
    229     public ModelClass getMapType() {
    230         if (mMapType == null) {
    231             mMapType = loadClassErasure(MAP_CLASS_NAME);
    232         }
    233         return mMapType;
    234     }
    235 
    236     ModelClass getStringType() {
    237         if (mStringType == null) {
    238             mStringType = findClass(STRING_CLASS_NAME, null);
    239         }
    240         return mStringType;
    241     }
    242 
    243     ModelClass getObjectType() {
    244         if (mObjectType == null) {
    245             mObjectType = findClass(OBJECT_CLASS_NAME, null);
    246         }
    247         return mObjectType;
    248     }
    249 
    250     ModelClass getObservableType() {
    251         if (mObservableType == null) {
    252             mObservableType = findClass(OBSERVABLE_CLASS_NAME, null);
    253         }
    254         return mObservableType;
    255     }
    256 
    257     ModelClass getObservableListType() {
    258         if (mObservableListType == null) {
    259             mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME);
    260         }
    261         return mObservableListType;
    262     }
    263 
    264     ModelClass getObservableMapType() {
    265         if (mObservableMapType == null) {
    266             mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME);
    267         }
    268         return mObservableMapType;
    269     }
    270 
    271     ModelClass getViewDataBindingType() {
    272         if (mViewBindingType == null) {
    273             mViewBindingType = findClass(VIEW_DATA_BINDING, null);
    274         }
    275         return mViewBindingType;
    276     }
    277 
    278     protected ModelClass[] getObservableFieldTypes() {
    279         if (mObservableFieldTypes == null) {
    280             mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length];
    281             for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
    282                 mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]);
    283             }
    284         }
    285         return mObservableFieldTypes;
    286     }
    287 
    288     ModelClass getViewStubType() {
    289         if (mViewStubType == null) {
    290             mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
    291         }
    292         return mViewStubType;
    293     }
    294 
    295     private ModelClass loadClassErasure(String className) {
    296         return findClass(className, null).erasure();
    297     }
    298 }
    299