• 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             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 
loadPrimitive(String className)109     public abstract ModelClass loadPrimitive(String className);
110 
getInstance()111     public static ModelAnalyzer getInstance() {
112         return sAnalyzer;
113     }
114 
setProcessingEnvironment(ProcessingEnvironment processingEnvironment)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      */
applyImports(String className, Map<String, String> imports)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 
getDefaultValue(String className)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 
findClass(String className, Map<String, String> imports)210     public abstract ModelClass findClass(String className, Map<String, String> imports);
211 
findClass(Class classType)212     public abstract ModelClass findClass(Class classType);
213 
createTypeUtil()214     public abstract TypeUtil createTypeUtil();
215 
getListTypes()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 
getMapType()229     public ModelClass getMapType() {
230         if (mMapType == null) {
231             mMapType = loadClassErasure(MAP_CLASS_NAME);
232         }
233         return mMapType;
234     }
235 
getStringType()236     ModelClass getStringType() {
237         if (mStringType == null) {
238             mStringType = findClass(STRING_CLASS_NAME, null);
239         }
240         return mStringType;
241     }
242 
getObjectType()243     ModelClass getObjectType() {
244         if (mObjectType == null) {
245             mObjectType = findClass(OBJECT_CLASS_NAME, null);
246         }
247         return mObjectType;
248     }
249 
getObservableType()250     ModelClass getObservableType() {
251         if (mObservableType == null) {
252             mObservableType = findClass(OBSERVABLE_CLASS_NAME, null);
253         }
254         return mObservableType;
255     }
256 
getObservableListType()257     ModelClass getObservableListType() {
258         if (mObservableListType == null) {
259             mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME);
260         }
261         return mObservableListType;
262     }
263 
getObservableMapType()264     ModelClass getObservableMapType() {
265         if (mObservableMapType == null) {
266             mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME);
267         }
268         return mObservableMapType;
269     }
270 
getViewDataBindingType()271     ModelClass getViewDataBindingType() {
272         if (mViewBindingType == null) {
273             mViewBindingType = findClass(VIEW_DATA_BINDING, null);
274         }
275         return mViewBindingType;
276     }
277 
getObservableFieldTypes()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 
getViewStubType()288     ModelClass getViewStubType() {
289         if (mViewStubType == null) {
290             mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
291         }
292         return mViewStubType;
293     }
294 
loadClassErasure(String className)295     private ModelClass loadClassErasure(String className) {
296         return findClass(className, null).erasure();
297     }
298 }
299