All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.databinding.tool.solver.ExecutionPath Maven / Gradle / Ivy

/*
 * 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;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy