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 17 package android.databinding.tool; 18 19 import android.databinding.tool.expr.Dependency; 20 import android.databinding.tool.expr.Expr; 21 import android.databinding.tool.expr.ExprModel; 22 import android.databinding.tool.expr.IdentifierExpr; 23 import android.databinding.tool.processing.Scope; 24 import android.databinding.tool.processing.scopes.FileScopeProvider; 25 import android.databinding.tool.store.Location; 26 import android.databinding.tool.store.ResourceBundle; 27 import android.databinding.tool.store.ResourceBundle.BindingTargetBundle; 28 import android.databinding.tool.util.L; 29 import android.databinding.tool.util.Preconditions; 30 import android.databinding.tool.writer.LayoutBinderWriter; 31 import android.databinding.tool.writer.LayoutBinderWriterKt; 32 33 import org.antlr.v4.runtime.misc.Nullable; 34 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.Comparator; 38 import java.util.HashMap; 39 import java.util.HashSet; 40 import java.util.List; 41 42 /** 43 * Keeps all information about the bindings per layout file 44 */ 45 public 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 LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle)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 for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle 220 .getBindingBundleList()) { 221 try { 222 Scope.enter(bindingBundle.getValueLocation()); 223 bindingTarget.addBinding(bindingBundle.getName(), 224 parse(bindingBundle.getExpr(), bindingBundle.isTwoWay(), 225 bindingBundle.getValueLocation())); 226 } finally { 227 Scope.exit(); 228 } 229 } 230 bindingTarget.resolveTwoWayExpressions(); 231 bindingTarget.resolveMultiSetters(); 232 bindingTarget.resolveListeners(); 233 } finally { 234 Scope.exit(); 235 } 236 } 237 mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets); 238 Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME); 239 } finally { 240 Scope.exit(); 241 } 242 } 243 resolveWhichExpressionsAreUsed()244 public void resolveWhichExpressionsAreUsed() { 245 List<Expr> used = new ArrayList<Expr>(); 246 for (BindingTarget target : mBindingTargets) { 247 for (Binding binding : target.getBindings()) { 248 binding.getExpr().setIsUsed(true); 249 used.add(binding.getExpr()); 250 } 251 } 252 while (!used.isEmpty()) { 253 Expr e = used.remove(used.size() - 1); 254 for (Dependency dep : e.getDependencies()) { 255 if (!dep.getOther().isUsed()) { 256 used.add(dep.getOther()); 257 dep.getOther().setIsUsed(true); 258 } 259 } 260 } 261 } 262 addVariable(String name, String type, Location location, boolean declared)263 public IdentifierExpr addVariable(String name, String type, Location location, 264 boolean declared) { 265 Preconditions.check(!mUserDefinedVariables.containsKey(name), 266 "%s has already been defined as %s", name, type); 267 final IdentifierExpr id = mExprModel.identifier(name); 268 id.setUserDefinedType(type); 269 id.enableDirectInvalidation(); 270 if (location != null) { 271 id.addLocation(location); 272 } 273 mUserDefinedVariables.put(name, type); 274 if (declared) { 275 id.setDeclared(); 276 } 277 return id; 278 } 279 getUserDefinedVariables()280 public HashMap<String, String> getUserDefinedVariables() { 281 return mUserDefinedVariables; 282 } 283 createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle)284 public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) { 285 final BindingTarget target = new BindingTarget(targetBundle); 286 mBindingTargets.add(target); 287 target.setModel(mExprModel); 288 return target; 289 } 290 parse(String input, boolean isTwoWay, @Nullable Location locationInFile)291 public Expr parse(String input, boolean isTwoWay, @Nullable Location locationInFile) { 292 final Expr parsed = mExpressionParser.parse(input, locationInFile); 293 parsed.setBindingExpression(true); 294 parsed.setTwoWay(isTwoWay); 295 return parsed; 296 } 297 getBindingTargets()298 public List<BindingTarget> getBindingTargets() { 299 return mBindingTargets; 300 } 301 getSortedTargets()302 public List<BindingTarget> getSortedTargets() { 303 return mSortedBindingTargets; 304 } 305 isEmpty()306 public boolean isEmpty() { 307 return mExprModel.size() == 0; 308 } 309 getModel()310 public ExprModel getModel() { 311 return mExprModel; 312 } 313 ensureWriter()314 private void ensureWriter() { 315 if (mWriter == null) { 316 mWriter = new LayoutBinderWriter(this); 317 } 318 } 319 sealModel()320 public void sealModel() { 321 mExprModel.seal(); 322 } 323 writeViewBinderBaseClass(boolean forLibrary)324 public String writeViewBinderBaseClass(boolean forLibrary) { 325 ensureWriter(); 326 return mWriter.writeBaseClass(forLibrary); 327 } 328 writeViewBinder(int minSdk)329 public String writeViewBinder(int minSdk) { 330 ensureWriter(); 331 Preconditions.checkNotNull(getPackage(), "package cannot be null"); 332 Preconditions.checkNotNull(getClassName(), "base class name cannot be null"); 333 return mWriter.write(minSdk); 334 } 335 getPackage()336 public String getPackage() { 337 return mBundle.getBindingClassPackage(); 338 } 339 isMerge()340 public boolean isMerge() { 341 return mBundle.isMerge(); 342 } 343 getModulePackage()344 public String getModulePackage() { 345 return mModulePackage; 346 } 347 getLayoutname()348 public String getLayoutname() { 349 return mBundle.getFileName(); 350 } 351 getImplementationName()352 public String getImplementationName() { 353 if (hasVariations()) { 354 return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl"; 355 } else { 356 return mBundle.getBindingClassName(); 357 } 358 } 359 getClassName()360 public String getClassName() { 361 return mBundle.getBindingClassName(); 362 } 363 getTag()364 public String getTag() { 365 return mBundle.getDirectory() + "/" + mBundle.getFileName(); 366 } 367 hasVariations()368 public boolean hasVariations() { 369 return mBundle.hasVariations(); 370 } 371 372 @Override provideScopeFilePath()373 public String provideScopeFilePath() { 374 return mBundle.getAbsoluteFilePath(); 375 } 376 } 377