• 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;
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 
setInstance(ModelAnalyzer analyzer)86     protected void setInstance(ModelAnalyzer analyzer) {
87         sAnalyzer = analyzer;
88     }
89 
findCommonParentOf(ModelClass modelClass1, ModelClass modelClass2)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             if (modelClass1.isObject() && modelClass2.isInterface()) {
98                 return modelClass1;
99             } else if (modelClass2.isObject() && modelClass1.isInterface()) {
100                 return modelClass2;
101             }
102 
103             ModelClass primitive1 = modelClass1.unbox();
104             ModelClass primitive2 = modelClass2.unbox();
105             if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) {
106                 return findCommonParentOf(primitive1, primitive2);
107             }
108         }
109         Preconditions.checkNotNull(curr,
110                 "must be able to find a common parent for " + modelClass1 + " and " + modelClass2);
111         return curr;
112 
113     }
114 
loadPrimitive(String className)115     public abstract ModelClass loadPrimitive(String className);
116 
getInstance()117     public static ModelAnalyzer getInstance() {
118         return sAnalyzer;
119     }
120 
setProcessingEnvironment(ProcessingEnvironment processingEnvironment)121     public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) {
122         if (sAnalyzer != null) {
123             throw new IllegalStateException("processing env is already created, you cannot "
124                     + "change class loader after that");
125         }
126         L.d("setting processing env to %s", processingEnvironment);
127         AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment);
128         sAnalyzer = annotationAnalyzer;
129     }
130 
131     /**
132      * Takes a raw className (potentially w/ generics and arrays) and expands definitions using
133      * the import statements.
134      * <p>
135      * For instance, this allows user to define variables
136      * <variable type="User" name="user"/>
137      * if they previously imported User.
138      * <import name="com.example.User"/>
139      */
applyImports(String className, Map<String, String> imports)140     public String applyImports(String className, Map<String, String> imports) {
141         className = className.trim();
142         int numDimensions = 0;
143         String generic = null;
144         // handle array
145         while (className.endsWith("[]")) {
146             numDimensions++;
147             className = className.substring(0, className.length() - 2);
148         }
149         // handle generics
150         final int lastCharIndex = className.length() - 1;
151         if ('>' == className.charAt(lastCharIndex)) {
152             // has generic.
153             int open = className.indexOf('<');
154             if (open == -1) {
155                 L.e("un-matching generic syntax for %s", className);
156                 return className;
157             }
158             generic = applyImports(className.substring(open + 1, lastCharIndex), imports);
159             className = className.substring(0, open);
160         }
161         int dotIndex = className.indexOf('.');
162         final String qualifier;
163         final String rest;
164         if (dotIndex == -1) {
165             qualifier = className;
166             rest = null;
167         } else {
168             qualifier = className.substring(0, dotIndex);
169             rest = className.substring(dotIndex); // includes dot
170         }
171         final String expandedQualifier = imports.get(qualifier);
172         String result;
173         if (expandedQualifier != null) {
174             result = rest == null ? expandedQualifier : expandedQualifier + rest;
175         } else {
176             result = className; // no change
177         }
178         // now append back dimension and generics
179         if (generic != null) {
180             result = result + "<" + applyImports(generic, imports) + ">";
181         }
182         while (numDimensions-- > 0) {
183             result = result + "[]";
184         }
185         return result;
186     }
187 
getDefaultValue(String className)188     public String getDefaultValue(String className) {
189         if ("int".equals(className)) {
190             return "0";
191         }
192         if ("short".equals(className)) {
193             return "0";
194         }
195         if ("long".equals(className)) {
196             return "0L";
197         }
198         if ("float".equals(className)) {
199             return "0f";
200         }
201         if ("double".equals(className)) {
202             return "0.0";
203         }
204         if ("boolean".equals(className)) {
205             return "false";
206         }
207         if ("char".equals(className)) {
208             return "'\\u0000'";
209         }
210         if ("byte".equals(className)) {
211             return "0";
212         }
213         return "null";
214     }
215 
findClass(String className, Map<String, String> imports)216     public abstract ModelClass findClass(String className, Map<String, String> imports);
217 
findClass(Class classType)218     public abstract ModelClass findClass(Class classType);
219 
createTypeUtil()220     public abstract TypeUtil createTypeUtil();
221 
getListTypes()222     ModelClass[] getListTypes() {
223         if (mListTypes == null) {
224             mListTypes = new ModelClass[LIST_CLASS_NAMES.length];
225             for (int i = 0; i < mListTypes.length; i++) {
226                 final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null);
227                 if (modelClass != null) {
228                     mListTypes[i] = modelClass.erasure();
229                 }
230             }
231         }
232         return mListTypes;
233     }
234 
getMapType()235     public ModelClass getMapType() {
236         if (mMapType == null) {
237             mMapType = loadClassErasure(MAP_CLASS_NAME);
238         }
239         return mMapType;
240     }
241 
getStringType()242     ModelClass getStringType() {
243         if (mStringType == null) {
244             mStringType = findClass(STRING_CLASS_NAME, null);
245         }
246         return mStringType;
247     }
248 
getObjectType()249     ModelClass getObjectType() {
250         if (mObjectType == null) {
251             mObjectType = findClass(OBJECT_CLASS_NAME, null);
252         }
253         return mObjectType;
254     }
255 
getObservableType()256     ModelClass getObservableType() {
257         if (mObservableType == null) {
258             mObservableType = findClass(OBSERVABLE_CLASS_NAME, null);
259         }
260         return mObservableType;
261     }
262 
getObservableListType()263     ModelClass getObservableListType() {
264         if (mObservableListType == null) {
265             mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME);
266         }
267         return mObservableListType;
268     }
269 
getObservableMapType()270     ModelClass getObservableMapType() {
271         if (mObservableMapType == null) {
272             mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME);
273         }
274         return mObservableMapType;
275     }
276 
getViewDataBindingType()277     ModelClass getViewDataBindingType() {
278         if (mViewBindingType == null) {
279             mViewBindingType = findClass(VIEW_DATA_BINDING, null);
280         }
281         return mViewBindingType;
282     }
283 
getObservableFieldTypes()284     protected ModelClass[] getObservableFieldTypes() {
285         if (mObservableFieldTypes == null) {
286             mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length];
287             for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
288                 mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]);
289             }
290         }
291         return mObservableFieldTypes;
292     }
293 
getViewStubType()294     ModelClass getViewStubType() {
295         if (mViewStubType == null) {
296             mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
297         }
298         return mViewStubType;
299     }
300 
loadClassErasure(String className)301     private ModelClass loadClassErasure(String className) {
302         return findClass(className, null).erasure();
303     }
304 }
305