/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.databinding.tool.solver; import android.databinding.tool.expr.Expr; import android.databinding.tool.util.Preconditions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Represents all possible outcomes of an expressions with its branching. */ public class ExecutionPath { @Nullable //null for root and branches private final Expr mExpr; @NotNull private List mChildren = new ArrayList(); @Nullable private ExecutionBranch mTrueBranch; @Nullable private ExecutionBranch mFalseBranch; // values that we know due to branching private Map mKnownValues = new HashMap(); // expressions that are available right now private Set mScopeExpressions = new HashSet(); private final boolean mIsAlreadyEvaluated; public static ExecutionPath createRoot() { return new ExecutionPath(null, false); } private ExecutionPath(@Nullable Expr expr, boolean isAlreadyEvaluated) { mExpr = expr; mIsAlreadyEvaluated = isAlreadyEvaluated; } @Nullable public ExecutionPath addBranch(Expr pred, boolean expectedValue) { // TODO special predicates like Symbol(true, false) Preconditions.checkNull(expectedValue ? mTrueBranch : mFalseBranch, "Cannot add two " + expectedValue + "branches"); final Boolean knownValue = mKnownValues.get(pred); if (knownValue != null) { // we know the result. cut the branch if (expectedValue == knownValue) { // just add as a path return addPath(null); } else { // drop path. this cannot happen return null; } } else { ExecutionPath path = createPath(null); ExecutionBranch edge = new ExecutionBranch(path, pred, expectedValue); path.mKnownValues.put(pred, expectedValue); if (expectedValue) { if (mFalseBranch != null) { Preconditions.check(mFalseBranch.getConditional() == pred, "Cannot add" + " branches w/ different conditionals."); } mTrueBranch = edge; } else { if (mTrueBranch != null) { Preconditions.check(mTrueBranch.getConditional() == pred, "Cannot add" + " branches w/ different conditionals."); } mFalseBranch = edge; } return path; } } private ExecutionPath createPath(@Nullable Expr expr) { ExecutionPath path = new ExecutionPath(expr, expr == null || mScopeExpressions.contains(expr)); // now pass down all values etc path.mKnownValues.putAll(mKnownValues); path.mScopeExpressions.addAll(mScopeExpressions); return path; } @NotNull public ExecutionPath addPath(@Nullable Expr expr) { Preconditions.checkNull(mFalseBranch, "Cannot add path after branches are set"); Preconditions.checkNull(mTrueBranch, "Cannot add path after branches are set"); final ExecutionPath path = createPath(expr); if (expr != null) { mScopeExpressions.add(expr); path.mScopeExpressions.add(expr); } mChildren.add(path); return path; } public void debug(StringBuilder builder, int offset) { offset(builder, offset); if (mExpr != null || !mIsAlreadyEvaluated) { builder.append("expr:").append(mExpr == null ? "root" : mExpr.getUniqueKey()); builder.append(" isRead:").append(mIsAlreadyEvaluated); } else { builder.append("branch"); } if (!mKnownValues.isEmpty()) { builder.append(" I know:"); for (Map.Entry entry : mKnownValues.entrySet()) { builder.append(" "); builder.append(entry.getKey().getUniqueKey()); builder.append(" is ").append(entry.getValue()); } } for (ExecutionPath path : mChildren) { builder.append("\n"); path.debug(builder, offset); } if (mTrueBranch != null) { debug(builder, mTrueBranch, offset); } if (mFalseBranch != null) { debug(builder, mFalseBranch, offset); } } @Nullable public Expr getExpr() { return mExpr; } @NotNull public List getChildren() { return mChildren; } @Nullable public ExecutionBranch getTrueBranch() { return mTrueBranch; } @Nullable public ExecutionBranch getFalseBranch() { return mFalseBranch; } public boolean isAlreadyEvaluated() { return mIsAlreadyEvaluated; } private void debug(StringBuilder builder, ExecutionBranch branch, int offset) { builder.append("\n"); offset(builder, offset); builder.append("if ") .append(branch.getConditional().getUniqueKey()) .append(" is ").append(branch.getExpectedCondition()).append("\n"); branch.getPath().debug(builder, offset + 1); } private void offset(StringBuilder builder, int offset) { for (int i = 0; i < offset; i++) { builder.append(" "); } } public Map getKnownValues() { return mKnownValues; } }