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 package android.databinding.tool.solver; 17 18 import android.databinding.tool.expr.Expr; 19 import android.databinding.tool.util.Preconditions; 20 21 import org.jetbrains.annotations.NotNull; 22 import org.jetbrains.annotations.Nullable; 23 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.HashSet; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Set; 30 31 /** 32 * Represents all possible outcomes of an expressions with its branching. 33 */ 34 public class ExecutionPath { 35 @Nullable //null for root and branches 36 private final Expr mExpr; 37 @NotNull 38 private List<ExecutionPath> mChildren = new ArrayList<ExecutionPath>(); 39 40 @Nullable 41 private ExecutionBranch mTrueBranch; 42 43 @Nullable 44 private ExecutionBranch mFalseBranch; 45 46 // values that we know due to branching 47 private Map<Expr, Boolean> mKnownValues = new HashMap<Expr, Boolean>(); 48 49 // expressions that are available right now 50 private Set<Expr> mScopeExpressions = new HashSet<Expr>(); 51 52 private final boolean mIsAlreadyEvaluated; 53 createRoot()54 public static ExecutionPath createRoot() { 55 return new ExecutionPath(null, false); 56 } 57 ExecutionPath(@ullable Expr expr, boolean isAlreadyEvaluated)58 private ExecutionPath(@Nullable Expr expr, boolean isAlreadyEvaluated) { 59 mExpr = expr; 60 mIsAlreadyEvaluated = isAlreadyEvaluated; 61 } 62 63 @Nullable addBranch(Expr pred, boolean expectedValue)64 public ExecutionPath addBranch(Expr pred, boolean expectedValue) { 65 // TODO special predicates like Symbol(true, false) 66 Preconditions.checkNull(expectedValue ? mTrueBranch : mFalseBranch, 67 "Cannot add two " + expectedValue + "branches"); 68 final Boolean knownValue = mKnownValues.get(pred); 69 if (knownValue != null) { 70 // we know the result. cut the branch 71 if (expectedValue == knownValue) { 72 // just add as a path 73 return addPath(null); 74 } else { 75 // drop path. this cannot happen 76 return null; 77 } 78 } else { 79 ExecutionPath path = createPath(null); 80 ExecutionBranch edge = new ExecutionBranch(path, pred, expectedValue); 81 path.mKnownValues.put(pred, expectedValue); 82 if (expectedValue) { 83 if (mFalseBranch != null) { 84 Preconditions.check(mFalseBranch.getConditional() == pred, "Cannot add" 85 + " branches w/ different conditionals."); 86 } 87 mTrueBranch = edge; 88 } else { 89 if (mTrueBranch != null) { 90 Preconditions.check(mTrueBranch.getConditional() == pred, "Cannot add" 91 + " branches w/ different conditionals."); 92 } 93 mFalseBranch = edge; 94 } 95 return path; 96 } 97 } 98 createPath(@ullable Expr expr)99 private ExecutionPath createPath(@Nullable Expr expr) { 100 ExecutionPath path = new ExecutionPath(expr, expr == null || 101 mScopeExpressions.contains(expr)); 102 // now pass down all values etc 103 path.mKnownValues.putAll(mKnownValues); 104 path.mScopeExpressions.addAll(mScopeExpressions); 105 return path; 106 } 107 108 @NotNull addPath(@ullable Expr expr)109 public ExecutionPath addPath(@Nullable Expr expr) { 110 Preconditions.checkNull(mFalseBranch, "Cannot add path after branches are set"); 111 Preconditions.checkNull(mTrueBranch, "Cannot add path after branches are set"); 112 final ExecutionPath path = createPath(expr); 113 if (expr != null) { 114 mScopeExpressions.add(expr); 115 path.mScopeExpressions.add(expr); 116 } 117 mChildren.add(path); 118 return path; 119 } 120 debug(StringBuilder builder, int offset)121 public void debug(StringBuilder builder, int offset) { 122 offset(builder, offset); 123 if (mExpr != null || !mIsAlreadyEvaluated) { 124 builder.append("expr:").append(mExpr == null ? "root" : mExpr.getUniqueKey()); 125 builder.append(" isRead:").append(mIsAlreadyEvaluated); 126 } else { 127 builder.append("branch"); 128 } 129 if (!mKnownValues.isEmpty()) { 130 builder.append(" I know:"); 131 for (Map.Entry<Expr, Boolean> entry : mKnownValues.entrySet()) { 132 builder.append(" "); 133 builder.append(entry.getKey().getUniqueKey()); 134 builder.append(" is ").append(entry.getValue()); 135 } 136 } 137 for (ExecutionPath path : mChildren) { 138 builder.append("\n"); 139 path.debug(builder, offset); 140 } 141 if (mTrueBranch != null) { 142 debug(builder, mTrueBranch, offset); 143 } 144 if (mFalseBranch != null) { 145 debug(builder, mFalseBranch, offset); 146 } 147 } 148 149 @Nullable getExpr()150 public Expr getExpr() { 151 return mExpr; 152 } 153 154 @NotNull getChildren()155 public List<ExecutionPath> getChildren() { 156 return mChildren; 157 } 158 159 @Nullable getTrueBranch()160 public ExecutionBranch getTrueBranch() { 161 return mTrueBranch; 162 } 163 164 @Nullable getFalseBranch()165 public ExecutionBranch getFalseBranch() { 166 return mFalseBranch; 167 } 168 isAlreadyEvaluated()169 public boolean isAlreadyEvaluated() { 170 return mIsAlreadyEvaluated; 171 } 172 debug(StringBuilder builder, ExecutionBranch branch, int offset)173 private void debug(StringBuilder builder, ExecutionBranch branch, int offset) { 174 builder.append("\n"); 175 offset(builder, offset); 176 builder.append("if ") 177 .append(branch.getConditional().getUniqueKey()) 178 .append(" is ").append(branch.getExpectedCondition()).append("\n"); 179 branch.getPath().debug(builder, offset + 1); 180 } 181 offset(StringBuilder builder, int offset)182 private void offset(StringBuilder builder, int offset) { 183 for (int i = 0; i < offset; i++) { 184 builder.append(" "); 185 } 186 } 187 getKnownValues()188 public Map<Expr, Boolean> getKnownValues() { 189 return mKnownValues; 190 } 191 } 192