ModelAnalyzer.java revision 2611838bffef5a009ca71e3e9e59a93f29b098ed
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 */
16package android.databinding.tool.reflection;
17
18import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
19import android.databinding.tool.util.L;
20import android.databinding.tool.util.Preconditions;
21
22import java.util.Map;
23
24import 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 */
31public 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