SequentialNodeFactory.java
package edu.udel.cis.vsl.gmc.seq;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import edu.udel.cis.vsl.gmc.TraceStepIF;
/**
* The factory to get a GMC search {@link SequentialNode}, if the
* {@link SequentialNode} has been seen before, the seen {@link SequentialNode}
* will be returned, otherwise, a new {@link SequentialNode} will be created and
* returned.
*
* @author Yihao Yan (yanyihao)
*/
public class SequentialNodeFactory<STATE, TRANSITION> {
/**
* Maps each STATE to a unique {@link SequentialNode}.
*/
private Map<STATE, SequentialNode<STATE>> nodeMap = new HashMap<>();
/**
* The counter used to count the # of {@link SequentialNode} cached in
* {@link #nodeMap}.
*/
private int nodeCounter = 0;
/**
* A {@link StateManager} can be used to compute the next state and
* normalize a state.
*/
private StateManager<STATE, TRANSITION> stateManager;
private boolean saveStates = true;
private static int NOT_SAVED = -1;
public SequentialNodeFactory(StateManager<STATE, TRANSITION> stateManager,
boolean saveStates) {
this.stateManager = stateManager;
this.saveStates = saveStates;
}
/**
* <p>
* Implements the fly-weight pattern and normalize a state. Note that only
* normalized and canonical state will have id.
* </p>
*
* @param state
* @return the existing {@link SequentialNode} mapped to the state, or a new
* {@link SequentialNode} mapped to the state if there was no entry
* in the map for the key state. Note that the
* {@link SequentialNode} will always store the normalized or
* simplified version of {@code state}.
*/
public SequentialNode<STATE> getNode(TraceStepIF<STATE> traceStep) {
STATE state = traceStep.getFinalState();
if (saveStates) {
SequentialNode<STATE> result = nodeMap.get(state);
if (result == null) {
stateManager.normalize(traceStep);
STATE normalizedState = traceStep.getFinalState();
if (normalizedState != state) {
result = nodeMap.get(normalizedState);
if (result == null) {
result = new SequentialNode<STATE>(normalizedState,
nodeCounter++);
nodeMap.put(normalizedState, result);
}
} else {
result = new SequentialNode<STATE>(state, nodeCounter++);
}
nodeMap.put(state, result);
}
return result;
} else
return new SequentialNode<STATE>(state, NOT_SAVED);
}
/**
* Get the node associated to the given state, null there is no such a node.
*
* @param state
* The state whose associated node will be returned.
* @return the node associated to the given state, null there is no such a
* node.
*/
public SequentialNode<STATE> getNode(STATE state) {
return nodeMap.get(state);
}
/**
* Construct a new stack entry which will be pushed onto the stack.
*
* @param node
* The {@link SequentialNode} that wraps the source state.
* @param transitions
* This could be the ample set or ample set complement of the
* source state.
* @param full
* Whether {@code transitions} is ample set complement or not.
* @return The newly constructed {@link StackEntry}.
*/
public StackEntry<STATE, TRANSITION> newStackEntry(
SequentialNode<STATE> node, Collection<TRANSITION> transitions,
int offset) {
return new StackEntry<>(node, transitions, offset);
}
/**
* @return the number of search nodes saved.
*/
public int numOfSearchNodeSaved() {
return nodeMap.size();
}
/**
* Get the {@link SequentialNode} associated with the initial state.
*
* @param initState
* @return
*/
public SequentialNode<STATE> getInitialNode(STATE initState) {
SequentialNode<STATE> initNode;
if (saveStates) {
initNode = new SequentialNode<STATE>(initState, nodeCounter++);
} else
initNode = new SequentialNode<STATE>(initState, NOT_SAVED);
nodeMap.put(initState, initNode);
return initNode;
}
/**
* Look up if there exists a {@link SequentialNode} associated with a
* non-initial state, if there is, return the {@link SequentialNode},
* otherwise return null.
*
* @param state
* @return
*/
SequentialNode<STATE> lookup(STATE state) {
return nodeMap.get(state);
}
}