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