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