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