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 17package android.databinding.tool; 18 19import android.databinding.tool.expr.Dependency; 20import android.databinding.tool.expr.Expr; 21import android.databinding.tool.expr.ExprModel; 22import android.databinding.tool.expr.IdentifierExpr; 23import android.databinding.tool.processing.Scope; 24import android.databinding.tool.processing.scopes.FileScopeProvider; 25import android.databinding.tool.store.Location; 26import android.databinding.tool.store.ResourceBundle; 27import android.databinding.tool.store.ResourceBundle.BindingTargetBundle; 28import android.databinding.tool.util.L; 29import android.databinding.tool.util.Preconditions; 30import android.databinding.tool.writer.LayoutBinderWriter; 31import android.databinding.tool.writer.LayoutBinderWriterKt; 32 33import com.android.annotations.Nullable; 34 35import java.util.ArrayList; 36import java.util.Collections; 37import java.util.Comparator; 38import java.util.HashMap; 39import java.util.HashSet; 40import java.util.List; 41 42/** 43 * Keeps all information about the bindings per layout file 44 */ 45public class LayoutBinder implements FileScopeProvider { 46 private static final Comparator<BindingTarget> COMPARE_FIELD_NAME = new Comparator<BindingTarget>() { 47 @Override 48 public int compare(BindingTarget first, BindingTarget second) { 49 final String fieldName1 = LayoutBinderWriterKt.getFieldName(first); 50 final String fieldName2 = LayoutBinderWriterKt.getFieldName(second); 51 return fieldName1.compareTo(fieldName2); 52 } 53 }; 54 55 /* 56 * val pkg: String, val projectPackage: String, val baseClassName: String, 57 val layoutName:String, val lb: LayoutExprBinding*/ 58 private final ExprModel mExprModel; 59 private final ExpressionParser mExpressionParser; 60 private final List<BindingTarget> mBindingTargets; 61 private final List<BindingTarget> mSortedBindingTargets; 62 private String mModulePackage; 63 private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>(); 64 65 private LayoutBinderWriter mWriter; 66 private ResourceBundle.LayoutFileBundle mBundle; 67 private static final String[] sJavaLangClasses = { 68 "Deprecated", 69 "Override", 70 "SafeVarargs", 71 "SuppressWarnings", 72 "Appendable", 73 "AutoCloseable", 74 "CharSequence", 75 "Cloneable", 76 "Comparable", 77 "Iterable", 78 "Readable", 79 "Runnable", 80 "Thread.UncaughtExceptionHandler", 81 "Boolean", 82 "Byte", 83 "Character", 84 "Character.Subset", 85 "Character.UnicodeBlock", 86 "Class", 87 "ClassLoader", 88 "Compiler", 89 "Double", 90 "Enum", 91 "Float", 92 "InheritableThreadLocal", 93 "Integer", 94 "Long", 95 "Math", 96 "Number", 97 "Object", 98 "Package", 99 "Process", 100 "ProcessBuilder", 101 "Runtime", 102 "RuntimePermission", 103 "SecurityManager", 104 "Short", 105 "StackTraceElement", 106 "StrictMath", 107 "String", 108 "StringBuffer", 109 "StringBuilder", 110 "System", 111 "Thread", 112 "ThreadGroup", 113 "ThreadLocal", 114 "Throwable", 115 "Void", 116 "Thread.State", 117 "ArithmeticException", 118 "ArrayIndexOutOfBoundsException", 119 "ArrayStoreException", 120 "ClassCastException", 121 "ClassNotFoundException", 122 "CloneNotSupportedException", 123 "EnumConstantNotPresentException", 124 "Exception", 125 "IllegalAccessException", 126 "IllegalArgumentException", 127 "IllegalMonitorStateException", 128 "IllegalStateException", 129 "IllegalThreadStateException", 130 "IndexOutOfBoundsException", 131 "InstantiationException", 132 "InterruptedException", 133 "NegativeArraySizeException", 134 "NoSuchFieldException", 135 "NoSuchMethodException", 136 "NullPointerException", 137 "NumberFormatException", 138 "ReflectiveOperationException", 139 "RuntimeException", 140 "SecurityException", 141 "StringIndexOutOfBoundsException", 142 "TypeNotPresentException", 143 "UnsupportedOperationException", 144 "AbstractMethodError", 145 "AssertionError", 146 "ClassCircularityError", 147 "ClassFormatError", 148 "Error", 149 "ExceptionInInitializerError", 150 "IllegalAccessError", 151 "IncompatibleClassChangeError", 152 "InstantiationError", 153 "InternalError", 154 "LinkageError", 155 "NoClassDefFoundError", 156 "NoSuchFieldError", 157 "NoSuchMethodError", 158 "OutOfMemoryError", 159 "StackOverflowError", 160 "ThreadDeath", 161 "UnknownError", 162 "UnsatisfiedLinkError", 163 "UnsupportedClassVersionError", 164 "VerifyError", 165 "VirtualMachineError", 166 }; 167 168 public LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle) { 169 try { 170 Scope.enter(this); 171 mExprModel = new ExprModel(); 172 mExpressionParser = new ExpressionParser(mExprModel); 173 mBindingTargets = new ArrayList<BindingTarget>(); 174 mBundle = layoutBundle; 175 mModulePackage = layoutBundle.getModulePackage(); 176 HashSet<String> names = new HashSet<String>(); 177 // copy over data. 178 for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) { 179 addVariable(variable.name, variable.type, variable.location, variable.declared); 180 names.add(variable.name); 181 } 182 183 for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) { 184 mExprModel.addImport(userImport.name, userImport.type, userImport.location); 185 names.add(userImport.name); 186 } 187 if (!names.contains("context")) { 188 mExprModel.builtInVariable("context", "android.content.Context", 189 "getRoot().getContext()"); 190 names.add("context"); 191 } 192 for (String javaLangClass : sJavaLangClasses) { 193 mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null); 194 } 195 // First resolve all the View fields 196 // Ensure there are no conflicts with variable names 197 for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) { 198 try { 199 Scope.enter(targetBundle); 200 final BindingTarget bindingTarget = createBindingTarget(targetBundle); 201 if (bindingTarget.getId() != null) { 202 final String fieldName = LayoutBinderWriterKt. 203 getReadableName(bindingTarget); 204 if (names.contains(fieldName)) { 205 L.w("View field %s collides with a variable or import", fieldName); 206 } else { 207 names.add(fieldName); 208 mExprModel.viewFieldExpr(bindingTarget); 209 } 210 } 211 } finally { 212 Scope.exit(); 213 } 214 } 215 216 for (BindingTarget bindingTarget : mBindingTargets) { 217 try { 218 Scope.enter(bindingTarget.mBundle); 219 final String className = getPackage() + "." + getClassName(); 220 for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle 221 .getBindingBundleList()) { 222 try { 223 Scope.enter(bindingBundle.getValueLocation()); 224 Expr expr = parse(bindingBundle.getExpr(), 225 bindingBundle.getValueLocation(), 226 bindingTarget); 227 bindingTarget.addBinding(bindingBundle.getName(), expr); 228 if (bindingBundle.isTwoWay()) { 229 bindingTarget.addInverseBinding(bindingBundle.getName(), expr, 230 className); 231 } 232 } finally { 233 Scope.exit(); 234 } 235 } 236 // resolve callbacks first because they introduce local variables. 237 bindingTarget.resolveCallbackParams(); 238 bindingTarget.resolveTwoWayExpressions(); 239 bindingTarget.resolveMultiSetters(); 240 bindingTarget.resolveListeners(); 241 } finally { 242 Scope.exit(); 243 } 244 } 245 mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets); 246 Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME); 247 } finally { 248 Scope.exit(); 249 } 250 } 251 252 public void resolveWhichExpressionsAreUsed() { 253 List<Expr> used = new ArrayList<Expr>(); 254 for (BindingTarget target : mBindingTargets) { 255 for (Binding binding : target.getBindings()) { 256 binding.getExpr().markAsUsed(); 257 used.add(binding.getExpr()); 258 } 259 } 260 while (!used.isEmpty()) { 261 Expr e = used.remove(used.size() - 1); 262 for (Dependency dep : e.getDependencies()) { 263 if (!dep.getOther().isUsed()) { 264 used.add(dep.getOther()); 265 dep.getOther().markAsUsed(); 266 } 267 } 268 } 269 } 270 271 public IdentifierExpr addVariable(String name, String type, Location location, 272 boolean declared) { 273 Preconditions.check(!mUserDefinedVariables.containsKey(name), 274 "%s has already been defined as %s", name, type); 275 final IdentifierExpr id = mExprModel.identifier(name); 276 id.setUserDefinedType(type); 277 id.enableDirectInvalidation(); 278 if (location != null) { 279 id.addLocation(location); 280 } 281 mUserDefinedVariables.put(name, type); 282 if (declared) { 283 id.setDeclared(); 284 } 285 return id; 286 } 287 288 public HashMap<String, String> getUserDefinedVariables() { 289 return mUserDefinedVariables; 290 } 291 292 public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) { 293 final BindingTarget target = new BindingTarget(targetBundle); 294 mBindingTargets.add(target); 295 target.setModel(mExprModel); 296 return target; 297 } 298 299 public Expr parse(String input, @Nullable Location locationInFile, BindingTarget target) { 300 final Expr parsed = mExpressionParser.parse(input, locationInFile, target); 301 parsed.setBindingExpression(true); 302 return parsed; 303 } 304 305 public List<BindingTarget> getBindingTargets() { 306 return mBindingTargets; 307 } 308 309 public List<BindingTarget> getSortedTargets() { 310 return mSortedBindingTargets; 311 } 312 313 public boolean isEmpty() { 314 return mExprModel.size() == 0; 315 } 316 317 public ExprModel getModel() { 318 return mExprModel; 319 } 320 321 private void ensureWriter() { 322 if (mWriter == null) { 323 mWriter = new LayoutBinderWriter(this); 324 } 325 } 326 327 public void sealModel() { 328 mExprModel.seal(); 329 } 330 331 public String writeViewBinderBaseClass(boolean forLibrary) { 332 ensureWriter(); 333 return mWriter.writeBaseClass(forLibrary); 334 } 335 336 public String writeViewBinder(int minSdk) { 337 ensureWriter(); 338 Preconditions.checkNotNull(getPackage(), "package cannot be null"); 339 Preconditions.checkNotNull(getClassName(), "base class name cannot be null"); 340 return mWriter.write(minSdk); 341 } 342 343 public String getPackage() { 344 return mBundle.getBindingClassPackage(); 345 } 346 347 public boolean isMerge() { 348 return mBundle.isMerge(); 349 } 350 351 public String getModulePackage() { 352 return mModulePackage; 353 } 354 355 public String getLayoutname() { 356 return mBundle.getFileName(); 357 } 358 359 public String getImplementationName() { 360 if (hasVariations()) { 361 return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl"; 362 } else { 363 return mBundle.getBindingClassName(); 364 } 365 } 366 367 public String getClassName() { 368 return mBundle.getBindingClassName(); 369 } 370 371 public String getTag() { 372 return mBundle.getDirectory() + "/" + mBundle.getFileName(); 373 } 374 375 public boolean hasVariations() { 376 return mBundle.hasVariations(); 377 } 378 379 @Override 380 public String provideScopeFilePath() { 381 return mBundle.getAbsoluteFilePath(); 382 } 383} 384