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