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

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.core.arg.ARGPath.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 edu.buaa.satla.analysis.util.AbstractStates.extractLocation;
import static edu.buaa.satla.analysis.util.CFAUtils.leavingEdges;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.sosy_lab.common.Appender;
import org.sosy_lab.common.JSON;
import org.sosy_lab.common.Pair;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

/**
 * ARGPath contains a non-empty path through the ARG
 * consisting of both a sequence of states
 * and the edges between them.
 * Very often, the first state is the root state of the ARG,
 * and the last state is a target state, though this is not guaranteed.
 *
 * The number of states and edges is currently always equal.
 * To achieve this, the last edge is an outgoing edge of the location of the last state.
 * If you want only the edges up to the last state and not beyond,
 * use {@link #getInnerEdges()} or {@link #pathIterator()}
 * instead of {@link #asEdgesList()} (this is recommended).
 *
 * States on this path cannot be null.
 * Edges can be null,
 * if there is no corresponding CFAEdge between two consecutive abstract states.
 *
 * The recommended way to iterate through an ARGPath if you need both states and edges
 * is to use {@link #pathIterator()}.
 *
 * The usual way to get an ARGPath instance is from methods in {@link ARGUtils}
 * such as {@link ARGUtils#getOnePathTo(ARGState)} and {@link ARGUtils#getRandomPath(ARGState)}.
 */
@Immutable
public class ARGPath implements Appender {

    private final ImmutableList<ARGState> states;
    private final List<CFAEdge> edges; // immutable, but may contain null

    ARGPath(List<ARGState> pStates) {
        checkArgument(!pStates.isEmpty(), "ARGPaths may not be empty");
        states = ImmutableList.copyOf(pStates);

        List<CFAEdge> edgesBuilder = new ArrayList<>(states.size());
        for (int i = 0; i < states.size() - 1; i++) {
            ARGState parent = states.get(i);
            ARGState child = states.get(i + 1);
            edgesBuilder.add(parent.getEdgeToChild(child)); // may return null
        }

        // For backwards compatibility,
        // the list of states and edges should have same length.
        // For this, we add one outgoing edge of the last state to the list.
        CFANode lastLoc = extractLocation(states.get(states.size() - 1));
        edgesBuilder.add(leavingEdges(lastLoc).first().orNull());

        edges = Collections.unmodifiableList(edgesBuilder);
        assert states.size() == edges.size();
    }

    ARGPath(List<ARGState> pStates, List<CFAEdge> pEdges) {
        checkArgument(!pStates.isEmpty(), "ARGPaths may not be empty");
        checkArgument(pStates.size() == pEdges.size(), "ARGPaths must have equal number of states and edges");
        CFAEdge lastEdge = pEdges.get(pEdges.size() - 1);
        if (lastEdge != null) {
            CFANode lastLoc = extractLocation(pStates.get(pStates.size() - 1));
            checkArgument(leavingEdges(lastLoc).contains(lastEdge));
        }

        states = ImmutableList.copyOf(pStates);
        edges = Collections.unmodifiableList(new ArrayList<>(pEdges));
    }

    public ImmutableList<ARGState> asStatesList() {
        return states;
    }

    public List<CFAEdge> asEdgesList() {
        return edges;
    }

    /**
     * Only return the list of edges between the states,
     * excluding the one edge after the last state.
     * The result of this method is always one element shorter
     * than {@link #asEdgesList()}.
     */
    public List<CFAEdge> getInnerEdges() {
        return edges.subList(0, edges.size() - 1);
    }

    public ImmutableSet<ARGState> getStateSet() {
        return ImmutableSet.copyOf(states);
    }

    public MutableARGPath mutableCopy() {
        MutableARGPath result = new MutableARGPath();
        Iterables.addAll(result, Pair.zipWithPadding(states, edges));
        return result;
    }

    /**
     * Create a fresh {@link PathIterator} for this path,
     * with its position at the first state.
     */
    public PathIterator pathIterator() {
        return new PathIterator();
    }

    public int size() {
        return states.size();
    }

    public ARGState getFirstState() {
        return states.get(0);
    }

    public ARGState getLastState() {
        return Iterables.getLast(states);
    }

    @Override
    public void appendTo(Appendable appendable) throws IOException {
        Joiner.on('\n').skipNulls().appendTo(appendable, getInnerEdges());
    }

    @Override
    public String toString() {
        return Joiner.on('\n').skipNulls().join(getInnerEdges());
    }

    public void toJSON(Appendable sb) throws IOException {
        List<Map<?, ?>> path = new ArrayList<>(size());
        for (Pair<ARGState, CFAEdge> pair : Pair.zipWithPadding(states, edges)) {
            Map<String, Object> elem = new HashMap<>();
            ARGState argelem = pair.getFirst();
            CFAEdge edge = pair.getSecond();
            if (edge == null) {
                continue; // in this case we do not need the edge
            }
            elem.put("argelem", argelem.getStateId());
            elem.put("source", edge.getPredecessor().getNodeNumber());
            elem.put("target", edge.getSuccessor().getNodeNumber());
            elem.put("desc", edge.getDescription().replaceAll("\n", " "));
            elem.put("val", "");
            elem.put("line", edge.getFileLocation().getStartingLineNumber());
            elem.put("file", edge.getFileLocation().getFileName());
            path.add(elem);
        }
        JSON.writeJSONString(path, sb);
    }

    /**
     * An {@link Iterator}-like class for iterating through an {@link ARGPath}
     * providing access to both the abstract states and the edges.
     * The iterator's position is always at an abstract state,
     * and from this position allows access to the abstract state
     * and the edges before and after this state.
     *
     * A typical use case would look like this:
     * <code>
     * PathIterator it = path.pathIterator();
     * while (it.hasNext()) {
     *   handleState(it.getAbstractState());
     *   if (it.hasNext()) {
     *     handleEdge(it.getOutgoingEdge());
     *   }
     * }
     * </code>
     *
     * or like this:
     * <code>
     * PathIterator it = path.pathIterator();
     * handleFirstState(it.getAbstractState()); // safe because paths are never empty
     * while (it.hasNext()) {
     *   handleEdge(it.getIncomingEdge());
     *   handleState(it.getAbstractState());
     * }
     * </code>
     */
    public class PathIterator {

        private int pos = 0; // the index of the current stat

        /**
         * Check whether there is at least one more state in the path.
         */
        public boolean hasNext() {
            return pos < states.size() - 1;
        }

        /**
         * Advance the iterator by one position.
         * @throws IllegalStateException If {@link #hasNext()} would return false.
         */
        public void advance() throws IllegalStateException {
            checkState(hasNext(), "No more states in PathIterator.");
            pos++;
        }

        /**
         * Get the current position of the iterator
         * (first state is at position 0).
         */
        public int getIndex() {
            return pos;
        }

        /**
         * Get the abstract state at the current position.
         * Note that unlike {@link Iterator#next()}, this does not change the iterator's state.
         * @return A non-null {@link ARGState}.
         */
        public ARGState getAbstractState() {
            return states.get(pos);
        }

        /**
         * Get the edge before the current abstract state.
         * May not be called before {@link #advance()} was called once
         * (there is no edge before the first state).
         * @return A {@link CFAEdge} or null, if there is no edge between these two states.
         */
        public @Nullable CFAEdge getIncomingEdge() {
            checkState(pos > 0, "First state in ARGPath has no incoming edge.");
            return edges.get(pos - 1);
        }

        /**
         * Get the edge after the current abstract state.
         * May not be called when {@link #hasNext()} would return false
         * (there is no edge after the last state).
         * @return A {@link CFAEdge} or null, if there is no edge between these two states.
         */
        public @Nullable CFAEdge getOutgoingEdge() {
            checkState(hasNext(), "Last state in ARGPath has no outgoing edge.");
            return edges.get(pos);
        }
    }
}