1 /* 2 * Copyright (C) 2016 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 17 package android.databinding.tool.expr; 18 19 import android.databinding.tool.CallbackWrapper; 20 import android.databinding.tool.reflection.ModelAnalyzer; 21 import android.databinding.tool.reflection.ModelClass; 22 import android.databinding.tool.reflection.ModelMethod; 23 import android.databinding.tool.solver.ExecutionPath; 24 import android.databinding.tool.util.Preconditions; 25 import android.databinding.tool.writer.KCode; 26 import android.databinding.tool.writer.LayoutBinderWriterKt; 27 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.concurrent.atomic.AtomicInteger; 31 32 public class LambdaExpr extends Expr { 33 private static AtomicInteger sIdCounter = new AtomicInteger(); 34 private final int mId = sIdCounter.incrementAndGet(); 35 private CallbackWrapper mCallbackWrapper; 36 // set when Binding resolves the receiver 37 private final CallbackExprModel mCallbackExprModel; 38 private int mCallbackId; 39 private ExecutionPath mExecutionPath; 40 LambdaExpr(Expr expr, CallbackExprModel callbackExprModel)41 public LambdaExpr(Expr expr, CallbackExprModel callbackExprModel) { 42 super(expr); 43 mCallbackExprModel = callbackExprModel; 44 } 45 getExpr()46 public Expr getExpr() { 47 return getChildren().get(0); 48 } 49 getCallbackExprModel()50 public CallbackExprModel getCallbackExprModel() { 51 return mCallbackExprModel; 52 } 53 54 @Override resolveType(ModelAnalyzer modelAnalyzer)55 protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 56 Preconditions.checkNotNull(mCallbackWrapper, "Lambda expression must be resolved to its" 57 + " setter first to get the type."); 58 return mCallbackWrapper.klass; 59 } 60 61 @Override constructDependencies()62 protected List<Dependency> constructDependencies() { 63 return Collections.emptyList(); 64 } 65 getCallbackWrapper()66 public CallbackWrapper getCallbackWrapper() { 67 return mCallbackWrapper; 68 } 69 70 @Override resolveListeners(ModelClass valueType, Expr parent)71 public Expr resolveListeners(ModelClass valueType, Expr parent) { 72 return this; 73 } 74 75 @Override computeUniqueKey()76 protected String computeUniqueKey() { 77 return "callback" + mId; 78 } 79 80 @Override isDynamic()81 public boolean isDynamic() { 82 return false; 83 } 84 85 @Override generateCode()86 protected KCode generateCode() { 87 Preconditions 88 .checkNotNull(mCallbackWrapper, "Cannot find the callback method for %s", this); 89 KCode code = new KCode(""); 90 final int minApi = mCallbackWrapper.getMinApi(); 91 final String fieldName = LayoutBinderWriterKt.getFieldName(this); 92 if (minApi > 1) { 93 code.app("(getBuildSdkInt() < " + minApi + " ? null : ").app(fieldName).app(")"); 94 } else { 95 code.app(fieldName); 96 } 97 return code; 98 } 99 100 @Override cloneToModel(ExprModel model)101 public Expr cloneToModel(ExprModel model) { 102 return model.lambdaExpr(getExpr().cloneToModel(model), (CallbackExprModel) model); 103 } 104 generateConstructor()105 public String generateConstructor() { 106 return getCallbackWrapper().constructForIdentifier(mCallbackId); 107 } 108 109 @Override markAsUsed()110 public void markAsUsed() { 111 super.markAsUsed(); 112 } 113 114 @Override getInvertibleError()115 protected String getInvertibleError() { 116 return "Lambda expressions cannot be inverted"; 117 } 118 119 @Override toExecutionPath(List<ExecutionPath> paths)120 public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) { 121 // i'm not involved. 122 throw new UnsupportedOperationException("should not call toExecutionPath on a lambda" 123 + " expression"); 124 } 125 getExecutionPath()126 public final ExecutionPath getExecutionPath() { 127 return mExecutionPath; 128 } 129 getCallbackId()130 public int getCallbackId() { 131 return mCallbackId; 132 } 133 setup(ModelClass klass, ModelMethod method, int callbackId)134 public void setup(ModelClass klass, ModelMethod method, int callbackId) { 135 mCallbackId = callbackId; 136 mCallbackWrapper = getModel().callbackWrapper(klass, method); 137 // now register the arguments as variables. 138 final ModelClass[] parameterTypes = method.getParameterTypes(); 139 final List<CallbackArgExpr> args = mCallbackExprModel.getArguments(); 140 if (parameterTypes.length == args.size()) { 141 for (int i = 0; i < parameterTypes.length; i++) { 142 args.get(i).setClassFromCallback(parameterTypes[i]); 143 } 144 } 145 // first convert to execution path because we may add additional expressions 146 mExecutionPath = ExecutionPath.createRoot(); 147 getExpr().toExecutionPath(mExecutionPath); 148 mCallbackExprModel.seal(); 149 } 150 } 151