edu.buaa.satla.analysis.core.arg.ARGState.java Source code

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.core.arg.ARGState.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  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.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package edu.buaa.satla.analysis.core.arg;

import static com.google.common.base.Preconditions.*;
import static com.google.common.collect.FluentIterable.from;
import static edu.buaa.satla.analysis.util.AbstractStates.extractLocation;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.core.defaults.AbstractSingleWrapperState;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.core.interfaces.Graphable;
import org.sosy_lab.cpachecker.core.interfaces.TargetableWithPredicatedAnalysis;

import com.google.common.base.Function;
import com.google.common.collect.Sets;

import edu.buaa.satla.analysis.util.UniqueIdGenerator;
import edu.buaa.satla.analysis.util.predicates.interfaces.BooleanFormula;
import edu.buaa.satla.analysis.util.predicates.interfaces.view.FormulaManagerView;

public class ARGState extends AbstractSingleWrapperState
        implements Comparable<ARGState>, TargetableWithPredicatedAnalysis, Graphable {

    private static final long serialVersionUID = 2608287648397165040L;

    // We use a List here although we would like to have a Set
    // because ArrayList is much more memory efficient than e.g. LinkedHashSet.
    // Also these collections are small and so a slow contains() method won't hurt.
    // To enforce set semantics, do not add elements except through addparent()!
    private final Collection<ARGState> children = new ArrayList<>(1);
    private final Collection<ARGState> parents = new ArrayList<>(1);

    private ARGState mCoveredBy = null;
    private Set<ARGState> mCoveredByThis = null; // lazy initialization because rarely needed

    // boolean which keeps track of which elements have already had their successors computed
    private boolean wasExpanded = false;
    private boolean mayCover = true;
    private boolean destroyed = false;
    private boolean hasCoveredParent = false;

    private ARGState mergedWith = null;

    private final int stateId;

    private static final UniqueIdGenerator idGenerator = new UniqueIdGenerator();

    public ARGState(@Nullable AbstractState pWrappedState, @Nullable ARGState pParentElement) {
        super(pWrappedState);
        stateId = idGenerator.getFreshId();
        if (pParentElement != null) {
            addParent(pParentElement);
        }
    }

    // parent & child relations

    /**
     * Get the parent elements of this state.
     * @return A unmodifiable collection of ARGStates without duplicates.
     */
    public Collection<ARGState> getParents() {
        return Collections.unmodifiableCollection(parents);
    }

    public void addParent(ARGState pOtherParent) {
        checkNotNull(pOtherParent);
        assert !destroyed : "Don't use destroyed ARGState " + this;

        // Manually enforce set semantics.
        if (!parents.contains(pOtherParent)) {
            assert !pOtherParent.children.contains(this);
            parents.add(pOtherParent);
            pOtherParent.children.add(this);
        } else {
            assert pOtherParent.children.contains(this);
        }
    }

    /**
     * Get the child elements of this state.
     * @return An unmodifiable collection of ARGStates without duplicates.
     */
    public Collection<ARGState> getChildren() {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        return Collections.unmodifiableCollection(children);
    }

    /**
     * Returns the edge from current state to child or Null, if there is no edge.
     * Both forward and backward analysis must be considered!
     */
    @Nullable
    public CFAEdge getEdgeToChild(ARGState pChild) {
        checkArgument(children.contains(pChild));

        CFANode currentLoc = extractLocation(this);
        CFANode childLoc = extractLocation(pChild);

        if (currentLoc.hasEdgeTo(childLoc)) { // Forwards
            return currentLoc.getEdgeTo(childLoc);

        } else if (currentLoc.getLeavingSummaryEdge() != null
                && currentLoc.getLeavingSummaryEdge().getSuccessor().equals(childLoc)) { // Forwards
            return currentLoc.getLeavingSummaryEdge();

        } else if (childLoc.hasEdgeTo(currentLoc)) { // Backwards
            return childLoc.getEdgeTo(currentLoc);

        } else if (currentLoc.getEnteringSummaryEdge() != null
                && currentLoc.getEnteringSummaryEdge().getSuccessor().equals(childLoc)) { // Backwards
            return currentLoc.getEnteringSummaryEdge();

        } else {
            return null;
        }
    }

    public Set<ARGState> getSubgraph() {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        Set<ARGState> result = new HashSet<>();
        Deque<ARGState> workList = new ArrayDeque<>();

        workList.add(this);

        while (!workList.isEmpty()) {
            ARGState currentElement = workList.removeFirst();
            if (result.add(currentElement)) {
                // currentElement was not in result
                workList.addAll(currentElement.children);
            }
        }
        return result;
    }

    // coverage

    public void setCovered(@Nonnull ARGState pCoveredBy) {
        checkState(!isCovered(), "Cannot cover already covered element %s", this);
        checkNotNull(pCoveredBy);
        checkArgument(pCoveredBy.mayCover, "Trying to cover with non-covering element %s", pCoveredBy);

        mCoveredBy = pCoveredBy;
        if (pCoveredBy.mCoveredByThis == null) {
            // lazy initialization because rarely needed
            pCoveredBy.mCoveredByThis = new LinkedHashSet<>(2);
        }
        pCoveredBy.mCoveredByThis.add(this);
    }

    public void uncover() {
        assert isCovered();
        assert mCoveredBy.mCoveredByThis.contains(this);

        mCoveredBy.mCoveredByThis.remove(this);
        mCoveredBy = null;
    }

    public boolean isCovered() {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        return mCoveredBy != null;
    }

    public ARGState getCoveringState() {
        checkState(isCovered());
        return mCoveredBy;
    }

    public Set<ARGState> getCoveredByThis() {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        if (mCoveredByThis == null) {
            return Collections.emptySet();
        } else {
            return Collections.unmodifiableSet(mCoveredByThis);
        }
    }

    public boolean mayCover() {
        return mayCover && !hasCoveredParent && !isCovered();
    }

    public void setNotCovering() {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        mayCover = false;
    }

    void setHasCoveredParent(boolean pHasCoveredParent) {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        hasCoveredParent = pHasCoveredParent;
    }

    // merged-with marker so that stop can return true for merged elements

    void setMergedWith(ARGState pMergedWith) {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        assert mergedWith == null : "Second merging of element " + this;

        mergedWith = pMergedWith;
    }

    public ARGState getMergedWith() {
        return mergedWith;
    }

    // was-expanded marker so we can identify open leafs

    boolean wasExpanded() {
        return wasExpanded;
    }

    void markExpanded() {
        wasExpanded = true;
    }

    void deleteChild(ARGState child) {
        assert (children.contains(child));
        children.remove(child);
        child.parents.remove(this);
    }

    // small and less important stuff

    public int getStateId() {
        return stateId;
    }

    public boolean isDestroyed() {
        return destroyed;
    }

    /**
     * The ordering of this class is the chronological creation order.
     */
    @Override
    public final int compareTo(ARGState pO) {
        return Integer.compare(this.stateId, pO.stateId);
    }

    @Override
    public final boolean equals(Object pObj) {
        // Object.equals() is consistent with our compareTo()
        // because stateId is a unique identifier.
        return super.equals(pObj);
    }

    @Override
    public final int hashCode() {
        // Object.hashCode() is consistent with our compareTo()
        // because stateId is a unique identifier.
        return super.hashCode();
    }

    public boolean isOlderThan(ARGState other) {
        return (stateId < other.stateId);
    }

    @Override
    public boolean isTarget() {
        return !hasCoveredParent && !isCovered() && super.isTarget();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (destroyed) {
            sb.append("Destroyed ");
        }
        if (mCoveredBy != null) {
            sb.append("Covered ");
        }
        sb.append("ARG State (Id: ");
        sb.append(stateId);
        if (!destroyed) {
            sb.append(", Parents: ");
            sb.append(stateIdsOf(parents));
            sb.append(", Children: ");
            sb.append(stateIdsOf(children));

            if (mCoveredBy != null) {
                sb.append(", Covered by: ");
                sb.append(mCoveredBy.stateId);
            } else {
                sb.append(", Covering: ");
                sb.append(stateIdsOf(getCoveredByThis()));
            }
        }
        sb.append(") ");
        sb.append(getWrappedState());
        return sb.toString();
    }

    @Override
    public String toDOTLabel() {
        if (getWrappedState() instanceof Graphable) {
            return ((Graphable) getWrappedState()).toDOTLabel();
        }
        return "";
    }

    @Override
    public boolean shouldBeHighlighted() {
        if (getWrappedState() instanceof Graphable) {
            return ((Graphable) getWrappedState()).shouldBeHighlighted();
        }
        return false;
    }

    private final Iterable<Integer> stateIdsOf(Iterable<ARGState> elements) {
        return from(elements).transform(TO_STATE_ID);
    }

    private static final Function<ARGState, Integer> TO_STATE_ID = new Function<ARGState, Integer>() {
        @Override
        public Integer apply(ARGState pInput) {
            return pInput.stateId;
        }
    };

    // removal from ARG

    /**
     * This method removes this element from the ARG by removing it from its
     * parents' children list and from its children's parents list.
     *
     * This method also removes the element from the covered set of the other
     * element covering this element, if it is covered.
     *
     * This means, if its children do not have any other parents, they will be not
     * reachable any more, i.e. they do not belong to the ARG any more. But those
     * elements will not be removed from the covered set.
     */
    public void removeFromARG() {
        assert !destroyed : "Don't use destroyed ARGState " + this;

        // clear children
        for (ARGState child : children) {
            assert (child.parents.contains(this));
            child.parents.remove(this);
        }
        children.clear();

        // clear parents
        for (ARGState parent : parents) {
            assert (parent.children.contains(this));
            parent.children.remove(this);
        }
        parents.clear();

        // clear coverage relation
        if (isCovered()) {
            assert mCoveredBy.mCoveredByThis.contains(this);

            mCoveredBy.mCoveredByThis.remove(this);
            mCoveredBy = null;
        }

        if (mCoveredByThis != null) {
            for (ARGState covered : mCoveredByThis) {
                covered.mCoveredBy = null;
            }
            mCoveredByThis.clear();
            mCoveredByThis = null;
        }

        destroyed = true;
    }

    /**
     * This method does basically the same as removeFromARG for this element, but
     * before destroying it, it will copy all relationships to other elements to
     * a new state. I.e., the replacement element will receive all parents and
     * children of this element, and it will also cover all elements which are
     * currently covered by this element.
     *
     * @param replacement
     */
    public void replaceInARGWith(ARGState replacement) {
        assert !destroyed : "Don't use destroyed ARGState " + this;
        assert !replacement.destroyed : "Don't use destroyed ARGState " + replacement;
        assert !isCovered() : "Not implemented: Replacement of covered element " + this;
        assert !replacement.isCovered() : "Cannot replace with covered element " + replacement;

        // copy children
        for (ARGState child : children) {
            assert (child.parents.contains(this)) : "Inconsistent ARG at " + this;
            child.parents.remove(this);
            child.addParent(replacement);
        }
        children.clear();

        for (ARGState parent : parents) {
            assert (parent.children.contains(this)) : "Inconsistent ARG at " + this;
            parent.children.remove(this);
            replacement.addParent(parent);
        }
        parents.clear();

        if (mCoveredByThis != null) {
            if (replacement.mCoveredByThis == null) {
                // lazy initialization because rarely needed
                replacement.mCoveredByThis = Sets.newHashSetWithExpectedSize(mCoveredByThis.size());
            }

            for (ARGState covered : mCoveredByThis) {
                assert covered.mCoveredBy == this : "Inconsistent coverage relation at " + this;
                covered.mCoveredBy = replacement;
                replacement.mCoveredByThis.add(covered);
            }

            mCoveredByThis.clear();
            mCoveredByThis = null;
        }

        destroyed = true;
    }

    @Override
    public BooleanFormula getErrorCondition(FormulaManagerView pFmgr) {
        if (isTarget() && super.getWrappedState() instanceof TargetableWithPredicatedAnalysis) {
            return ((TargetableWithPredicatedAnalysis) super.getWrappedState()).getErrorCondition(pFmgr);
        } else {
            return pFmgr.getBooleanFormulaManager().makeBoolean(false);
        }
    }
}