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