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