JavaAnalyzer.java revision fead9ca09b117136b35bc5bf137340a754f9eddd
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14package android.databinding.tool.reflection.java;
15
16import com.google.common.collect.ImmutableMap;
17
18import android.databinding.tool.reflection.ModelAnalyzer;
19import android.databinding.tool.reflection.ModelClass;
20import android.databinding.tool.reflection.SdkUtil;
21import android.databinding.tool.reflection.TypeUtil;
22import android.databinding.tool.util.L;
23
24import java.io.File;
25import java.net.MalformedURLException;
26import java.net.URL;
27import java.net.URLClassLoader;
28import java.util.HashMap;
29import java.util.Map;
30
31public class JavaAnalyzer extends ModelAnalyzer {
32    public static final Map<String, Class> PRIMITIVE_TYPES =
33            new ImmutableMap.Builder<String, Class>()
34                    .put("boolean", boolean.class)
35                    .put("byte", byte.class)
36                    .put("short", short.class)
37                    .put("char", char.class)
38                    .put("int", int.class)
39                    .put("long", long.class)
40                    .put("float", float.class)
41                    .put("double", double.class)
42                    .build();
43
44    private HashMap<String, JavaClass> mClassCache = new HashMap<String, JavaClass>();
45
46    private final ClassLoader mClassLoader;
47
48    public JavaAnalyzer(ClassLoader classLoader) {
49        setInstance(this);
50        mClassLoader = classLoader;
51    }
52
53    @Override
54    public JavaClass loadPrimitive(String className) {
55        Class clazz = PRIMITIVE_TYPES.get(className);
56        if (clazz == null) {
57            return null;
58        } else {
59            return new JavaClass(clazz);
60        }
61    }
62
63    @Override
64    public ModelClass findClass(String className, Map<String, String> imports) {
65        // TODO handle imports
66        JavaClass loaded = mClassCache.get(className);
67        if (loaded != null) {
68            return loaded;
69        }
70        L.d("trying to load class %s from %s", className, mClassLoader.toString());
71        loaded = loadPrimitive(className);
72        if (loaded == null) {
73            try {
74                if (className.startsWith("[") && className.contains("L")) {
75                    int indexOfL = className.indexOf('L');
76                    JavaClass baseClass = (JavaClass) findClass(
77                            className.substring(indexOfL + 1, className.length() - 1), null);
78                    String realClassName = className.substring(0, indexOfL + 1) +
79                            baseClass.mClass.getCanonicalName() + ';';
80                    loaded = new JavaClass(Class.forName(realClassName, false, mClassLoader));
81                    mClassCache.put(className, loaded);
82                } else {
83                    loaded = loadRecursively(className);
84                    mClassCache.put(className, loaded);
85                }
86
87            } catch (Throwable t) {
88//                L.e(t, "cannot load class " + className);
89            }
90        }
91        // expr visitor may call this to resolve statics. Sometimes, it is OK not to find a class.
92        if (loaded == null) {
93            return null;
94        }
95        L.d("loaded class %s", loaded.mClass.getCanonicalName());
96        return loaded;
97    }
98
99    @Override
100    public ModelClass findClass(Class classType) {
101        return new JavaClass(classType);
102    }
103
104    @Override
105    public TypeUtil createTypeUtil() {
106        return new JavaTypeUtil();
107    }
108
109    private JavaClass loadRecursively(String className) throws ClassNotFoundException {
110        try {
111            L.d("recursively checking %s", className);
112            return new JavaClass(mClassLoader.loadClass(className));
113        } catch (ClassNotFoundException ex) {
114            int lastIndexOfDot = className.lastIndexOf(".");
115            if (lastIndexOfDot == -1) {
116                throw ex;
117            }
118            return loadRecursively(className.substring(0, lastIndexOfDot) + "$" + className
119                    .substring(lastIndexOfDot + 1));
120        }
121    }
122
123    public static void initForTests() {
124        Map<String, String> env = System.getenv();
125        for (Map.Entry<String, String> entry : env.entrySet()) {
126            L.d("%s %s", entry.getKey(), entry.getValue());
127        }
128        String androidHome = env.get("ANDROID_HOME");
129        if (androidHome == null) {
130            throw new IllegalStateException(
131                    "you need to have ANDROID_HOME set in your environment"
132                            + " to run compiler tests");
133        }
134        File androidJar = new File(androidHome + "/platforms/android-21/android.jar");
135        if (!androidJar.exists() || !androidJar.canRead()) {
136            throw new IllegalStateException(
137                    "cannot find android jar at " + androidJar.getAbsolutePath());
138        }
139        // now load android data binding library as well
140
141        try {
142            ClassLoader classLoader = new URLClassLoader(new URL[]{androidJar.toURI().toURL()},
143                    ModelAnalyzer.class.getClassLoader());
144            new JavaAnalyzer(classLoader);
145        } catch (MalformedURLException e) {
146            throw new RuntimeException("cannot create class loader", e);
147        }
148
149        SdkUtil.initialize(8, new File(androidHome));
150    }
151}
152