Binding.java revision 731b74f7f44e67312a1fc4161c4e0aae221b2417
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) {
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(targetViewName, currentValue, argCode);
135    }
136
137    public Expr[] getComponentExpressions() {
138        return new Expr[] { mExpr };
139    }
140
141    public boolean requiresOldValue() {
142        return getSetterCall().requiresOldValue();
143    }
144
145    /**
146     * The min api level in which this binding should be executed.
147     * <p>
148     * This should be the minimum value among the dependencies of this binding. For now, we only
149     * check the setter.
150     */
151    public int getMinApi() {
152        return getSetterCall().getMinApi();
153    }
154
155    public String getName() {
156        return mName;
157    }
158
159    public Expr getExpr() {
160        return mExpr;
161    }
162
163    private boolean isViewStubAttribute() {
164        return ("android:inflatedId".equals(mName) ||
165                "android:layout".equals(mName) ||
166                "android:visibility".equals(mName) ||
167                "android:layoutInflater".equals(mName));
168    }
169
170    private boolean isListenerAttribute() {
171        return ("android:onInflate".equals(mName) ||
172                "android:onInflateListener".equals(mName));
173
174    }
175
176    private static class ViewStubSetterCall extends SetterCall {
177        private final String mName;
178
179        public ViewStubSetterCall(String name) {
180            mName = name.substring(name.lastIndexOf(':') + 1);
181        }
182
183        @Override
184        protected String toJavaInternal(String viewExpression, String converted) {
185            return "if (" + viewExpression + ".isInflated()) " + viewExpression +
186                    ".getBinding().setVariable(BR." + mName + ", " + converted + ")";
187        }
188
189        @Override
190        protected String toJavaInternal(String viewExpression, String oldValue, String converted) {
191            return null;
192        }
193
194        @Override
195        public int getMinApi() {
196            return 0;
197        }
198
199        @Override
200        public boolean requiresOldValue() {
201            return false;
202        }
203
204        @Override
205        public ModelClass[] getParameterTypes() {
206            return new ModelClass[] {
207                    ModelAnalyzer.getInstance().findClass(Object.class)
208            };
209        }
210    }
211
212    private static class ViewStubDirectCall extends SetterCall {
213        private final SetterCall mWrappedCall;
214
215        public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) {
216            mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name,
217                    viewType, expr.getResolvedType(), expr.getModel().getImports());
218            if (mWrappedCall == null) {
219                L.e("Cannot find the setter for attribute '%s' on %s with parameter type %s.",
220                        name, viewType, expr.getResolvedType());
221            }
222        }
223
224        @Override
225        protected String toJavaInternal(String viewExpression, String converted) {
226            return "if (!" + viewExpression + ".isInflated()) " +
227                    mWrappedCall.toJava(viewExpression + ".getViewStub()", null, converted);
228        }
229
230        @Override
231        protected String toJavaInternal(String viewExpression, String oldValue, String converted) {
232            return null;
233        }
234
235        @Override
236        public int getMinApi() {
237            return 0;
238        }
239
240        @Override
241        public boolean requiresOldValue() {
242            return false;
243        }
244
245        @Override
246        public ModelClass[] getParameterTypes() {
247            return new ModelClass[] {
248                    ModelAnalyzer.getInstance().findClass(Object.class)
249            };
250        }
251    }
252}
253