LayoutBinder.java revision 4df4ba38a62b791bbbc25e923efe8d9c2f9a52e9
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 public String writeViewBinder(int minSdk) { 273 mExprModel.seal(this); 274 ensureWriter(); 275 Preconditions.checkNotNull(getPackage(), "package cannot be null"); 276 Preconditions.checkNotNull(getClassName(), "base class name cannot be null"); 277 return mWriter.write(minSdk); 278 } 279 280 public String getPackage() { 281 return mBundle.getBindingClassPackage(); 282 } 283 284 public boolean isMerge() { 285 return mBundle.isMerge(); 286 } 287 288 public String getModulePackage() { 289 return mModulePackage; 290 } 291 292 public String getLayoutname() { 293 return mBundle.getFileName(); 294 } 295 296 public String getImplementationName() { 297 if (hasVariations()) { 298 return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl"; 299 } else { 300 return mBundle.getBindingClassName(); 301 } 302 } 303 304 public String getClassName() { 305 return mBundle.getBindingClassName(); 306 } 307 308 public String getTag() { 309 return mBundle.getDirectory() + "/" + mBundle.getFileName(); 310 } 311 312 public boolean hasVariations() { 313 return mBundle.hasVariations(); 314 } 315 316 @Override 317 public void resolveListeners() { 318 for (BindingTarget target : mBindingTargets) { 319 for (Binding binding : target.getBindings()) { 320 binding.resolveListeners(); 321 } 322 } 323 } 324} 325