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