Home | History | Annotate | Download | only in expr
      1 /*
      2  * Copyright (C) 2015 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.reflection.ModelAnalyzer;
     20 import android.databinding.tool.reflection.ModelClass;
     21 import android.databinding.tool.solver.ExecutionPath;
     22 import android.databinding.tool.writer.KCode;
     23 
     24 import java.util.ArrayList;
     25 import java.util.BitSet;
     26 import java.util.List;
     27 
     28 public class TernaryExpr extends Expr {
     29 
     30     TernaryExpr(Expr pred, Expr ifTrue, Expr ifFalse) {
     31         super(pred, ifTrue, ifFalse);
     32     }
     33 
     34     public Expr getPred() {
     35         return getChildren().get(0);
     36     }
     37 
     38     public Expr getIfTrue() {
     39         return getChildren().get(1);
     40     }
     41 
     42     public Expr getIfFalse() {
     43         return getChildren().get(2);
     44     }
     45 
     46     @Override
     47     protected String computeUniqueKey() {
     48         return "?:" + super.computeUniqueKey();
     49     }
     50 
     51     @Override
     52     public String getInvertibleError() {
     53         if (getPred().isDynamic()) {
     54             return "The condition of a ternary operator must be constant: " +
     55                     getPred().toFullCode();
     56         }
     57         final String trueInvertible = getIfTrue().getInvertibleError();
     58         if (trueInvertible != null) {
     59             return trueInvertible;
     60         } else {
     61             return getIfFalse().getInvertibleError();
     62         }
     63     }
     64 
     65     @Override
     66     protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
     67         final Expr ifTrue = getIfTrue();
     68         final Expr ifFalse = getIfFalse();
     69         if (isNullLiteral(ifTrue)) {
     70             return ifFalse.getResolvedType();
     71         } else if (isNullLiteral(ifFalse)) {
     72             return ifTrue.getResolvedType();
     73         }
     74         return modelAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(),
     75                 getIfFalse().getResolvedType());
     76     }
     77 
     78     private static boolean isNullLiteral(Expr expr) {
     79         final ModelClass type = expr.getResolvedType();
     80         return (type.isObject() && (expr instanceof SymbolExpr) &&
     81                 "null".equals(((SymbolExpr) expr).getText()));
     82     }
     83 
     84     @Override
     85     protected List<Dependency> constructDependencies() {
     86         List<Dependency> deps = new ArrayList<Dependency>();
     87         Expr predExpr = getPred();
     88         final Dependency pred = new Dependency(this, predExpr);
     89         pred.setMandatory(true);
     90         deps.add(pred);
     91 
     92         Expr ifTrueExpr = getIfTrue();
     93         if (ifTrueExpr.isDynamic()) {
     94             deps.add(new Dependency(this, ifTrueExpr, predExpr, true));
     95         }
     96         Expr ifFalseExpr = getIfFalse();
     97         if (ifFalseExpr.isDynamic()) {
     98             deps.add(new Dependency(this, ifFalseExpr, predExpr, false));
     99         }
    100         return deps;
    101     }
    102 
    103     @Override
    104     public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
    105         List<ExecutionPath> executionPaths = getPred().toExecutionPath(paths);
    106         // now optionally add others
    107         List<ExecutionPath> result = new ArrayList<ExecutionPath>();
    108         for (ExecutionPath path : executionPaths) {
    109             ExecutionPath ifTrue = path.addBranch(getPred(), true);
    110             if (ifTrue != null) {
    111                 result.addAll(getIfTrue().toExecutionPath(ifTrue));
    112             }
    113             ExecutionPath ifFalse = path.addBranch(getPred(), false);
    114             if (ifFalse != null) {
    115                 result.addAll(getIfFalse().toExecutionPath(ifFalse));
    116             }
    117         }
    118         return addJustMeToExecutionPath(result);
    119     }
    120 
    121     @Override
    122     protected BitSet getPredicateInvalidFlags() {
    123         return getPred().getInvalidFlags();
    124     }
    125 
    126     @Override
    127     protected KCode generateCode() {
    128         return new KCode()
    129                 .app("(", getPred().toCode())
    130                 .app(") ? (", getIfTrue().toCode())
    131                 .app(") : (", getIfFalse().toCode())
    132                 .app(")");
    133     }
    134 
    135     @Override
    136     public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
    137         final Expr pred = getPred().cloneToModel(model);
    138         final Expr ifTrue = getIfTrue().generateInverse(model, value, bindingClassName);
    139         final Expr ifFalse = getIfFalse().generateInverse(model, value, bindingClassName);
    140         return model.ternary(pred, ifTrue, ifFalse);
    141     }
    142 
    143     @Override
    144     public Expr cloneToModel(ExprModel model) {
    145         return model.ternary(getPred().cloneToModel(model), getIfTrue().cloneToModel(model),
    146                 getIfFalse().cloneToModel(model));
    147     }
    148 
    149     @Override
    150     public boolean isConditional() {
    151         return true;
    152     }
    153 
    154     @Override
    155     public String toString() {
    156         return getPred().toString() + " ? " + getIfTrue() + " : " + getIfFalse();
    157     }
    158 }
    159