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