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