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