1c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount/* 2c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * Copyright (C) 2016 The Android Open Source Project 3c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * 4c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * Licensed under the Apache License, Version 2.0 (the "License"); 5c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * you may not use this file except in compliance with the License. 6c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * You may obtain a copy of the License at 7c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * 8c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * http://www.apache.org/licenses/LICENSE-2.0 9c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * 10c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * Unless required by applicable law or agreed to in writing, software 11c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * distributed under the License is distributed on an "AS IS" BASIS, 12c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * See the License for the specific language governing permissions and 14c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount * limitations under the License. 15c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount */ 16c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 17c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountpackage android.databinding.tool.expr; 18c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 19c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport android.databinding.tool.processing.Scope; 20c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport android.databinding.tool.reflection.ModelAnalyzer; 21c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport android.databinding.tool.reflection.ModelClass; 22c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport android.databinding.tool.reflection.ModelMethod; 23c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport android.databinding.tool.solver.ExecutionPath; 24c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 25c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport java.util.ArrayList; 26c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountimport java.util.List; 27c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 28c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mountpublic abstract class MethodBaseExpr extends Expr { 29c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount String mName; 30c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 31c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount MethodBaseExpr(Expr parent, String name) { 32c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount super(parent); 33c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount mName = name; 34c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 35c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 36c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount public Expr getTarget() { 37c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return getChildren().get(0); 38c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 39c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 40c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount @Override 41c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) { 42c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths); 43c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount // after this, we need a null check. 44c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount List<ExecutionPath> result = new ArrayList<ExecutionPath>(); 45c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (getTarget() instanceof StaticIdentifierExpr) { 46c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount result.addAll(toExecutionPathInOrder(paths, getTarget())); 47c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } else { 48c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount for (ExecutionPath path : targetPaths) { 49c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final ComparisonExpr cmp = getModel() 50c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount .comparison("!=", getTarget(), getModel().symbol("null", Object.class)); 51c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount path.addPath(cmp); 52c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final ExecutionPath subPath = path.addBranch(cmp, true); 53c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (subPath != null) { 54c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount subPath.addPath(this); 55c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount result.add(subPath); 56c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 57c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 58c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 59c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return result; 60c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 61c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 62c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount protected Expr resolveListenersAsMethodReference(ModelClass listener, Expr parent) { 63c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final Expr target = getTarget(); 64c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final ModelClass childType = target.getResolvedType(); 65c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (listener == null) { 66c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount throw new IllegalStateException( 67c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount String.format("Could not resolve %s as a listener.", this)); 68c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 69c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 70c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount List<ModelMethod> abstractMethods = listener.getAbstractMethods(); 71c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount int numberOfAbstractMethods = abstractMethods == null ? 0 : abstractMethods.size(); 72c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (numberOfAbstractMethods != 1) { 73c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount throw new IllegalStateException(String.format( 74c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount "Could not find accessor %s.%s and %s has %d abstract methods, so is" + 75c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount " not resolved as a listener", 76c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount childType.getCanonicalName(), mName, 77c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount listener.getCanonicalName(), numberOfAbstractMethods)); 78c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 79c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 80c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount // Look for a signature matching the abstract method 81c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final ModelMethod listenerMethod = abstractMethods.get(0); 82c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final ModelClass[] listenerParameters = listenerMethod.getParameterTypes(); 83c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount boolean isStatic = getTarget() instanceof StaticIdentifierExpr; 84c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount List<ModelMethod> methods = childType.findMethods(mName, isStatic); 85c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount for (ModelMethod method : methods) { 86c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (acceptsParameters(method, listenerParameters) && 87c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount method.getReturnType(null).equals(listenerMethod.getReturnType(null))) { 88c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount target.getParents().remove(this); 89c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount resetResolvedType(); 90c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount // replace this with ListenerExpr in parent 91c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount Expr listenerExpr = getModel().listenerExpr(getTarget(), mName, listener, 92c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount listenerMethod); 93c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (parent != null) { 94c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount int index; 95c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount while ((index = parent.getChildren().indexOf(this)) != -1) { 96c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount parent.getChildren().set(index, listenerExpr); 97c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 98c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 99c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (getModel().mBindingExpressions.contains(this)) { 100c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount getModel().bindingExpr(listenerExpr); 101c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 102c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount getParents().remove(parent); 103c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (getParents().isEmpty()) { 104c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount getModel().removeExpr(this); 105c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 106c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return listenerExpr; 107c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 108c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 109c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 110c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount throw new IllegalStateException(String.format( 111c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount "Listener class %s with method %s did not match signature of any method %s", 112c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount listener.getCanonicalName(), listenerMethod.getName(), this)); 113c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 114c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 115c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount private boolean acceptsParameters(ModelMethod method, ModelClass[] listenerParameters) { 116c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount ModelClass[] parameters = method.getParameterTypes(); 117c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (parameters.length != listenerParameters.length) { 118c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return false; 119c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 120c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount for (int i = 0; i < parameters.length; i++) { 121c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (!parameters[i].isAssignableFrom(listenerParameters[i])) { 122c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return false; 123c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 124c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 125c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return true; 126c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 127c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 128c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount @Override 129c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount protected List<Dependency> constructDependencies() { 130c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount final List<Dependency> dependencies = constructDynamicChildrenDependencies(); 131c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount for (Dependency dependency : dependencies) { 132c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount if (dependency.getOther() == getTarget()) { 133c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount dependency.setMandatory(true); 134c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 135c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 136c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return dependencies; 137c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 138c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 139c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount public String getName() { 140c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount return mName; 141c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 142c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount 143c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount @Override 144c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount public void updateExpr(ModelAnalyzer modelAnalyzer) { 145c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount try { 146c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount Scope.enter(this); 147c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount resolveType(modelAnalyzer); 148c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount super.updateExpr(modelAnalyzer); 149c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } finally { 150c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount Scope.exit(); 151c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 152c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount } 153c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount} 154