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