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 17package android.databinding.tool.expr; 18 19import android.databinding.tool.reflection.ModelAnalyzer; 20import android.databinding.tool.reflection.ModelClass; 21import android.databinding.tool.solver.ExecutionPath; 22import android.databinding.tool.writer.KCode; 23 24import java.util.ArrayList; 25import java.util.BitSet; 26import java.util.List; 27 28public 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