• 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.expr.LambdaExpr;
22 import android.databinding.tool.processing.ErrorMessages;
23 import android.databinding.tool.processing.Scope;
24 import android.databinding.tool.processing.scopes.LocationScopeProvider;
25 import android.databinding.tool.reflection.ModelAnalyzer;
26 import android.databinding.tool.reflection.ModelClass;
27 import android.databinding.tool.reflection.ModelMethod;
28 import android.databinding.tool.store.Location;
29 import android.databinding.tool.store.SetterStore;
30 import android.databinding.tool.store.SetterStore.BindingSetterCall;
31 import android.databinding.tool.store.SetterStore.SetterCall;
32 import android.databinding.tool.util.L;
33 import android.databinding.tool.util.Preconditions;
34 import android.databinding.tool.writer.LayoutBinderWriterKt;
35 
36 import java.util.List;
37 import java.util.Map;
38 
39 public class Binding implements LocationScopeProvider {
40 
41     private final String mName;
42     private Expr mExpr;
43     private final BindingTarget mTarget;
44     private BindingSetterCall mSetterCall;
45 
Binding(BindingTarget target, String name, Expr expr)46     public Binding(BindingTarget target, String name, Expr expr) {
47         this(target, name, expr, null);
48     }
49 
Binding(BindingTarget target, String name, Expr expr, BindingSetterCall setterCall)50     public Binding(BindingTarget target, String name, Expr expr, BindingSetterCall setterCall) {
51         mTarget = target;
52         mName = name;
53         mExpr = expr;
54         mSetterCall = setterCall;
55     }
56 
57     @Override
provideScopeLocation()58     public List<Location> provideScopeLocation() {
59         return mExpr.getLocations();
60     }
61 
resolveListeners()62     public void resolveListeners() {
63         final ModelClass listenerParameter = getListenerParameter(mTarget, mName, mExpr.getModel());
64         Expr listenerExpr = mExpr.resolveListeners(listenerParameter, null);
65         if (listenerExpr != mExpr) {
66             listenerExpr.setBindingExpression(true);
67             mExpr = listenerExpr;
68         }
69     }
70 
resolveCallbackParams()71     public void resolveCallbackParams() {
72         if (!(mExpr instanceof LambdaExpr)) {
73             return;
74         }
75         LambdaExpr lambdaExpr = (LambdaExpr) mExpr;
76         final ModelClass listener = getListenerParameter(mTarget, mName, mExpr.getModel());
77         Preconditions.checkNotNull(listener, ErrorMessages.CANNOT_FIND_SETTER_CALL, mName,
78                 "lambda", getTarget().getInterfaceType());
79         //noinspection ConstantConditions
80         List<ModelMethod> abstractMethods = listener.getAbstractMethods();
81         int numberOfAbstractMethods = abstractMethods.size();
82         if (numberOfAbstractMethods != 1) {
83             L.e(ErrorMessages.CANNOT_FIND_ABSTRACT_METHOD, mName, listener.getCanonicalName(),
84                     numberOfAbstractMethods, 1);
85         }
86         final ModelMethod method = abstractMethods.get(0);
87         final int argCount = lambdaExpr.getCallbackExprModel().getArgCount();
88         if (argCount != 0 && argCount != method.getParameterTypes().length) {
89             L.e(ErrorMessages.CALLBACK_ARGUMENT_COUNT_MISMATCH, listener.getCanonicalName(),
90                     method.getName(), method.getParameterTypes().length, argCount);
91         }
92         lambdaExpr.setup(listener, method, mExpr.getModel().obtainCallbackId());
93     }
94 
resolveTwoWayExpressions()95     public void resolveTwoWayExpressions() {
96         Expr expr = mExpr.resolveTwoWayExpressions(null);
97         if (expr != mExpr) {
98             mExpr = expr;
99         }
100     }
101 
getSetterCall()102     private SetterStore.BindingSetterCall getSetterCall() {
103         if (mSetterCall == null) {
104             try {
105                 Scope.enter(getTarget());
106                 Scope.enter(this);
107                 resolveSetterCall();
108                 if (mSetterCall == null) {
109                     L.e(ErrorMessages.CANNOT_FIND_SETTER_CALL, mName, mExpr.getResolvedType(),
110                             getTarget().getInterfaceType());
111                 }
112             } finally {
113                 Scope.exit();
114                 Scope.exit();
115             }
116         }
117         return mSetterCall;
118     }
119 
resolveSetterCall()120     private void resolveSetterCall() {
121         ModelClass viewType = mTarget.getResolvedType();
122         if (viewType != null && viewType.extendsViewStub()) {
123             if (isListenerAttribute(mName)) {
124                 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
125                 ModelClass viewStubProxy = modelAnalyzer.
126                         findClass("android.databinding.ViewStubProxy", null);
127                 mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName,
128                         viewStubProxy, mExpr.getResolvedType(), mExpr.getModel().getImports());
129             } else if (isViewStubAttribute(mName)) {
130                 mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr.getResolvedType(),
131                         mExpr.getModel().getImports());
132             } else {
133                 mSetterCall = new ViewStubSetterCall(mName);
134             }
135         } else {
136             final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
137             mSetterCall = setterStore.getSetterCall(mName,
138                     viewType, mExpr.getResolvedType(), mExpr.getModel().getImports());
139         }
140     }
141 
142     /**
143      * Similar to getSetterCall, but assumes an Object parameter to find the best matching listener.
144      */
getListenerParameter(BindingTarget target, String name, ExprModel model)145     private static ModelClass getListenerParameter(BindingTarget target, String name,
146             ExprModel model) {
147         ModelClass viewType = target.getResolvedType();
148         SetterCall setterCall;
149         ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
150         ModelClass objectParameter = modelAnalyzer.findClass(Object.class);
151         SetterStore setterStore = SetterStore.get(modelAnalyzer);
152         if (viewType != null && viewType.extendsViewStub()) {
153             if (isListenerAttribute(name)) {
154                 ModelClass viewStubProxy = modelAnalyzer.
155                         findClass("android.databinding.ViewStubProxy", null);
156                 setterCall = SetterStore.get(modelAnalyzer).getSetterCall(name,
157                         viewStubProxy, objectParameter, model.getImports());
158             } else if (isViewStubAttribute(name)) {
159                 setterCall = null; // view stub attrs are not callbacks
160             } else {
161                 setterCall = new ViewStubSetterCall(name);
162             }
163         } else {
164             setterCall = setterStore.getSetterCall(name, viewType, objectParameter,
165                     model.getImports());
166         }
167         if (setterCall != null) {
168             return setterCall.getParameterTypes()[0];
169         }
170         List<SetterStore.MultiAttributeSetter> setters =
171                 setterStore.getMultiAttributeSetterCalls(new String[]{name}, viewType,
172                 new ModelClass[] {modelAnalyzer.findClass(Object.class)});
173         if (setters.isEmpty()) {
174             return null;
175         } else {
176             return setters.get(0).getParameterTypes()[0];
177         }
178     }
179 
getTarget()180     public BindingTarget getTarget() {
181         return mTarget;
182     }
183 
toJavaCode(String targetViewName, String bindingComponent)184     public String toJavaCode(String targetViewName, String bindingComponent) {
185         final String currentValue = requiresOldValue()
186                 ? "this." + LayoutBinderWriterKt.getOldValueName(mExpr) : null;
187         final String argCode = getExpr().toCode().generate();
188         return getSetterCall().toJava(bindingComponent, targetViewName, currentValue, argCode);
189     }
190 
getBindingAdapterInstanceClass()191     public String getBindingAdapterInstanceClass() {
192         return getSetterCall().getBindingAdapterInstanceClass();
193     }
194 
getComponentExpressions()195     public Expr[] getComponentExpressions() {
196         return new Expr[] { mExpr };
197     }
198 
requiresOldValue()199     public boolean requiresOldValue() {
200         return getSetterCall().requiresOldValue();
201     }
202 
203     /**
204      * The min api level in which this binding should be executed.
205      * <p>
206      * This should be the minimum value among the dependencies of this binding. For now, we only
207      * check the setter.
208      */
getMinApi()209     public int getMinApi() {
210         return getSetterCall().getMinApi();
211     }
212 
getName()213     public String getName() {
214         return mName;
215     }
216 
getExpr()217     public Expr getExpr() {
218         return mExpr;
219     }
220 
isViewStubAttribute(String name)221     private static boolean isViewStubAttribute(String name) {
222         return ("android:inflatedId".equals(name) ||
223                 "android:layout".equals(name) ||
224                 "android:visibility".equals(name) ||
225                 "android:layoutInflater".equals(name));
226     }
227 
isListenerAttribute(String name)228     private static boolean isListenerAttribute(String name) {
229         return ("android:onInflate".equals(name) ||
230                 "android:onInflateListener".equals(name));
231     }
232 
233     private static class ViewStubSetterCall extends SetterCall {
234         private final String mName;
235 
ViewStubSetterCall(String name)236         public ViewStubSetterCall(String name) {
237             mName = name.substring(name.lastIndexOf(':') + 1);
238         }
239 
240         @Override
toJavaInternal(String componentExpression, String viewExpression, String converted)241         protected String toJavaInternal(String componentExpression, String viewExpression,
242                 String converted) {
243             return "if (" + viewExpression + ".isInflated()) " + viewExpression +
244                     ".getBinding().setVariable(BR." + mName + ", " + converted + ")";
245         }
246 
247         @Override
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted)248         protected String toJavaInternal(String componentExpression, String viewExpression,
249                 String oldValue, String converted) {
250             return null;
251         }
252 
253         @Override
getMinApi()254         public int getMinApi() {
255             return 0;
256         }
257 
258         @Override
requiresOldValue()259         public boolean requiresOldValue() {
260             return false;
261         }
262 
263         @Override
getParameterTypes()264         public ModelClass[] getParameterTypes() {
265             return new ModelClass[] {
266                     ModelAnalyzer.getInstance().findClass(Object.class)
267             };
268         }
269 
270         @Override
getBindingAdapterInstanceClass()271         public String getBindingAdapterInstanceClass() {
272             return null;
273         }
274     }
275 
276     private static class ViewStubDirectCall extends SetterCall {
277         private final SetterCall mWrappedCall;
278 
ViewStubDirectCall(String name, ModelClass viewType, ModelClass resolvedType, Map<String, String> imports)279         public ViewStubDirectCall(String name, ModelClass viewType, ModelClass resolvedType,
280                 Map<String, String> imports) {
281             mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name,
282                     viewType, resolvedType, imports);
283             if (mWrappedCall == null) {
284                 L.e("Cannot find the setter for attribute '%s' on %s with parameter type %s.",
285                         name, viewType, resolvedType);
286             }
287         }
288 
289         @Override
toJavaInternal(String componentExpression, String viewExpression, String converted)290         protected String toJavaInternal(String componentExpression, String viewExpression,
291                 String converted) {
292             return "if (!" + viewExpression + ".isInflated()) " +
293                     mWrappedCall.toJava(componentExpression, viewExpression + ".getViewStub()",
294                             null, converted);
295         }
296 
297         @Override
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted)298         protected String toJavaInternal(String componentExpression, String viewExpression,
299                 String oldValue, String converted) {
300             return null;
301         }
302 
303         @Override
getMinApi()304         public int getMinApi() {
305             return 0;
306         }
307 
308         @Override
requiresOldValue()309         public boolean requiresOldValue() {
310             return false;
311         }
312 
313         @Override
getParameterTypes()314         public ModelClass[] getParameterTypes() {
315             return new ModelClass[] {
316                     ModelAnalyzer.getInstance().findClass(Object.class)
317             };
318         }
319 
320         @Override
getBindingAdapterInstanceClass()321         public String getBindingAdapterInstanceClass() {
322             return mWrappedCall.getBindingAdapterInstanceClass();
323         }
324     }
325 }
326