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 86 protected void setInstance(ModelAnalyzer analyzer) { 87 sAnalyzer = analyzer; 88 } 89 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 ModelClass primitive1 = modelClass1.unbox(); 98 ModelClass primitive2 = modelClass2.unbox(); 99 if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) { 100 return findCommonParentOf(primitive1, primitive2); 101 } 102 } 103 Preconditions.checkNotNull(curr, 104 "must be able to find a common parent for " + modelClass1 + " and " + modelClass2); 105 return curr; 106 107 } 108 109 public abstract ModelClass loadPrimitive(String className); 110 111 public static ModelAnalyzer getInstance() { 112 return sAnalyzer; 113 } 114 115 public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) { 116 if (sAnalyzer != null) { 117 throw new IllegalStateException("processing env is already created, you cannot " 118 + "change class loader after that"); 119 } 120 L.d("setting processing env to %s", processingEnvironment); 121 AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment); 122 sAnalyzer = annotationAnalyzer; 123 } 124 125 /** 126 * Takes a raw className (potentially w/ generics and arrays) and expands definitions using 127 * the import statements. 128 * <p> 129 * For instance, this allows user to define variables 130 * <variable type="User" name="user"/> 131 * if they previously imported User. 132 * <import name="com.example.User"/> 133 */ 134 public String applyImports(String className, Map<String, String> imports) { 135 className = className.trim(); 136 int numDimensions = 0; 137 String generic = null; 138 // handle array 139 while (className.endsWith("[]")) { 140 numDimensions++; 141 className = className.substring(0, className.length() - 2); 142 } 143 // handle generics 144 final int lastCharIndex = className.length() - 1; 145 if ('>' == className.charAt(lastCharIndex)) { 146 // has generic. 147 int open = className.indexOf('<'); 148 if (open == -1) { 149 L.e("un-matching generic syntax for %s", className); 150 return className; 151 } 152 generic = applyImports(className.substring(open + 1, lastCharIndex), imports); 153 className = className.substring(0, open); 154 } 155 int dotIndex = className.indexOf('.'); 156 final String qualifier; 157 final String rest; 158 if (dotIndex == -1) { 159 qualifier = className; 160 rest = null; 161 } else { 162 qualifier = className.substring(0, dotIndex); 163 rest = className.substring(dotIndex); // includes dot 164 } 165 final String expandedQualifier = imports.get(qualifier); 166 String result; 167 if (expandedQualifier != null) { 168 result = rest == null ? expandedQualifier : expandedQualifier + rest; 169 } else { 170 result = className; // no change 171 } 172 // now append back dimension and generics 173 if (generic != null) { 174 result = result + "<" + applyImports(generic, imports) + ">"; 175 } 176 while (numDimensions-- > 0) { 177 result = result + "[]"; 178 } 179 return result; 180 } 181 182 public String getDefaultValue(String className) { 183 if ("int".equals(className)) { 184 return "0"; 185 } 186 if ("short".equals(className)) { 187 return "0"; 188 } 189 if ("long".equals(className)) { 190 return "0L"; 191 } 192 if ("float".equals(className)) { 193 return "0f"; 194 } 195 if ("double".equals(className)) { 196 return "0.0"; 197 } 198 if ("boolean".equals(className)) { 199 return "false"; 200 } 201 if ("char".equals(className)) { 202 return "'\\u0000'"; 203 } 204 if ("byte".equals(className)) { 205 return "0"; 206 } 207 return "null"; 208 } 209 210 public abstract ModelClass findClass(String className, Map<String, String> imports); 211 212 public abstract ModelClass findClass(Class classType); 213 214 public abstract TypeUtil createTypeUtil(); 215 216 ModelClass[] getListTypes() { 217 if (mListTypes == null) { 218 mListTypes = new ModelClass[LIST_CLASS_NAMES.length]; 219 for (int i = 0; i < mListTypes.length; i++) { 220 final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null); 221 if (modelClass != null) { 222 mListTypes[i] = modelClass.erasure(); 223 } 224 } 225 } 226 return mListTypes; 227 } 228 229 public ModelClass getMapType() { 230 if (mMapType == null) { 231 mMapType = loadClassErasure(MAP_CLASS_NAME); 232 } 233 return mMapType; 234 } 235 236 ModelClass getStringType() { 237 if (mStringType == null) { 238 mStringType = findClass(STRING_CLASS_NAME, null); 239 } 240 return mStringType; 241 } 242 243 ModelClass getObjectType() { 244 if (mObjectType == null) { 245 mObjectType = findClass(OBJECT_CLASS_NAME, null); 246 } 247 return mObjectType; 248 } 249 250 ModelClass getObservableType() { 251 if (mObservableType == null) { 252 mObservableType = findClass(OBSERVABLE_CLASS_NAME, null); 253 } 254 return mObservableType; 255 } 256 257 ModelClass getObservableListType() { 258 if (mObservableListType == null) { 259 mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME); 260 } 261 return mObservableListType; 262 } 263 264 ModelClass getObservableMapType() { 265 if (mObservableMapType == null) { 266 mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME); 267 } 268 return mObservableMapType; 269 } 270 271 ModelClass getViewDataBindingType() { 272 if (mViewBindingType == null) { 273 mViewBindingType = findClass(VIEW_DATA_BINDING, null); 274 } 275 return mViewBindingType; 276 } 277 278 protected ModelClass[] getObservableFieldTypes() { 279 if (mObservableFieldTypes == null) { 280 mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length]; 281 for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) { 282 mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]); 283 } 284 } 285 return mObservableFieldTypes; 286 } 287 288 ModelClass getViewStubType() { 289 if (mViewStubType == null) { 290 mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null); 291 } 292 return mViewStubType; 293 } 294 295 private ModelClass loadClassErasure(String className) { 296 return findClass(className, null).erasure(); 297 } 298 } 299