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