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.Expr;
     20 import android.databinding.tool.expr.ExprModel;
     21 import android.databinding.tool.processing.ErrorMessages;
     22 import android.databinding.tool.processing.Scope;
     23 import android.databinding.tool.processing.scopes.LocationScopeProvider;
     24 import android.databinding.tool.reflection.ModelAnalyzer;
     25 import android.databinding.tool.reflection.ModelClass;
     26 import android.databinding.tool.store.Location;
     27 import android.databinding.tool.store.ResourceBundle;
     28 import android.databinding.tool.store.SetterStore;
     29 import android.databinding.tool.store.SetterStore.BindingGetterCall;
     30 import android.databinding.tool.util.L;
     31 import android.databinding.tool.util.Preconditions;
     32 
     33 import java.util.ArrayList;
     34 import java.util.HashMap;
     35 import java.util.List;
     36 import java.util.Map;
     37 
     38 public class BindingTarget implements LocationScopeProvider {
     39     List<Binding> mBindings = new ArrayList<Binding>();
     40     List<InverseBinding> mInverseBindings = new ArrayList<InverseBinding>();
     41     ExprModel mModel;
     42     ModelClass mResolvedClass;
     43 
     44     // if this target presents itself in multiple layout files with different view types,
     45     // it receives an interface type and should use it in the getter instead.
     46     ResourceBundle.BindingTargetBundle mBundle;
     47 
     48     public BindingTarget(ResourceBundle.BindingTargetBundle bundle) {
     49         mBundle = bundle;
     50     }
     51 
     52     public boolean isUsed() {
     53         return mBundle.isUsed();
     54     }
     55 
     56     public void addBinding(String name, Expr expr) {
     57         if (SetterStore.get(ModelAnalyzer.getInstance()).isTwoWayEventAttribute(name)) {
     58             L.e(ErrorMessages.TWO_WAY_EVENT_ATTRIBUTE, name);
     59         }
     60         mBindings.add(new Binding(this, name, expr));
     61     }
     62 
     63     public String getInterfaceType() {
     64         return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
     65     }
     66 
     67     public InverseBinding addInverseBinding(String name, Expr expr, String bindingClass) {
     68         expr.assertIsInvertible();
     69         final InverseBinding inverseBinding = new InverseBinding(this, name, expr, bindingClass);
     70         mInverseBindings.add(inverseBinding);
     71         mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
     72                 mModel.twoWayListenerExpr(inverseBinding),
     73                 inverseBinding.getEventSetter()));
     74         return inverseBinding;
     75     }
     76 
     77     public InverseBinding addInverseBinding(String name, BindingGetterCall call) {
     78         final InverseBinding inverseBinding = new InverseBinding(this, name, call);
     79         mInverseBindings.add(inverseBinding);
     80         mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
     81                 mModel.twoWayListenerExpr(inverseBinding)));
     82         return inverseBinding;
     83     }
     84 
     85     @Override
     86     public List<Location> provideScopeLocation() {
     87         return mBundle.provideScopeLocation();
     88     }
     89 
     90     public String getId() {
     91         return mBundle.getId();
     92     }
     93 
     94     public String getTag() {
     95         return mBundle.getTag();
     96     }
     97 
     98     public String getOriginalTag() {
     99         return mBundle.getOriginalTag();
    100     }
    101 
    102     public String getViewClass() {
    103         return mBundle.getFullClassName();
    104     }
    105 
    106     public ModelClass getResolvedType() {
    107         if (mResolvedClass == null) {
    108             if (mBundle.isBinder()) {
    109                 mResolvedClass = ModelAnalyzer.getInstance().
    110                         findClass(mBundle.getInterfaceType(), mModel.getImports());
    111             } else {
    112                 mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
    113                         mModel.getImports());
    114             }
    115         }
    116         return mResolvedClass;
    117     }
    118 
    119     public String getIncludedLayout() {
    120         return mBundle.getIncludedLayout();
    121     }
    122 
    123     public boolean isBinder() {
    124         return getIncludedLayout() != null;
    125     }
    126 
    127     public boolean supportsTag() {
    128         return !SetterStore.get(ModelAnalyzer.getInstance())
    129                 .isUntaggable(mBundle.getFullClassName());
    130     }
    131 
    132     public List<Binding> getBindings() {
    133         return mBindings;
    134     }
    135 
    136     public List<InverseBinding> getInverseBindings() {
    137         return mInverseBindings;
    138     }
    139 
    140     public ExprModel getModel() {
    141         return mModel;
    142     }
    143 
    144     public void setModel(ExprModel model) {
    145         mModel = model;
    146     }
    147 
    148     public void resolveListeners() {
    149         for (Binding binding : mBindings) {
    150             try {
    151                 Scope.enter(binding);
    152                 binding.resolveListeners();
    153             } finally {
    154                 Scope.exit();
    155             }
    156         }
    157     }
    158 
    159     public void resolveCallbackParams() {
    160         for (Binding binding : mBindings) {
    161             try {
    162                 Scope.enter(binding);
    163                 binding.resolveCallbackParams();
    164             } finally {
    165                 Scope.exit();
    166             }
    167         }
    168     }
    169 
    170     public void resolveTwoWayExpressions() {
    171         for (Binding binding : mBindings) {
    172             try {
    173                 Scope.enter(binding);
    174                 binding.resolveTwoWayExpressions();
    175             } finally {
    176                 Scope.exit();
    177             }
    178         }
    179     }
    180 
    181     /**
    182      * Called after BindingTarget is finalized.
    183      * <p>
    184      * We traverse all bindings and ask SetterStore to figure out if any can be combined.
    185      * When N bindings are combined, they are demoted from being a binding expression and a new
    186      * ArgList expression is added as the new binding expression that depends on others.
    187      */
    188     public void resolveMultiSetters() {
    189         L.d("resolving multi setters for %s", getId());
    190         final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
    191         final String[] attributes = new String[mBindings.size()];
    192         final ModelClass[] types = new ModelClass[mBindings.size()];
    193         for (int i = 0; i < mBindings.size(); i ++) {
    194             Binding binding = mBindings.get(i);
    195             try {
    196                 Scope.enter(binding);
    197                 attributes[i] = binding.getName();
    198                 types[i] = binding.getExpr().getResolvedType();
    199             } finally {
    200                 Scope.exit();
    201             }
    202         }
    203         final List<SetterStore.MultiAttributeSetter> multiAttributeSetterCalls = setterStore
    204                 .getMultiAttributeSetterCalls(attributes, getResolvedType(), types);
    205         if (multiAttributeSetterCalls.isEmpty()) {
    206             return;
    207         }
    208         final Map<String, Binding> lookup = new HashMap<String, Binding>();
    209         for (Binding binding : mBindings) {
    210             String name = binding.getName();
    211             if (name.startsWith("android:")) {
    212                 lookup.put(name, binding);
    213             } else {
    214                 int ind = name.indexOf(":");
    215                 if (ind == -1) {
    216                     lookup.put(name, binding);
    217                 } else {
    218                     lookup.put(name.substring(ind + 1), binding);
    219                 }
    220             }
    221         }
    222         List<MergedBinding> mergeBindings = new ArrayList<MergedBinding>();
    223         for (final SetterStore.MultiAttributeSetter setter : multiAttributeSetterCalls) {
    224             L.d("resolved %s", setter);
    225             final List<Binding> mergedBindings = new ArrayList<Binding>();
    226             for (String attribute : setter.attributes) {
    227                 Binding binding = lookup.get(attribute);
    228                 Preconditions.checkNotNull(binding, "cannot find binding for %s", attribute);
    229                 mergedBindings.add(binding);
    230             }
    231 
    232             for (Binding binding : mergedBindings) {
    233                 binding.getExpr().setBindingExpression(false);
    234                 mBindings.remove(binding);
    235             }
    236             MergedBinding mergedBinding = new MergedBinding(getModel(), setter, this,
    237                     mergedBindings);
    238             mergeBindings.add(mergedBinding);
    239         }
    240         for (MergedBinding binding : mergeBindings) {
    241             mBindings.add(binding);
    242         }
    243     }
    244 }
    245