197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar/*
297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * you may not use this file except in compliance with the License.
597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * You may obtain a copy of the License at
697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Unless required by applicable law or agreed to in writing, software
897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * See the License for the specific language governing permissions and
1197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * limitations under the License.
1297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar */
1397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
14fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.reflection.java;
1597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
16fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
18fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.SdkUtil;
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.TypeUtil;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
2197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
2297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.io.File;
2397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.net.MalformedURLException;
2497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.net.URL;
2597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.net.URLClassLoader;
2697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.HashMap;
2797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.Map;
2897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
2997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarpublic class JavaAnalyzer extends ModelAnalyzer {
302611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    public static final Map<String, Class> PRIMITIVE_TYPES;
312611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    static {
322611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES = new HashMap<String, Class>();
332611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("boolean", boolean.class);
342611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("byte", byte.class);
352611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("short", short.class);
362611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("char", char.class);
372611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("int", int.class);
382611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("long", long.class);
392611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("float", float.class);
402611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        PRIMITIVE_TYPES.put("double", double.class);
412611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    }
4297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
43895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar    private HashMap<String, JavaClass> mClassCache = new HashMap<String, JavaClass>();
4497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
4597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private final ClassLoader mClassLoader;
4697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
47fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public JavaAnalyzer(ClassLoader classLoader) {
4897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        setInstance(this);
4997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mClassLoader = classLoader;
5097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
5197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
5297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    @Override
5397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public JavaClass loadPrimitive(String className) {
5497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        Class clazz = PRIMITIVE_TYPES.get(className);
5597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (clazz == null) {
5697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return null;
5797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        } else {
5897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return new JavaClass(clazz);
5997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
6097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
6197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
6297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    @Override
63924fa7c597694ebc433fc0379d0015785351d1b7Yigit Boyar    protected ModelClass[] getObservableFieldTypes() {
64924fa7c597694ebc433fc0379d0015785351d1b7Yigit Boyar        return new ModelClass[0];
65924fa7c597694ebc433fc0379d0015785351d1b7Yigit Boyar    }
66924fa7c597694ebc433fc0379d0015785351d1b7Yigit Boyar
67924fa7c597694ebc433fc0379d0015785351d1b7Yigit Boyar    @Override
6897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public ModelClass findClass(String className, Map<String, String> imports) {
6997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        // TODO handle imports
7097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        JavaClass loaded = mClassCache.get(className);
7197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (loaded != null) {
7297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return loaded;
7397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
7497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        L.d("trying to load class %s from %s", className, mClassLoader.toString());
7597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        loaded = loadPrimitive(className);
7697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (loaded == null) {
7797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            try {
7897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                if (className.startsWith("[") && className.contains("L")) {
7997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    int indexOfL = className.indexOf('L');
8097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    JavaClass baseClass = (JavaClass) findClass(
8197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                            className.substring(indexOfL + 1, className.length() - 1), null);
8297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    String realClassName = className.substring(0, indexOfL + 1) +
8397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                            baseClass.mClass.getCanonicalName() + ';';
8497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    loaded = new JavaClass(Class.forName(realClassName, false, mClassLoader));
8597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    mClassCache.put(className, loaded);
8697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                } else {
8797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    loaded = loadRecursively(className);
8897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    mClassCache.put(className, loaded);
8997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                }
9097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
9197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            } catch (Throwable t) {
9297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar//                L.e(t, "cannot load class " + className);
9397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
9497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
9597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        // expr visitor may call this to resolve statics. Sometimes, it is OK not to find a class.
9697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (loaded == null) {
9797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return null;
9897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
9997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        L.d("loaded class %s", loaded.mClass.getCanonicalName());
10097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return loaded;
10197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
10297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
10397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    @Override
10497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public ModelClass findClass(Class classType) {
10597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return new JavaClass(classType);
10697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
10797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
10897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    @Override
10997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public TypeUtil createTypeUtil() {
11097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return new JavaTypeUtil();
11197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
11297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
11397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private JavaClass loadRecursively(String className) throws ClassNotFoundException {
11497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        try {
11597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            L.d("recursively checking %s", className);
11697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return new JavaClass(mClassLoader.loadClass(className));
11797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        } catch (ClassNotFoundException ex) {
11897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            int lastIndexOfDot = className.lastIndexOf(".");
11997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            if (lastIndexOfDot == -1) {
12097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                throw ex;
12197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
12297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return loadRecursively(className.substring(0, lastIndexOfDot) + "$" + className
12397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    .substring(lastIndexOfDot + 1));
12497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
12597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
12697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
12797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static void initForTests() {
12897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        Map<String, String> env = System.getenv();
12997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        for (Map.Entry<String, String> entry : env.entrySet()) {
13097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            L.d("%s %s", entry.getKey(), entry.getValue());
13197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
13297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        String androidHome = env.get("ANDROID_HOME");
13397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (androidHome == null) {
134fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            throw new IllegalStateException(
135fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    "you need to have ANDROID_HOME set in your environment"
136fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                            + " to run compiler tests");
13797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
13897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        File androidJar = new File(androidHome + "/platforms/android-21/android.jar");
13997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (!androidJar.exists() || !androidJar.canRead()) {
14097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            throw new IllegalStateException(
14197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    "cannot find android jar at " + androidJar.getAbsolutePath());
14297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
14397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        // now load android data binding library as well
14497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
14597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        try {
14697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            ClassLoader classLoader = new URLClassLoader(new URL[]{androidJar.toURI().toURL()},
14797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    ModelAnalyzer.class.getClassLoader());
148fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            new JavaAnalyzer(classLoader);
14997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        } catch (MalformedURLException e) {
15097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            throw new RuntimeException("cannot create class loader", e);
15197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
15297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
15397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        SdkUtil.initialize(8, new File(androidHome));
15497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
15597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar}
156