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