Home | History | Annotate | Download | only in tool
      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 
     17 package android.databinding.tool;
     18 
     19 import android.databinding.tool.expr.Dependency;
     20 import android.databinding.tool.expr.Expr;
     21 import android.databinding.tool.expr.ExprModel;
     22 import android.databinding.tool.expr.IdentifierExpr;
     23 import android.databinding.tool.processing.Scope;
     24 import android.databinding.tool.processing.scopes.FileScopeProvider;
     25 import android.databinding.tool.store.Location;
     26 import android.databinding.tool.store.ResourceBundle;
     27 import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
     28 import android.databinding.tool.util.L;
     29 import android.databinding.tool.util.Preconditions;
     30 import android.databinding.tool.writer.LayoutBinderWriter;
     31 import android.databinding.tool.writer.LayoutBinderWriterKt;
     32 
     33 import org.antlr.v4.runtime.misc.Nullable;
     34 
     35 import java.util.ArrayList;
     36 import java.util.Collections;
     37 import java.util.Comparator;
     38 import java.util.HashMap;
     39 import java.util.HashSet;
     40 import java.util.List;
     41 
     42 /**
     43  * Keeps all information about the bindings per layout file
     44  */
     45 public class LayoutBinder implements 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 = LayoutBinderWriterKt.getFieldName(first);
     50             final String fieldName2 = LayoutBinderWriterKt.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             HashSet<String> names = new HashSet<String>();
    177             // copy over data.
    178             for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) {
    179                 addVariable(variable.name, variable.type, variable.location, variable.declared);
    180                 names.add(variable.name);
    181             }
    182 
    183             for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
    184                 mExprModel.addImport(userImport.name, userImport.type, userImport.location);
    185                 names.add(userImport.name);
    186             }
    187             if (!names.contains("context")) {
    188                 mExprModel.builtInVariable("context", "android.content.Context",
    189                         "getRoot().getContext()");
    190                 names.add("context");
    191             }
    192             for (String javaLangClass : sJavaLangClasses) {
    193                 mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
    194             }
    195             // First resolve all the View fields
    196             // Ensure there are no conflicts with variable names
    197             for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
    198                 try {
    199                     Scope.enter(targetBundle);
    200                     final BindingTarget bindingTarget = createBindingTarget(targetBundle);
    201                     if (bindingTarget.getId() != null) {
    202                         final String fieldName = LayoutBinderWriterKt.
    203                                 getReadableName(bindingTarget);
    204                         if (names.contains(fieldName)) {
    205                             L.w("View field %s collides with a variable or import", fieldName);
    206                         } else {
    207                             names.add(fieldName);
    208                             mExprModel.viewFieldExpr(bindingTarget);
    209                         }
    210                     }
    211                 } finally {
    212                     Scope.exit();
    213                 }
    214             }
    215 
    216             for (BindingTarget bindingTarget : mBindingTargets) {
    217                 try {
    218                     Scope.enter(bindingTarget.mBundle);
    219                     for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle
    220                             .getBindingBundleList()) {
    221                         try {
    222                             Scope.enter(bindingBundle.getValueLocation());
    223                             bindingTarget.addBinding(bindingBundle.getName(),
    224                                     parse(bindingBundle.getExpr(), bindingBundle.isTwoWay(),
    225                                             bindingBundle.getValueLocation()));
    226                         } finally {
    227                             Scope.exit();
    228                         }
    229                     }
    230                     bindingTarget.resolveTwoWayExpressions();
    231                     bindingTarget.resolveMultiSetters();
    232                     bindingTarget.resolveListeners();
    233                 } finally {
    234                     Scope.exit();
    235                 }
    236             }
    237             mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets);
    238             Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
    239         } finally {
    240             Scope.exit();
    241         }
    242     }
    243 
    244     public void resolveWhichExpressionsAreUsed() {
    245         List<Expr> used = new ArrayList<Expr>();
    246         for (BindingTarget target : mBindingTargets) {
    247             for (Binding binding : target.getBindings()) {
    248                 binding.getExpr().setIsUsed(true);
    249                 used.add(binding.getExpr());
    250             }
    251         }
    252         while (!used.isEmpty()) {
    253             Expr e = used.remove(used.size() - 1);
    254             for (Dependency dep : e.getDependencies()) {
    255                 if (!dep.getOther().isUsed()) {
    256                     used.add(dep.getOther());
    257                     dep.getOther().setIsUsed(true);
    258                 }
    259             }
    260         }
    261     }
    262 
    263     public IdentifierExpr addVariable(String name, String type, Location location,
    264             boolean declared) {
    265         Preconditions.check(!mUserDefinedVariables.containsKey(name),
    266                 "%s has already been defined as %s", name, type);
    267         final IdentifierExpr id = mExprModel.identifier(name);
    268         id.setUserDefinedType(type);
    269         id.enableDirectInvalidation();
    270         if (location != null) {
    271             id.addLocation(location);
    272         }
    273         mUserDefinedVariables.put(name, type);
    274         if (declared) {
    275             id.setDeclared();
    276         }
    277         return id;
    278     }
    279 
    280     public HashMap<String, String> getUserDefinedVariables() {
    281         return mUserDefinedVariables;
    282     }
    283 
    284     public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
    285         final BindingTarget target = new BindingTarget(targetBundle);
    286         mBindingTargets.add(target);
    287         target.setModel(mExprModel);
    288         return target;
    289     }
    290 
    291     public Expr parse(String input, boolean isTwoWay, @Nullable Location locationInFile) {
    292         final Expr parsed = mExpressionParser.parse(input, locationInFile);
    293         parsed.setBindingExpression(true);
    294         parsed.setTwoWay(isTwoWay);
    295         return parsed;
    296     }
    297 
    298     public List<BindingTarget> getBindingTargets() {
    299         return mBindingTargets;
    300     }
    301 
    302     public List<BindingTarget> getSortedTargets() {
    303         return mSortedBindingTargets;
    304     }
    305 
    306     public boolean isEmpty() {
    307         return mExprModel.size() == 0;
    308     }
    309 
    310     public ExprModel getModel() {
    311         return mExprModel;
    312     }
    313 
    314     private void ensureWriter() {
    315         if (mWriter == null) {
    316             mWriter = new LayoutBinderWriter(this);
    317         }
    318     }
    319 
    320     public void sealModel() {
    321         mExprModel.seal();
    322     }
    323 
    324     public String writeViewBinderBaseClass(boolean forLibrary) {
    325         ensureWriter();
    326         return mWriter.writeBaseClass(forLibrary);
    327     }
    328 
    329     public String writeViewBinder(int minSdk) {
    330         ensureWriter();
    331         Preconditions.checkNotNull(getPackage(), "package cannot be null");
    332         Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
    333         return mWriter.write(minSdk);
    334     }
    335 
    336     public String getPackage() {
    337         return mBundle.getBindingClassPackage();
    338     }
    339 
    340     public boolean isMerge() {
    341         return mBundle.isMerge();
    342     }
    343 
    344     public String getModulePackage() {
    345         return mModulePackage;
    346     }
    347 
    348     public String getLayoutname() {
    349         return mBundle.getFileName();
    350     }
    351 
    352     public String getImplementationName() {
    353         if (hasVariations()) {
    354             return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
    355         } else {
    356             return mBundle.getBindingClassName();
    357         }
    358     }
    359 
    360     public String getClassName() {
    361         return mBundle.getBindingClassName();
    362     }
    363 
    364     public String getTag() {
    365         return mBundle.getDirectory() + "/" + mBundle.getFileName();
    366     }
    367 
    368     public boolean hasVariations() {
    369         return mBundle.hasVariations();
    370     }
    371 
    372     @Override
    373     public String provideScopeFilePath() {
    374         return mBundle.getAbsoluteFilePath();
    375     }
    376 }
    377