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