FieldAccessExpr.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
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.expr;
18
19import com.google.common.base.Preconditions;
20
21import android.databinding.tool.reflection.Callable;
22import android.databinding.tool.reflection.Callable.Type;
23import android.databinding.tool.reflection.ModelAnalyzer;
24import android.databinding.tool.reflection.ModelClass;
25import android.databinding.tool.reflection.ModelMethod;
26import android.databinding.tool.util.L;
27
28import java.util.ArrayList;
29import java.util.List;
30
31public class FieldAccessExpr extends Expr {
32    String mName;
33    Callable mGetter;
34    final boolean mIsObservableField;
35    private List<ModelMethod> mListenerMethods;
36    private List<ModelMethod> mCalledMethods;
37    private List<ModelClass> mListenerTypes;
38    private List<ModelMethod> mPotentialListeners;
39
40    FieldAccessExpr(Expr parent, String name) {
41        super(parent);
42        mName = name;
43        mIsObservableField = false;
44    }
45
46    FieldAccessExpr(Expr parent, String name, boolean isObservableField) {
47        super(parent);
48        mName = name;
49        mIsObservableField = isObservableField;
50    }
51
52    public Expr getChild() {
53        return getChildren().get(0);
54    }
55
56    public Callable getGetter() {
57        if (mGetter == null) {
58            getResolvedType();
59        }
60        return mGetter;
61    }
62
63    public List<ModelMethod> getListenerMethods() {
64        return mListenerMethods;
65    }
66
67    public List<ModelMethod> getCalledMethods() {
68        return mCalledMethods;
69    }
70
71    public List<ModelClass> getListenerTypes() { return mListenerTypes; }
72
73    public boolean isListener() {
74        return mListenerMethods != null && !mListenerMethods.isEmpty();
75    }
76
77    public int getMinApi() {
78        if (isListener()) {
79            int minApi = 1;
80            for (ModelClass listener : mListenerTypes) {
81                int listenerApi = listener.getMinApi();
82                minApi = Math.max(minApi, listenerApi);
83            }
84            return minApi;
85        }
86        return mGetter.getMinApi();
87    }
88
89    @Override
90    public boolean isDynamic() {
91        if (mGetter == null) {
92            getResolvedType();
93        }
94        if (mGetter == null || mGetter.type == Type.METHOD) {
95            return !isListener();
96        }
97        // if it is static final, gone
98        if (getChild().isDynamic()) {
99            // if owner is dynamic, then we can be dynamic unless we are static final
100            return !mGetter.isStatic() || mGetter.isDynamic();
101        }
102
103        // if owner is NOT dynamic, we can be dynamic if an only if getter is dynamic
104        return mGetter.isDynamic();
105    }
106
107    public boolean hasBindableAnnotations() {
108        return mGetter.canBeInvalidated();
109    }
110
111    @Override
112    public boolean resolveListeners(ModelClass listener) {
113        if (mPotentialListeners == null) {
114            return false;
115        }
116
117        List<ModelMethod> abstractMethods = listener.getAbstractMethods();
118        int numberOfAbstractMethods = abstractMethods == null ? 0 : abstractMethods.size();
119        if (numberOfAbstractMethods != 1) {
120            if (mGetter == null) {
121                L.e("Could not find accessor %s.%s and %s has %d abstract methods, so is" +
122                                " not resolved as a listener",
123                        getChild().getResolvedType().getCanonicalName(), mName,
124                        listener.getCanonicalName(), numberOfAbstractMethods);
125            }
126            return false;
127        }
128
129        // See if we've already resolved this listener type
130        if (mListenerMethods == null) {
131            mListenerMethods = new ArrayList<ModelMethod>();
132            mCalledMethods = new ArrayList<ModelMethod>();
133            mListenerTypes = new ArrayList<ModelClass>();
134        } else {
135            for (ModelClass previousListeners : mListenerTypes) {
136                if (previousListeners.equals(listener)) {
137                    return false;
138                }
139            }
140        }
141
142        // Look for a signature matching the abstract method
143        final ModelMethod listenerMethod = abstractMethods.get(0);
144        final ModelClass[] listenerParameters = listenerMethod.getParameterTypes();
145        for (ModelMethod method : mPotentialListeners) {
146            if (acceptsParameters(method, listenerParameters)) {
147                mListenerTypes.add(listener);
148                mListenerMethods.add(listenerMethod);
149                mCalledMethods.add(method);
150                resetResolvedType();
151                return true;
152            }
153        }
154
155        if (mGetter == null) {
156            L.e("Listener class %s with method %s did not match signature of any method %s.%s",
157                    listener.getCanonicalName(), listenerMethod.getName(),
158                    getChild().getResolvedType().getCanonicalName(), mName);
159        }
160        return false;
161    }
162
163    private boolean acceptsParameters(ModelMethod method, ModelClass[] listenerParameters) {
164        ModelClass[] parameters = method.getParameterTypes();
165        if (parameters.length != listenerParameters.length) {
166            return false;
167        }
168        for (int i = 0; i < parameters.length; i++) {
169            if (!parameters[i].isAssignableFrom(listenerParameters[i])) {
170                return false;
171            }
172        }
173        return true;
174    }
175
176    @Override
177    protected List<Dependency> constructDependencies() {
178        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
179        for (Dependency dependency : dependencies) {
180            if (dependency.getOther() == getChild()) {
181                dependency.setMandatory(true);
182            }
183        }
184        return dependencies;
185    }
186
187    @Override
188    protected String computeUniqueKey() {
189        if (mIsObservableField) {
190            return sUniqueKeyJoiner.join(mName, "..", super.computeUniqueKey());
191        }
192        return sUniqueKeyJoiner.join(mName, ".", super.computeUniqueKey());
193    }
194
195    public String getName() {
196        return mName;
197    }
198
199    @Override
200    public void updateExpr(ModelAnalyzer modelAnalyzer) {
201        resolveType(modelAnalyzer);
202        super.updateExpr(modelAnalyzer);
203    }
204
205    @Override
206    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
207        if (mGetter == null) {
208            if (mPotentialListeners != null) {
209                return modelAnalyzer.findClass(Object.class);
210            }
211            Expr child = getChild();
212            child.resolveType(modelAnalyzer);
213            boolean isStatic = child instanceof StaticIdentifierExpr;
214            ModelClass resolvedType = child.getResolvedType();
215            L.d("resolving %s. Resolved class type: %s", this, resolvedType);
216
217            mGetter = resolvedType.findGetterOrField(mName, isStatic);
218            mPotentialListeners = resolvedType.findMethods(mName, isStatic);
219
220            if (mGetter == null) {
221                if (mPotentialListeners == null) {
222                    L.e("Could not find accessor %s.%s", resolvedType.getCanonicalName(), mName);
223                }
224                return modelAnalyzer.findClass(Object.class);
225            }
226
227            if (mGetter.isStatic() && !isStatic) {
228                // found a static method on an instance. register a new one
229                child.getParents().remove(this);
230                getChildren().remove(child);
231                StaticIdentifierExpr staticId = getModel().staticIdentifierFor(resolvedType);
232                getChildren().add(staticId);
233                staticId.getParents().add(this);
234                child = getChild(); // replace the child for the next if stmt
235            }
236
237            if (mGetter.resolvedType.isObservableField()) {
238                // Make this the ".get()" and add an extra field access for the observable field
239                child.getParents().remove(this);
240                getChildren().remove(child);
241
242                FieldAccessExpr observableField = getModel().observableField(child, mName);
243                observableField.mGetter = mGetter;
244
245                getChildren().add(observableField);
246                observableField.getParents().add(this);
247                mGetter = mGetter.resolvedType.findGetterOrField("get", false);
248                mName = "";
249            }
250        }
251        if (isListener()) {
252            return modelAnalyzer.findClass(Object.class);
253        }
254        return mGetter.resolvedType;
255    }
256
257    @Override
258    protected String asPackage() {
259        String parentPackage = getChild().asPackage();
260        return parentPackage == null ? null : parentPackage + "." + mName;
261    }
262}
263