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