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