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 */ 16 package android.databinding.tool.reflection; 17 18 import android.databinding.tool.reflection.annotation.AnnotationAnalyzer; 19 import android.databinding.tool.util.L; 20 import android.databinding.tool.util.Preconditions; 21 import android.databinding.tool.util.StringUtils; 22 23 import java.util.HashMap; 24 import java.util.Map; 25 26 import javax.annotation.processing.ProcessingEnvironment; 27 28 /** 29 * This is the base class for several implementations of something that 30 * acts like a ClassLoader. Different implementations work with the Annotation 31 * Processor, ClassLoader, and an Android Studio plugin. 32 */ 33 public abstract class ModelAnalyzer { 34 35 public static final String[] LIST_CLASS_NAMES = { 36 "java.util.List", 37 "android.util.SparseArray", 38 "android.util.SparseBooleanArray", 39 "android.util.SparseIntArray", 40 "android.util.SparseLongArray", 41 "android.util.LongSparseArray", 42 "android.support.v4.util.LongSparseArray", 43 }; 44 45 public static final String MAP_CLASS_NAME = "java.util.Map"; 46 47 public static final String STRING_CLASS_NAME = "java.lang.String"; 48 49 public static final String OBJECT_CLASS_NAME = "java.lang.Object"; 50 51 public static final String OBSERVABLE_CLASS_NAME = "android.databinding.Observable"; 52 53 public static final String OBSERVABLE_LIST_CLASS_NAME = "android.databinding.ObservableList"; 54 55 public static final String OBSERVABLE_MAP_CLASS_NAME = "android.databinding.ObservableMap"; 56 57 public static final String[] OBSERVABLE_FIELDS = { 58 "android.databinding.ObservableBoolean", 59 "android.databinding.ObservableByte", 60 "android.databinding.ObservableChar", 61 "android.databinding.ObservableShort", 62 "android.databinding.ObservableInt", 63 "android.databinding.ObservableLong", 64 "android.databinding.ObservableFloat", 65 "android.databinding.ObservableDouble", 66 "android.databinding.ObservableField", 67 "android.databinding.ObservableParcelable", 68 }; 69 70 public static final String VIEW_DATA_BINDING = 71 "android.databinding.ViewDataBinding"; 72 73 public static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub"; 74 75 private ModelClass[] mListTypes; 76 private ModelClass mMapType; 77 private ModelClass mStringType; 78 private ModelClass mObjectType; 79 private ModelClass mObservableType; 80 private ModelClass mObservableListType; 81 private ModelClass mObservableMapType; 82 private ModelClass[] mObservableFieldTypes; 83 private ModelClass mViewBindingType; 84 private ModelClass mViewStubType; 85 86 private static ModelAnalyzer sAnalyzer; 87 private final Map<String, InjectedClass> mInjectedClasses = 88 new HashMap<String, InjectedClass>(); 89 setInstance(ModelAnalyzer analyzer)90 protected void setInstance(ModelAnalyzer analyzer) { 91 sAnalyzer = analyzer; 92 } 93 findCommonParentOf(ModelClass modelClass1, ModelClass modelClass2)94 public ModelClass findCommonParentOf(ModelClass modelClass1, ModelClass modelClass2) { 95 return findCommonParentOf(modelClass1, modelClass2, true); 96 } 97 findCommonParentOf(ModelClass modelClass1, ModelClass modelClass2, boolean failOnError)98 public ModelClass findCommonParentOf(ModelClass modelClass1, ModelClass modelClass2, 99 boolean failOnError) { 100 ModelClass curr = modelClass1; 101 while (curr != null && !curr.isAssignableFrom(modelClass2)) { 102 curr = curr.getSuperclass(); 103 } 104 if (curr == null) { 105 if (modelClass1.isObject() && modelClass2.isInterface()) { 106 return modelClass1; 107 } else if (modelClass2.isObject() && modelClass1.isInterface()) { 108 return modelClass2; 109 } 110 111 ModelClass primitive1 = modelClass1.unbox(); 112 ModelClass primitive2 = modelClass2.unbox(); 113 if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) { 114 return findCommonParentOf(primitive1, primitive2, failOnError); 115 } 116 } 117 if (failOnError) { 118 Preconditions.checkNotNull(curr, 119 "must be able to find a common parent for " + modelClass1 + " and " 120 + modelClass2); 121 } 122 return curr; 123 } 124 loadPrimitive(String className)125 public abstract ModelClass loadPrimitive(String className); 126 getInstance()127 public static ModelAnalyzer getInstance() { 128 return sAnalyzer; 129 } 130 setProcessingEnvironment(ProcessingEnvironment processingEnvironment)131 public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) { 132 if (sAnalyzer != null) { 133 throw new IllegalStateException("processing env is already created, you cannot " 134 + "change class loader after that"); 135 } 136 L.d("setting processing env to %s", processingEnvironment); 137 sAnalyzer = new AnnotationAnalyzer(processingEnvironment); 138 } 139 140 /** 141 * Takes a raw className (potentially w/ generics and arrays) and expands definitions using 142 * the import statements. 143 * <p> 144 * For instance, this allows user to define variables 145 * <variable type="User" name="user"/> 146 * if they previously imported User. 147 * <import name="com.example.User"/> 148 */ applyImports(String className, Map<String, String> imports)149 public String applyImports(String className, Map<String, String> imports) { 150 className = className.trim(); 151 int numDimensions = 0; 152 String generic = null; 153 // handle array 154 while (className.endsWith("[]")) { 155 numDimensions++; 156 className = className.substring(0, className.length() - 2); 157 } 158 // handle generics 159 final int lastCharIndex = className.length() - 1; 160 if ('>' == className.charAt(lastCharIndex)) { 161 // has generic. 162 int open = className.indexOf('<'); 163 if (open == -1) { 164 L.e("un-matching generic syntax for %s", className); 165 return className; 166 } 167 generic = applyImports(className.substring(open + 1, lastCharIndex), imports); 168 className = className.substring(0, open); 169 } 170 int dotIndex = className.indexOf('.'); 171 final String qualifier; 172 final String rest; 173 if (dotIndex == -1) { 174 qualifier = className; 175 rest = null; 176 } else { 177 qualifier = className.substring(0, dotIndex); 178 rest = className.substring(dotIndex); // includes dot 179 } 180 final String expandedQualifier = imports.get(qualifier); 181 String result; 182 if (expandedQualifier != null) { 183 result = rest == null ? expandedQualifier : expandedQualifier + rest; 184 } else { 185 result = className; // no change 186 } 187 // now append back dimension and generics 188 if (generic != null) { 189 result = result + "<" + applyImports(generic, imports) + ">"; 190 } 191 while (numDimensions-- > 0) { 192 result = result + "[]"; 193 } 194 return result; 195 } 196 getDefaultValue(String className)197 public String getDefaultValue(String className) { 198 if ("int".equals(className)) { 199 return "0"; 200 } 201 if ("short".equals(className)) { 202 return "0"; 203 } 204 if ("long".equals(className)) { 205 return "0L"; 206 } 207 if ("float".equals(className)) { 208 return "0f"; 209 } 210 if ("double".equals(className)) { 211 return "0.0"; 212 } 213 if ("boolean".equals(className)) { 214 return "false"; 215 } 216 if ("char".equals(className)) { 217 return "'\\u0000'"; 218 } 219 if ("byte".equals(className)) { 220 return "0"; 221 } 222 return "null"; 223 } 224 findClass(String className, Map<String, String> imports)225 public final ModelClass findClass(String className, Map<String, String> imports) { 226 if (mInjectedClasses.containsKey(className)) { 227 return mInjectedClasses.get(className); 228 } 229 return findClassInternal(className, imports); 230 } 231 findClassInternal(String className, Map<String, String> imports)232 public abstract ModelClass findClassInternal(String className, Map<String, String> imports); 233 findClass(Class classType)234 public abstract ModelClass findClass(Class classType); 235 createTypeUtil()236 public abstract TypeUtil createTypeUtil(); 237 injectClass(InjectedClass injectedClass)238 public ModelClass injectClass(InjectedClass injectedClass) { 239 mInjectedClasses.put(injectedClass.getCanonicalName(), injectedClass); 240 return injectedClass; 241 } 242 injectViewDataBinding(String className, Map<String, String> variables, Map<String, String> fields)243 public ModelClass injectViewDataBinding(String className, Map<String, String> variables, 244 Map<String, String> fields) { 245 InjectedClass injectedClass = new InjectedClass(className, 246 ModelAnalyzer.VIEW_DATA_BINDING); 247 248 if (fields != null) { 249 for (String name : fields.keySet()) { 250 String type = fields.get(name); 251 injectedClass.addField(new InjectedField(name, type)); 252 } 253 } 254 if (variables != null) { 255 for (String name : variables.keySet()) { 256 String type = variables.get(name); 257 String capName = StringUtils.capitalize(name); 258 String setName = "set" + capName; 259 String getName = "get" + capName; 260 injectedClass.addMethod(new InjectedMethod(injectedClass, false, getName, type)); 261 injectedClass.addMethod(new InjectedMethod(injectedClass, false, setName, "void", 262 type)); 263 } 264 } 265 mInjectedClasses.put(className, injectedClass); 266 return injectedClass; 267 } 268 getListTypes()269 ModelClass[] getListTypes() { 270 if (mListTypes == null) { 271 mListTypes = new ModelClass[LIST_CLASS_NAMES.length]; 272 for (int i = 0; i < mListTypes.length; i++) { 273 final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null); 274 if (modelClass != null) { 275 mListTypes[i] = modelClass.erasure(); 276 } 277 } 278 } 279 return mListTypes; 280 } 281 getMapType()282 public ModelClass getMapType() { 283 if (mMapType == null) { 284 mMapType = loadClassErasure(MAP_CLASS_NAME); 285 } 286 return mMapType; 287 } 288 getStringType()289 ModelClass getStringType() { 290 if (mStringType == null) { 291 mStringType = findClass(STRING_CLASS_NAME, null); 292 } 293 return mStringType; 294 } 295 getObjectType()296 ModelClass getObjectType() { 297 if (mObjectType == null) { 298 mObjectType = findClass(OBJECT_CLASS_NAME, null); 299 } 300 return mObjectType; 301 } 302 getObservableType()303 ModelClass getObservableType() { 304 if (mObservableType == null) { 305 mObservableType = findClass(OBSERVABLE_CLASS_NAME, null); 306 } 307 return mObservableType; 308 } 309 getObservableListType()310 ModelClass getObservableListType() { 311 if (mObservableListType == null) { 312 mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME); 313 } 314 return mObservableListType; 315 } 316 getObservableMapType()317 ModelClass getObservableMapType() { 318 if (mObservableMapType == null) { 319 mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME); 320 } 321 return mObservableMapType; 322 } 323 getViewDataBindingType()324 ModelClass getViewDataBindingType() { 325 if (mViewBindingType == null) { 326 mViewBindingType = findClass(VIEW_DATA_BINDING, null); 327 } 328 return mViewBindingType; 329 } 330 getObservableFieldTypes()331 protected ModelClass[] getObservableFieldTypes() { 332 if (mObservableFieldTypes == null) { 333 mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length]; 334 for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) { 335 mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]); 336 } 337 } 338 return mObservableFieldTypes; 339 } 340 getViewStubType()341 ModelClass getViewStubType() { 342 if (mViewStubType == null) { 343 mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null); 344 } 345 return mViewStubType; 346 } 347 loadClassErasure(String className)348 private ModelClass loadClassErasure(String className) { 349 return findClass(className, null).erasure(); 350 } 351 } 352