• 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     }
62 
getInterfaceType()63     public String getInterfaceType() {
64         return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
65     }
66 
addInverseBinding(String name, Expr expr, String bindingClass)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 
addInverseBinding(String name, BindingGetterCall call)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
provideScopeLocation()86     public List<Location> provideScopeLocation() {
87         return mBundle.provideScopeLocation();
88     }
89 
getId()90     public String getId() {
91         return mBundle.getId();
92     }
93 
getTag()94     public String getTag() {
95         return mBundle.getTag();
96     }
97 
getOriginalTag()98     public String getOriginalTag() {
99         return mBundle.getOriginalTag();
100     }
101 
getViewClass()102     public String getViewClass() {
103         return mBundle.getFullClassName();
104     }
105 
getResolvedType()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 
getIncludedLayout()119     public String getIncludedLayout() {
120         return mBundle.getIncludedLayout();
121     }
122 
isBinder()123     public boolean isBinder() {
124         return getIncludedLayout() != null;
125     }
126 
supportsTag()127     public boolean supportsTag() {
128         return !SetterStore.get(ModelAnalyzer.getInstance())
129                 .isUntaggable(mBundle.getFullClassName());
130     }
131 
getBindings()132     public List<Binding> getBindings() {
133         return mBindings;
134     }
135 
getInverseBindings()136     public List<InverseBinding> getInverseBindings() {
137         return mInverseBindings;
138     }
139 
getModel()140     public ExprModel getModel() {
141         return mModel;
142     }
143 
setModel(ExprModel model)144     public void setModel(ExprModel model) {
145         mModel = model;
146     }
147 
resolveListeners()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 
resolveCallbackParams()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 
resolveTwoWayExpressions()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      */
resolveMultiSetters()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