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