LayoutBinder.java revision 7c1b078ca84336caba7f811709836562bd5550d6
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.WriterPackage; 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 = WriterPackage.getFieldName(first); 48 final String fieldName2 = WriterPackage.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 bindingTarget.addBinding(bindingBundle.getName(), 203 parse(bindingBundle.getExpr(), bindingBundle.getValueLocation())); 204 } 205 bindingTarget.resolveMultiSetters(); 206 } finally { 207 Scope.exit(); 208 } 209 } 210 mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets); 211 Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME); 212 } finally { 213 Scope.exit(); 214 } 215 } 216 217 public void resolveWhichExpressionsAreUsed() { 218 List<Expr> used = new ArrayList<Expr>(); 219 for (BindingTarget target : mBindingTargets) { 220 for (Binding binding : target.getBindings()) { 221 binding.getExpr().setIsUsed(true); 222 used.add(binding.getExpr()); 223 } 224 } 225 while (!used.isEmpty()) { 226 Expr e = used.remove(used.size() - 1); 227 for (Dependency dep : e.getDependencies()) { 228 if (!dep.getOther().isUsed()) { 229 used.add(dep.getOther()); 230 dep.getOther().setIsUsed(true); 231 } 232 } 233 } 234 } 235 236 public IdentifierExpr addVariable(String name, String type, Location location, 237 boolean declared) { 238 Preconditions.check(!mUserDefinedVariables.containsKey(name), 239 "%s has already been defined as %s", name, type); 240 final IdentifierExpr id = mExprModel.identifier(name); 241 id.setUserDefinedType(type); 242 id.enableDirectInvalidation(); 243 if (location != null) { 244 id.addLocation(location); 245 } 246 mUserDefinedVariables.put(name, type); 247 if (declared) { 248 id.setDeclared(); 249 } 250 return id; 251 } 252 253 public HashMap<String, String> getUserDefinedVariables() { 254 return mUserDefinedVariables; 255 } 256 257 public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) { 258 final BindingTarget target = new BindingTarget(targetBundle); 259 mBindingTargets.add(target); 260 target.setModel(mExprModel); 261 return target; 262 } 263 264 public Expr parse(String input, @Nullable Location locationInFile) { 265 final Expr parsed = mExpressionParser.parse(input, locationInFile); 266 parsed.setBindingExpression(true); 267 return parsed; 268 } 269 270 public List<BindingTarget> getBindingTargets() { 271 return mBindingTargets; 272 } 273 274 public List<BindingTarget> getSortedTargets() { 275 return mSortedBindingTargets; 276 } 277 278 public boolean isEmpty() { 279 return mExprModel.size() == 0; 280 } 281 282 public ExprModel getModel() { 283 return mExprModel; 284 } 285 286 private void ensureWriter() { 287 if (mWriter == null) { 288 mWriter = new LayoutBinderWriter(this); 289 } 290 } 291 292 public void sealModel() { 293 mExprModel.seal(); 294 } 295 296 public String writeViewBinderBaseClass(boolean forLibrary) { 297 ensureWriter(); 298 return mWriter.writeBaseClass(forLibrary); 299 } 300 301 public String writeViewBinder(int minSdk) { 302 ensureWriter(); 303 Preconditions.checkNotNull(getPackage(), "package cannot be null"); 304 Preconditions.checkNotNull(getClassName(), "base class name cannot be null"); 305 return mWriter.write(minSdk); 306 } 307 308 public String getPackage() { 309 return mBundle.getBindingClassPackage(); 310 } 311 312 public boolean isMerge() { 313 return mBundle.isMerge(); 314 } 315 316 public String getModulePackage() { 317 return mModulePackage; 318 } 319 320 public String getLayoutname() { 321 return mBundle.getFileName(); 322 } 323 324 public String getImplementationName() { 325 if (hasVariations()) { 326 return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl"; 327 } else { 328 return mBundle.getBindingClassName(); 329 } 330 } 331 332 public String getClassName() { 333 return mBundle.getBindingClassName(); 334 } 335 336 public String getTag() { 337 return mBundle.getDirectory() + "/" + mBundle.getFileName(); 338 } 339 340 public boolean hasVariations() { 341 return mBundle.hasVariations(); 342 } 343 344 @Override 345 public String provideScopeFilePath() { 346 return mBundle.getAbsoluteFilePath(); 347 } 348} 349