16047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar/*
26047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Copyright (C) 2016 The Android Open Source Project
36047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
46047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
56047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * you may not use this file except in compliance with the License.
66047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * You may obtain a copy of the License at
76047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
86047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
96047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Unless required by applicable law or agreed to in writing, software
116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * See the License for the specific language governing permissions and
146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * limitations under the License.
156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar */
166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarpackage android.databinding.tool.expr;
186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.CallbackWrapper;
206047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.reflection.ModelAnalyzer;
216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.reflection.ModelClass;
226047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.reflection.ModelMethod;
236047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.solver.ExecutionPath;
246047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.util.Preconditions;
256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.writer.KCode;
266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.writer.LayoutBinderWriterKt;
276047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
286047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.Collections;
296047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.List;
306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
316047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
326047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarpublic class LambdaExpr extends Expr {
336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private static AtomicInteger sIdCounter = new AtomicInteger();
346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private final int mId = sIdCounter.incrementAndGet();
356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private CallbackWrapper mCallbackWrapper;
366047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    // set when Binding resolves the receiver
376047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private final CallbackExprModel mCallbackExprModel;
386047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private int mCallbackId;
396047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private ExecutionPath mExecutionPath;
406047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
416047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public LambdaExpr(Expr expr, CallbackExprModel callbackExprModel) {
426047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        super(expr);
436047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mCallbackExprModel = callbackExprModel;
446047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
456047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
466047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public Expr getExpr() {
476047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return getChildren().get(0);
486047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
496047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
506047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public CallbackExprModel getCallbackExprModel() {
516047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mCallbackExprModel;
526047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
536047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
546047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
556047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
566047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.checkNotNull(mCallbackWrapper, "Lambda expression must be resolved to its"
576047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                + " setter first to get the type.");
586047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mCallbackWrapper.klass;
596047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
606047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
616047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
626047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    protected List<Dependency> constructDependencies() {
636047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return Collections.emptyList();
646047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
656047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
666047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public CallbackWrapper getCallbackWrapper() {
676047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mCallbackWrapper;
686047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
696047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
706047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public Expr resolveListeners(ModelClass valueType, Expr parent) {
726047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return this;
736047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
746047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
756047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    protected String computeUniqueKey() {
776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return "callback" + mId;
786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
796047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
806047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
816047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public boolean isDynamic() {
826047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return false;
836047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
846047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
856047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
86bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    protected KCode generateCode() {
876047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions
886047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                .checkNotNull(mCallbackWrapper, "Cannot find the callback method for %s", this);
896047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        KCode code = new KCode("");
906047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final int minApi = mCallbackWrapper.getMinApi();
916047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final String fieldName = LayoutBinderWriterKt.getFieldName(this);
926047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (minApi > 1) {
936047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            code.app("(getBuildSdkInt() < " + minApi + " ? null : ").app(fieldName).app(")");
946047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } else {
956047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            code.app(fieldName);
966047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
976047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return code;
986047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
996047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
100bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    @Override
101bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public Expr cloneToModel(ExprModel model) {
102bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return model.lambdaExpr(getExpr().cloneToModel(model), (CallbackExprModel) model);
103bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
104bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
1056047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public String generateConstructor() {
1066047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return getCallbackWrapper().constructForIdentifier(mCallbackId);
1076047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1086047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1096047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
1106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public void markAsUsed() {
1116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        super.markAsUsed();
1126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
1156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    protected String getInvertibleError() {
1166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return "Lambda expressions cannot be inverted";
1176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
1206047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
1216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // i'm not involved.
1226047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        throw new UnsupportedOperationException("should not call toExecutionPath on a lambda"
1236047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                + " expression");
1246047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public final ExecutionPath getExecutionPath() {
1276047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mExecutionPath;
1286047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1296047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public int getCallbackId() {
1316047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mCallbackId;
1326047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public void setup(ModelClass klass, ModelMethod method, int callbackId) {
1356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mCallbackId = callbackId;
1366047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mCallbackWrapper = getModel().callbackWrapper(klass, method);
1376047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // now register the arguments as variables.
1386047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final ModelClass[] parameterTypes = method.getParameterTypes();
1396047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final List<CallbackArgExpr> args = mCallbackExprModel.getArguments();
1406047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (parameterTypes.length == args.size()) {
1416047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            for (int i = 0; i < parameterTypes.length; i++) {
1426047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                args.get(i).setClassFromCallback(parameterTypes[i]);
1436047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
1446047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1456047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // first convert to execution path because we may add additional expressions
1466047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mExecutionPath = ExecutionPath.createRoot();
1476047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        getExpr().toExecutionPath(mExecutionPath);
1486047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mCallbackExprModel.seal();
1496047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1506047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar}
151