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