1d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar/*
2d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
3d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
4d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * you may not use this file except in compliance with the License.
6d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * You may obtain a copy of the License at
7d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
8d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
10d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * See the License for the specific language governing permissions and
14d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * limitations under the License.
15d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar */
16d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.expr;
18d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
1988ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyarimport android.databinding.tool.ext.ExtKt;
20d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.tool.Binding;
21d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.tool.BindingTarget;
22d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.tool.InverseBinding;
23731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.processing.Scope;
24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable;
25716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.databinding.tool.reflection.Callable.Type;
26fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
28716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.databinding.tool.reflection.ModelMethod;
2988ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyarimport android.databinding.tool.util.BrNameUtil;
30d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.tool.store.SetterStore;
31d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.tool.store.SetterStore.BindingGetterCall;
32fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
3388ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyarimport android.databinding.tool.util.Preconditions;
34e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mountimport android.databinding.tool.writer.KCode;
35d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
36d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.List;
37d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
38d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarpublic class FieldAccessExpr extends Expr {
39d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    String mName;
4088ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar    // notification name for the field. Important when we map this to a method w/ different name
4188ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar    String mBrName;
42e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    Callable mGetter;
437920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount    final boolean mIsObservableField;
44793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount    boolean mIsListener;
45d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    boolean mIsViewAttributeAccess;
46d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
47d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    FieldAccessExpr(Expr parent, String name) {
48d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        super(parent);
49d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        mName = name;
507920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        mIsObservableField = false;
517920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount    }
527920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount
537920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount    FieldAccessExpr(Expr parent, String name, boolean isObservableField) {
547920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        super(parent);
557920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        mName = name;
567920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        mIsObservableField = isObservableField;
57d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
58d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
59a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount    public Expr getChild() {
607920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().get(0);
61d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
62d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
63e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    public Callable getGetter() {
649e20571da789c7d50650513c38d5a333b11b9fd3Yigit Boyar        if (mGetter == null) {
659e20571da789c7d50650513c38d5a333b11b9fd3Yigit Boyar            getResolvedType();
669e20571da789c7d50650513c38d5a333b11b9fd3Yigit Boyar        }
67d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mGetter;
68d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
69d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
70d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
71d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public String getInvertibleError() {
72d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (getGetter().setterName == null) {
73d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return "Two-way binding cannot resolve a setter for " + getResolvedType().toJavaCode() +
74d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    " property '" + mName + "'";
75d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
76d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (!mGetter.isDynamic()) {
77d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return "Cannot change a final field in " + getResolvedType().toJavaCode() +
78d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    " property " + mName;
79d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
80d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return null;
81d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
82d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
83716ba89e7f459f49ea85070d4710c1d79d715298George Mount    public int getMinApi() {
84716ba89e7f459f49ea85070d4710c1d79d715298George Mount        return mGetter.getMinApi();
85716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
86716ba89e7f459f49ea85070d4710c1d79d715298George Mount
87d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
88d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public boolean isDynamic() {
89d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        if (mGetter == null) {
90d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar            getResolvedType();
91d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
92716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (mGetter == null || mGetter.type == Type.METHOD) {
93793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return true;
94019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        }
95019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        // if it is static final, gone
96019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        if (getChild().isDynamic()) {
97019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            // if owner is dynamic, then we can be dynamic unless we are static final
98019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            return !mGetter.isStatic() || mGetter.isDynamic();
99019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        }
100019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar
101d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (mIsViewAttributeAccess) {
102d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return true; // must be able to invalidate this
103d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
104d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
105019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        // if owner is NOT dynamic, we can be dynamic if an only if getter is dynamic
106019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        return mGetter.isDynamic();
107d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
108d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
109c4a07bddb4dd5c3bfbecf4d87909c5b447ae56dcGeorge Mount    public boolean hasBindableAnnotations() {
110c4a07bddb4dd5c3bfbecf4d87909c5b447ae56dcGeorge Mount        return mGetter.canBeInvalidated();
111c4a07bddb4dd5c3bfbecf4d87909c5b447ae56dcGeorge Mount    }
112c4a07bddb4dd5c3bfbecf4d87909c5b447ae56dcGeorge Mount
113d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
114793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount    public Expr resolveListeners(ModelClass listener, Expr parent) {
115793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (mName == null || mName.isEmpty()) {
116793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return this; // ObservableFields aren't listeners
117793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        }
118793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        final ModelClass childType = getChild().getResolvedType();
119793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (getGetter() == null) {
120793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            if (listener == null || !mIsListener) {
121793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                L.e("Could not resolve %s.%s as an accessor or listener on the attribute.",
122793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                        childType.getCanonicalName(), mName);
123793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                return this;
124793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            }
125793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            getChild().getParents().remove(this);
126793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        } else if (listener == null) {
127793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return this; // Not a listener, but we have a getter.
128716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
129716ba89e7f459f49ea85070d4710c1d79d715298George Mount        List<ModelMethod> abstractMethods = listener.getAbstractMethods();
130716ba89e7f459f49ea85070d4710c1d79d715298George Mount        int numberOfAbstractMethods = abstractMethods == null ? 0 : abstractMethods.size();
131716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (numberOfAbstractMethods != 1) {
132716ba89e7f459f49ea85070d4710c1d79d715298George Mount            if (mGetter == null) {
133716ba89e7f459f49ea85070d4710c1d79d715298George Mount                L.e("Could not find accessor %s.%s and %s has %d abstract methods, so is" +
134716ba89e7f459f49ea85070d4710c1d79d715298George Mount                                " not resolved as a listener",
135793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                        childType.getCanonicalName(), mName,
136716ba89e7f459f49ea85070d4710c1d79d715298George Mount                        listener.getCanonicalName(), numberOfAbstractMethods);
137716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
138793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return this;
139716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
140716ba89e7f459f49ea85070d4710c1d79d715298George Mount
141716ba89e7f459f49ea85070d4710c1d79d715298George Mount        // Look for a signature matching the abstract method
142716ba89e7f459f49ea85070d4710c1d79d715298George Mount        final ModelMethod listenerMethod = abstractMethods.get(0);
143716ba89e7f459f49ea85070d4710c1d79d715298George Mount        final ModelClass[] listenerParameters = listenerMethod.getParameterTypes();
144793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        boolean isStatic = getChild() instanceof StaticIdentifierExpr;
145793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        List<ModelMethod> methods = childType.findMethods(mName, isStatic);
146793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (methods == null) {
147793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return this;
148793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        }
149793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        for (ModelMethod method : methods) {
150793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            if (acceptsParameters(method, listenerParameters) &&
151793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    method.getReturnType(null).equals(listenerMethod.getReturnType(null))) {
152716ba89e7f459f49ea85070d4710c1d79d715298George Mount                resetResolvedType();
153793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                // replace this with ListenerExpr in parent
154793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                Expr listenerExpr = getModel().listenerExpr(getChild(), mName, listener,
155793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                        listenerMethod);
156793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                if (parent != null) {
157793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    int index;
158793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    while ((index = parent.getChildren().indexOf(this)) != -1) {
159793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                        parent.getChildren().set(index, listenerExpr);
160793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    }
161793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                }
162793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                if (getModel().mBindingExpressions.contains(this)) {
163793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    getModel().bindingExpr(listenerExpr);
164793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                }
165793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                getParents().remove(parent);
166793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                if (getParents().isEmpty()) {
167793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    getModel().removeExpr(this);
168793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                }
169793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                return listenerExpr;
170716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
171716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
172716ba89e7f459f49ea85070d4710c1d79d715298George Mount
173716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (mGetter == null) {
174716ba89e7f459f49ea85070d4710c1d79d715298George Mount            L.e("Listener class %s with method %s did not match signature of any method %s.%s",
175716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    listener.getCanonicalName(), listenerMethod.getName(),
176793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                    childType.getCanonicalName(), mName);
177716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
178793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        return this;
179716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
180716ba89e7f459f49ea85070d4710c1d79d715298George Mount
181716ba89e7f459f49ea85070d4710c1d79d715298George Mount    private boolean acceptsParameters(ModelMethod method, ModelClass[] listenerParameters) {
182716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ModelClass[] parameters = method.getParameterTypes();
183716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (parameters.length != listenerParameters.length) {
184716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return false;
185716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
186716ba89e7f459f49ea85070d4710c1d79d715298George Mount        for (int i = 0; i < parameters.length; i++) {
187716ba89e7f459f49ea85070d4710c1d79d715298George Mount            if (!parameters[i].isAssignableFrom(listenerParameters[i])) {
188716ba89e7f459f49ea85070d4710c1d79d715298George Mount                return false;
189716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
190716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
191716ba89e7f459f49ea85070d4710c1d79d715298George Mount        return true;
192716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
193716ba89e7f459f49ea85070d4710c1d79d715298George Mount
194716ba89e7f459f49ea85070d4710c1d79d715298George Mount    @Override
195dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar    protected List<Dependency> constructDependencies() {
196dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
197dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        for (Dependency dependency : dependencies) {
198a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount            if (dependency.getOther() == getChild()) {
199dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar                dependency.setMandatory(true);
200dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar            }
201dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        }
202dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        return dependencies;
203dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar    }
204dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar
205dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar    @Override
206d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    protected String computeUniqueKey() {
2077920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        if (mIsObservableField) {
208d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return addTwoWay(join(mName, "..", super.computeUniqueKey()));
2097920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        }
210d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return addTwoWay(join(mName, ".", super.computeUniqueKey()));
211d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
212d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
213d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public String getName() {
214d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mName;
215d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
216d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
21788ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar    public String getBrName() {
21888ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar        if (mIsListener) {
21988ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            return null;
22088ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar        }
22188ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar        try {
22288ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            Scope.enter(this);
22388ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            Preconditions.checkNotNull(mGetter, "cannot get br name before resolving the getter");
22488ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            return mBrName;
22588ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar        } finally {
22688ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            Scope.exit();
22788ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar        }
22888ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar    }
22988ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar
230d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
23179fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    public void updateExpr(ModelAnalyzer modelAnalyzer) {
232731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        try {
233731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.enter(this);
234731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            resolveType(modelAnalyzer);
235731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            super.updateExpr(modelAnalyzer);
236731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        } finally {
237731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.exit();
238731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
23918243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    }
24018243f6f1b7527272ef4feccdf4327d80d9f2241George Mount
24118243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    @Override
24218243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
243793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (mIsListener) {
244793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return modelAnalyzer.findClass(Object.class);
245793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        }
2467920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        if (mGetter == null) {
247a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount            Expr child = getChild();
248731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            child.getResolvedType();
249a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount            boolean isStatic = child instanceof StaticIdentifierExpr;
250f9e51c010bd2eab9cca01baaccc0e5a73b8b72c9Yigit Boyar            ModelClass resolvedType = child.getResolvedType();
251e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar            L.d("resolving %s. Resolved class type: %s", this, resolvedType);
252f9e51c010bd2eab9cca01baaccc0e5a73b8b72c9Yigit Boyar
253fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            mGetter = resolvedType.findGetterOrField(mName, isStatic);
254716ba89e7f459f49ea85070d4710c1d79d715298George Mount
255716ba89e7f459f49ea85070d4710c1d79d715298George Mount            if (mGetter == null) {
256793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                mIsListener = resolvedType.findMethods(mName, isStatic) != null;
257793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                if (!mIsListener) {
258716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    L.e("Could not find accessor %s.%s", resolvedType.getCanonicalName(), mName);
259716ba89e7f459f49ea85070d4710c1d79d715298George Mount                }
260716ba89e7f459f49ea85070d4710c1d79d715298George Mount                return modelAnalyzer.findClass(Object.class);
261716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
262ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar
263ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            if (mGetter.isStatic() && !isStatic) {
264ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // found a static method on an instance. register a new one
265ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                child.getParents().remove(this);
266ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().remove(child);
267ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                StaticIdentifierExpr staticId = getModel().staticIdentifierFor(resolvedType);
268ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().add(staticId);
269ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                staticId.getParents().add(this);
270ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                child = getChild(); // replace the child for the next if stmt
271ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            }
272ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar
273fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (mGetter.resolvedType.isObservableField()) {
2747920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                // Make this the ".get()" and add an extra field access for the observable field
275a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount                child.getParents().remove(this);
276a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount                getChildren().remove(child);
2777920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount
278a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount                FieldAccessExpr observableField = getModel().observableField(child, mName);
2797920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                observableField.mGetter = mGetter;
2807920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount
2817920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                getChildren().add(observableField);
2827920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                observableField.getParents().add(this);
283d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                mGetter = mGetter.resolvedType.findGetterOrField("", false);
2847920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                mName = "";
28588ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar                mBrName = ExtKt.br(mName);
28688ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar            } else if (hasBindableAnnotations()) {
28788ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar                mBrName = ExtKt.br(BrNameUtil.brKey(mGetter));
2887920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount            }
2897920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        }
29018243f6f1b7527272ef4feccdf4327d80d9f2241George Mount        return mGetter.resolvedType;
2917920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount    }
2927920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount
2937920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount    @Override
294d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public Expr resolveTwoWayExpressions(Expr parent) {
295d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final Expr child = getChild();
296d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (!(child instanceof ViewFieldExpr)) {
297d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return this;
298d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
299d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final ViewFieldExpr expr = (ViewFieldExpr) child;
300d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final BindingTarget bindingTarget = expr.getBindingTarget();
301d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
302d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // This is a binding to a View's attribute, so look for matching attribute
303d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // on that View's BindingTarget. If there is an expression, we simply replace
304d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // the binding with that binding expression.
305d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (Binding binding : bindingTarget.getBindings()) {
306d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (attributeMatchesName(binding.getName(), mName)) {
307d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                final Expr replacement = binding.getExpr();
308d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                replaceExpression(parent, replacement);
309d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                return replacement;
310d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
311d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
312d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
313d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // There was no binding expression to bind to. This should be a two-way binding.
314d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // This is a synthesized two-way binding because we must capture the events from
315d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        // the View and change the value when the target View's attribute changes.
316d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
317d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final ModelClass targetClass = expr.getResolvedType();
318d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        BindingGetterCall getter = setterStore.getGetterCall(mName, targetClass, null, null);
319d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (getter == null) {
320d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            getter = setterStore.getGetterCall("android:" + mName, targetClass, null, null);
321d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (getter == null) {
322d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                L.e("Could not resolve the two-way binding attribute '%s' on type '%s'",
323d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        mName, targetClass);
324d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
325d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
326d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        InverseBinding inverseBinding = null;
327d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (Binding binding : bindingTarget.getBindings()) {
328d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            final Expr testExpr = binding.getExpr();
329d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (testExpr instanceof TwoWayListenerExpr &&
330d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    getter.getEventAttribute().equals(binding.getName())) {
331d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                inverseBinding = ((TwoWayListenerExpr) testExpr).mInverseBinding;
332d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                break;
333d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
334d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
335d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (inverseBinding == null) {
336d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            inverseBinding = bindingTarget.addInverseBinding(mName, getter);
337d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
338d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        inverseBinding.addChainedExpression(this);
339d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        mIsViewAttributeAccess = true;
340d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        enableDirectInvalidation();
341d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return this;
342d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
343d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
344d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static boolean attributeMatchesName(String attribute, String field) {
345d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        int colonIndex = attribute.indexOf(':');
346d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return attribute.substring(colonIndex + 1).equals(field);
347d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
348d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
349d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private void replaceExpression(Expr parent, Expr replacement) {
350d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (parent != null) {
351d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            List<Expr> children = parent.getChildren();
352d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            int index;
353d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            while ((index = children.indexOf(this)) >= 0) {
354d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                children.set(index, replacement);
355d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                replacement.getParents().add(parent);
356d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
357d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            while (getParents().remove(parent)) {
358d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                // just remove all copies of parent.
359d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
360d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
361d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (getParents().isEmpty()) {
362d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            getModel().removeExpr(this);
363d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
364d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
365d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
366d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
36718243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    protected String asPackage() {
368a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount        String parentPackage = getChild().asPackage();
36918243f6f1b7527272ef4feccdf4327d80d9f2241George Mount        return parentPackage == null ? null : parentPackage + "." + mName;
370d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
371e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
372e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    @Override
373d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    protected KCode generateCode(boolean expand) {
374d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        KCode code = new KCode();
375d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (expand) {
376d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            String defaultValue = ModelAnalyzer.getInstance().getDefaultValue(
377d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    getResolvedType().toJavaCode());
378d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            code.app("(", getChild().toCode(true))
379d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    .app(" == null) ? ")
380d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    .app(defaultValue)
381d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    .app(" : ");
382d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
383d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        code.app("", getChild().toCode(expand)).app(".");
384793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (getGetter().type == Callable.Type.FIELD) {
385793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return code.app(getGetter().name);
386e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        } else {
387793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return code.app(getGetter().name).app("()");
388e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        }
389e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    }
390d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
391d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
392d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public KCode toInverseCode(KCode value) {
393d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (mGetter.setterName == null) {
394d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            throw new IllegalStateException("There is no inverse for " + toCode().generate());
395d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
396d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        KCode castValue = new KCode("(").app(getResolvedType().toJavaCode() + ")(", value).app(")");
397d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String type = getChild().getResolvedType().toJavaCode();
398d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        KCode code = new KCode("targetObj_.");
399d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (getGetter().type == Callable.Type.FIELD) {
400d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            code.app(getGetter().setterName).app(" = ", castValue).app(";");
401d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        } else {
402d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            code.app(getGetter().setterName).app("(", castValue).app(")").app(";");
403d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
404d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return new KCode()
405d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .app("final ")
406d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .app(type)
407d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .app(" targetObj_ = ", getChild().toCode(true))
408d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .app(";")
409d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .nl(new KCode("if (targetObj_ != null) {"))
410d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .tab(code)
411d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                .nl(new KCode("}"));
412d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
413d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar}
414