• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BindingTarget(ResourceBundle.BindingTargetBundle bundle)48     public BindingTarget(ResourceBundle.BindingTargetBundle bundle) {
49         mBundle = bundle;
50     }
51 
isUsed()52     public boolean isUsed() {
53         return mBundle.isUsed();
54     }
55 
addBinding(String name, Expr expr)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         if (expr.isTwoWay()) {
62             try {
63                 Scope.enter(expr);
64                 expr.assertIsInvertible();
65                 final InverseBinding inverseBinding = new InverseBinding(this, name, expr);
66                 mInverseBindings.add(inverseBinding);
67                 mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
68                         mModel.twoWayListenerExpr(inverseBinding),
69                         inverseBinding.getEventSetter()));
70             } finally {
71                 Scope.exit();
72             }
73         }
74     }
75 
getInterfaceType()76     public String getInterfaceType() {
77         return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
78     }
79 
addInverseBinding(String name, BindingGetterCall call)80     public InverseBinding addInverseBinding(String name, BindingGetterCall call) {
81         final InverseBinding inverseBinding = new InverseBinding(this, name, null);
82         inverseBinding.setGetterCall(call);
83         mInverseBindings.add(inverseBinding);
84         mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
85                 mModel.twoWayListenerExpr(inverseBinding)));
86         return inverseBinding;
87     }
88 
89     @Override
provideScopeLocation()90     public List<Location> provideScopeLocation() {
91         return mBundle.provideScopeLocation();
92     }
93 
getId()94     public String getId() {
95         return mBundle.getId();
96     }
97 
getTag()98     public String getTag() {
99         return mBundle.getTag();
100     }
101 
getOriginalTag()102     public String getOriginalTag() {
103         return mBundle.getOriginalTag();
104     }
105 
getViewClass()106     public String getViewClass() {
107         return mBundle.getFullClassName();
108     }
109 
getResolvedType()110     public ModelClass getResolvedType() {
111         if (mResolvedClass == null) {
112             if (mBundle.isBinder()) {
113                 mResolvedClass = ModelAnalyzer.getInstance().
114                         findClass(ModelAnalyzer.VIEW_DATA_BINDING, mModel.getImports());
115             } else {
116                 mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
117                         mModel.getImports());
118             }
119         }
120         return mResolvedClass;
121     }
122 
getIncludedLayout()123     public String getIncludedLayout() {
124         return mBundle.getIncludedLayout();
125     }
126 
isBinder()127     public boolean isBinder() {
128         return getIncludedLayout() != null;
129     }
130 
supportsTag()131     public boolean supportsTag() {
132         return !SetterStore.get(ModelAnalyzer.getInstance())
133                 .isUntaggable(mBundle.getFullClassName());
134     }
135 
getBindings()136     public List<Binding> getBindings() {
137         return mBindings;
138     }
139 
getInverseBindings()140     public List<InverseBinding> getInverseBindings() {
141         return mInverseBindings;
142     }
143 
getModel()144     public ExprModel getModel() {
145         return mModel;
146     }
147 
setModel(ExprModel model)148     public void setModel(ExprModel model) {
149         mModel = model;
150     }
151 
resolveListeners()152     public void resolveListeners() {
153         for (Binding binding : mBindings) {
154             binding.resolveListeners();
155         }
156     }
157 
resolveTwoWayExpressions()158     public void resolveTwoWayExpressions() {
159         for (Binding binding : mBindings) {
160             binding.resolveTwoWayExpressions();
161         }
162     }
163 
164     /**
165      * Called after BindingTarget is finalized.
166      * <p>
167      * We traverse all bindings and ask SetterStore to figure out if any can be combined.
168      * When N bindings are combined, they are demoted from being a binding expression and a new
169      * ArgList expression is added as the new binding expression that depends on others.
170      */
resolveMultiSetters()171     public void resolveMultiSetters() {
172         L.d("resolving multi setters for %s", getId());
173         final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
174         final String[] attributes = new String[mBindings.size()];
175         final ModelClass[] types = new ModelClass[mBindings.size()];
176         for (int i = 0; i < mBindings.size(); i ++) {
177             Binding binding = mBindings.get(i);
178             try {
179                 Scope.enter(binding);
180                 attributes[i] = binding.getName();
181                 types[i] = binding.getExpr().getResolvedType();
182             } finally {
183                 Scope.exit();
184             }
185         }
186         final List<SetterStore.MultiAttributeSetter> multiAttributeSetterCalls = setterStore
187                 .getMultiAttributeSetterCalls(attributes, getResolvedType(), types);
188         if (multiAttributeSetterCalls.isEmpty()) {
189             return;
190         }
191         final Map<String, Binding> lookup = new HashMap<String, Binding>();
192         for (Binding binding : mBindings) {
193             String name = binding.getName();
194             if (name.startsWith("android:")) {
195                 lookup.put(name, binding);
196             } else {
197                 int ind = name.indexOf(":");
198                 if (ind == -1) {
199                     lookup.put(name, binding);
200                 } else {
201                     lookup.put(name.substring(ind + 1), binding);
202                 }
203             }
204         }
205         List<MergedBinding> mergeBindings = new ArrayList<MergedBinding>();
206         for (final SetterStore.MultiAttributeSetter setter : multiAttributeSetterCalls) {
207             L.d("resolved %s", setter);
208             final List<Binding> mergedBindings = new ArrayList<Binding>();
209             for (String attribute : setter.attributes) {
210                 Binding binding = lookup.get(attribute);
211                 Preconditions.checkNotNull(binding, "cannot find binding for %s", attribute);
212                 mergedBindings.add(binding);
213             }
214 
215             for (Binding binding : mergedBindings) {
216                 binding.getExpr().setBindingExpression(false);
217                 mBindings.remove(binding);
218             }
219             MergedBinding mergedBinding = new MergedBinding(getModel(), setter, this,
220                     mergedBindings);
221             mergeBindings.add(mergedBinding);
222         }
223         for (MergedBinding binding : mergeBindings) {
224             mBindings.add(binding);
225         }
226     }
227 }
228