Home | History | Annotate | Download | only in expr
      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 
     41     public LambdaExpr(Expr expr, CallbackExprModel callbackExprModel) {
     42         super(expr);
     43         mCallbackExprModel = callbackExprModel;
     44     }
     45 
     46     public Expr getExpr() {
     47         return getChildren().get(0);
     48     }
     49 
     50     public CallbackExprModel getCallbackExprModel() {
     51         return mCallbackExprModel;
     52     }
     53 
     54     @Override
     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
     62     protected List<Dependency> constructDependencies() {
     63         return Collections.emptyList();
     64     }
     65 
     66     public CallbackWrapper getCallbackWrapper() {
     67         return mCallbackWrapper;
     68     }
     69 
     70     @Override
     71     public Expr resolveListeners(ModelClass valueType, Expr parent) {
     72         return this;
     73     }
     74 
     75     @Override
     76     protected String computeUniqueKey() {
     77         return "callback" + mId;
     78     }
     79 
     80     @Override
     81     public boolean isDynamic() {
     82         return false;
     83     }
     84 
     85     @Override
     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
    101     public Expr cloneToModel(ExprModel model) {
    102         return model.lambdaExpr(getExpr().cloneToModel(model), (CallbackExprModel) model);
    103     }
    104 
    105     public String generateConstructor() {
    106         return getCallbackWrapper().constructForIdentifier(mCallbackId);
    107     }
    108 
    109     @Override
    110     public void markAsUsed() {
    111         super.markAsUsed();
    112     }
    113 
    114     @Override
    115     protected String getInvertibleError() {
    116         return "Lambda expressions cannot be inverted";
    117     }
    118 
    119     @Override
    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 
    126     public final ExecutionPath getExecutionPath() {
    127         return mExecutionPath;
    128     }
    129 
    130     public int getCallbackId() {
    131         return mCallbackId;
    132     }
    133 
    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