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 android.databinding.tool.reflection.ModelAnalyzer; 17import android.databinding.tool.reflection.ModelClass; 18import android.databinding.tool.reflection.SdkUtil; 19import android.databinding.tool.reflection.TypeUtil; 20import android.databinding.tool.util.L; 21 22import java.io.File; 23import java.net.MalformedURLException; 24import java.net.URL; 25import java.net.URLClassLoader; 26import java.util.HashMap; 27import java.util.Map; 28 29public class JavaAnalyzer extends ModelAnalyzer { 30 public static final Map<String, Class> PRIMITIVE_TYPES; 31 static { 32 PRIMITIVE_TYPES = new HashMap<String, Class>(); 33 PRIMITIVE_TYPES.put("boolean", boolean.class); 34 PRIMITIVE_TYPES.put("byte", byte.class); 35 PRIMITIVE_TYPES.put("short", short.class); 36 PRIMITIVE_TYPES.put("char", char.class); 37 PRIMITIVE_TYPES.put("int", int.class); 38 PRIMITIVE_TYPES.put("long", long.class); 39 PRIMITIVE_TYPES.put("float", float.class); 40 PRIMITIVE_TYPES.put("double", double.class); 41 } 42 43 private HashMap<String, JavaClass> mClassCache = new HashMap<String, JavaClass>(); 44 45 private final ClassLoader mClassLoader; 46 47 public JavaAnalyzer(ClassLoader classLoader) { 48 setInstance(this); 49 mClassLoader = classLoader; 50 } 51 52 @Override 53 public JavaClass loadPrimitive(String className) { 54 Class clazz = PRIMITIVE_TYPES.get(className); 55 if (clazz == null) { 56 return null; 57 } else { 58 return new JavaClass(clazz); 59 } 60 } 61 62 @Override 63 protected ModelClass[] getObservableFieldTypes() { 64 return new ModelClass[0]; 65 } 66 67 @Override 68 public ModelClass findClass(String className, Map<String, String> imports) { 69 // TODO handle imports 70 JavaClass loaded = mClassCache.get(className); 71 if (loaded != null) { 72 return loaded; 73 } 74 L.d("trying to load class %s from %s", className, mClassLoader.toString()); 75 loaded = loadPrimitive(className); 76 if (loaded == null) { 77 try { 78 if (className.startsWith("[") && className.contains("L")) { 79 int indexOfL = className.indexOf('L'); 80 JavaClass baseClass = (JavaClass) findClass( 81 className.substring(indexOfL + 1, className.length() - 1), null); 82 String realClassName = className.substring(0, indexOfL + 1) + 83 baseClass.mClass.getCanonicalName() + ';'; 84 loaded = new JavaClass(Class.forName(realClassName, false, mClassLoader)); 85 mClassCache.put(className, loaded); 86 } else { 87 loaded = loadRecursively(className); 88 mClassCache.put(className, loaded); 89 } 90 91 } catch (Throwable t) { 92// L.e(t, "cannot load class " + className); 93 } 94 } 95 // expr visitor may call this to resolve statics. Sometimes, it is OK not to find a class. 96 if (loaded == null) { 97 return null; 98 } 99 L.d("loaded class %s", loaded.mClass.getCanonicalName()); 100 return loaded; 101 } 102 103 @Override 104 public ModelClass findClass(Class classType) { 105 return new JavaClass(classType); 106 } 107 108 @Override 109 public TypeUtil createTypeUtil() { 110 return new JavaTypeUtil(); 111 } 112 113 private JavaClass loadRecursively(String className) throws ClassNotFoundException { 114 try { 115 L.d("recursively checking %s", className); 116 return new JavaClass(mClassLoader.loadClass(className)); 117 } catch (ClassNotFoundException ex) { 118 int lastIndexOfDot = className.lastIndexOf("."); 119 if (lastIndexOfDot == -1) { 120 throw ex; 121 } 122 return loadRecursively(className.substring(0, lastIndexOfDot) + "$" + className 123 .substring(lastIndexOfDot + 1)); 124 } 125 } 126 127 public static void initForTests() { 128 Map<String, String> env = System.getenv(); 129 for (Map.Entry<String, String> entry : env.entrySet()) { 130 L.d("%s %s", entry.getKey(), entry.getValue()); 131 } 132 String androidHome = env.get("ANDROID_HOME"); 133 if (androidHome == null) { 134 throw new IllegalStateException( 135 "you need to have ANDROID_HOME set in your environment" 136 + " to run compiler tests"); 137 } 138 File androidJar = new File(androidHome + "/platforms/android-21/android.jar"); 139 if (!androidJar.exists() || !androidJar.canRead()) { 140 throw new IllegalStateException( 141 "cannot find android jar at " + androidJar.getAbsolutePath()); 142 } 143 // now load android data binding library as well 144 145 try { 146 ClassLoader classLoader = new URLClassLoader(new URL[]{androidJar.toURI().toURL()}, 147 ModelAnalyzer.class.getClassLoader()); 148 new JavaAnalyzer(classLoader); 149 } catch (MalformedURLException e) { 150 throw new RuntimeException("cannot create class loader", e); 151 } 152 153 SdkUtil.initialize(8, new File(androidHome)); 154 } 155} 156