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