package jsetool.data;
import java.io.Serializable;
import java.util.*;
/**
* @author Mike Lawson
*
* @since Oct 20, 2004
*
* The ExplorationState class represents an explored state generated from the
* two communication finite state machines.
*
* It is composed of the current state and communication channel for both machine 1 and 2
*
*
*/
public class ExplorationState implements Serializable
{
/** For now, we'll only support two CFSMs */
protected ExplorationStateContainer[] machines;
/** The states that are next after this one. */
protected Vector nextStates = new Vector();
/** True if the state has been full explored already. */
protected boolean explored = false;
/** Indicator of a deadlock state. */
protected boolean stateIsDeadlocked = false;
/** Indicator of an unspecified reception state. */
protected boolean stateIsUnspecifiedReception = false;
/** An optional attribute not used internally but can be used by a rendering engine to help itself.*/
protected Object associatedAttribute = null;
protected static HashMap knownExplorationStates = new HashMap();
protected static int numberOfExplorationStatesCreated = 0;
/**
* An inner class that allow us to store both a next state and a
* descriptive label for the transition.
*
* @author Mike Lawson
*
* @since Oct 23, 2004
*
*/
public class NextStateContainer implements Serializable
{
private ExplorationState es;
private String transitionLabel;
/**
* Constructs a new NextStateContainer
* @param es the next exploration state
* @param transitionLabel
*/
public NextStateContainer(ExplorationState es, String transitionLabel)
{
this.es = es;
this.transitionLabel = transitionLabel;
}
/**
* Gets the exploration state
* @return ExplorationState
*/
public ExplorationState getExplorationState()
{
return es;
}
/**
* Gets the transition label
* @return String
*/
public String getTransitionLabel()
{
return transitionLabel;
}
} // inner class NextStateContainer
//
// factory methods
//
/**
* Factories an empty (m1.initial, m2.initial, E, E) state
* @param m1 Machine 1
* @param m2 Machine 2
* @return ExplorationState
*/
public static ExplorationState emptyState(MachineState m1, MachineState m2)
{
ExplorationState es = new ExplorationState(m1, m2);
knownExplorationStates.put(""+es.hashCode(), es);
return es;
}
/**
* Factories a new ExlorationState based on the given state, modifying M1.
* M1 will be set to a new MachineState, and if a new message is given,
* the message will be added to the machines communication channel.
*
* @param stateToExplore The original state being explored on which the new state will be based
* @param target The new target for M1
* @param message Optional and may be null, the message that is to be added to m1's channel.
* @return ExplorationState
*/
public static ExplorationState newStateForM1(ExplorationState stateToExplore, MachineState target, String message)
{
ExplorationState newState = new ExplorationState(stateToExplore);
// Set the new state's new machine state to where the transition took us.
newState.getMachine1().setMachineState(target);
// Add the message to machine1's communication channel
if (message != null)
{
newState.getMachine1().appendMessage(message);
}
// look in known states, if we already know about this newly created
// state, return the FOUND state, if not, add this new state and return it.
ExplorationState foundState = lookForState(newState);
if (foundState == null)
{
knownExplorationStates.put("" + newState.hashCode(), newState);
return newState;
}
else
{
return foundState;
}
}
/**
* Factories a new ExlorationState based on the given state, modifying M2.
* M2 will be set to a new MachineState, and if a new message is given,
* the message will be added to the machines communication channel.
*
* @param stateToExplore The original state being explored on which the new state will be based
* @param target The new target for M2
* @param message Optional and may be null, the message that is to be added to m2's channel.
* @return ExplorationState
*/
public static ExplorationState newStateForM2(ExplorationState stateToExplore, MachineState target, String message)
{
ExplorationState newState = new ExplorationState(stateToExplore);
// Set the new state's new machine state to where the transition took us.
newState.getMachine2().setMachineState(target);
// Add the message to machine1's communication channel
if (message != null)
{
newState.getMachine2().appendMessage(message);
}
// look in known states, if we already know about this newly created
// state, return the FOUND state, if not, add this new state and return it.
ExplorationState foundState = lookForState(newState);
if (foundState == null)
{
knownExplorationStates.put("" + newState.hashCode(), newState);
return newState;
}
else
{
return foundState;
}
}
/**
* Looks for the given state in the set of known states,
* returns the FOUND state if found, null if not found.
* @param s The state to see if we already have.
* @return ExplorationState the FOUND state if found, null if not found.
*/
private static ExplorationState lookForState(ExplorationState s)
{
ExplorationState es = (ExplorationState)knownExplorationStates.get(""+s.hashCode());
return es;
}
/**
* Default constructor
*
*/
public ExplorationState()
{
}
/**
* Creates a new, deep copy of this object, except that the nextStates
* attribute is left empty.
*
* @param other
*/
public ExplorationState(ExplorationState other)
{
machines = new ExplorationStateContainer[2];
// Clone the other's state containers.
machines[0] = new ExplorationStateContainer(other.getMachine1());
machines[1] = new ExplorationStateContainer(other.getMachine2());
}
/**
* Constructs an ExplorationState with the given MachineStates. The
* communication channels for both machines will be initialized as empty.
*
* @param m1
* the first machine state
* @param m2
* the second machine state.
*/
public ExplorationState(MachineState m1, MachineState m2)
{
machines = new ExplorationStateContainer[2];
machines[0] = new ExplorationStateContainer();
machines[1] = new ExplorationStateContainer();
machines[0].setMachineState(m1);
machines[1].setMachineState(m2);
}
/**
* Return true if the given object is equal to this object. Equality is said
* to be true if the states of both objects match plus all the channels are
* exactly the same.
*
* @param obj
* the other object to compare
* @return boolean
*/
public boolean equals(Object obj)
{
if (!(obj instanceof ExplorationState))
{
return false;
}
ExplorationState es = (ExplorationState) obj;
return getMachine1().equals(es.getMachine1()) && getMachine2().equals(es.getMachine2());
}
/**
* Returns a hash code value for the object. This method is supported for the
* benefit of hashtables such as those provided by
* java.util.Hashtable
.
*
* @return a hash code value for this object.
*/
public int hashCode()
{
int cornedBeef = 37;
cornedBeef ^= getMachine1().hashCode();
cornedBeef ^= 37 * getMachine2().hashCode();
return cornedBeef;
}
/**
* Prints the string representation of this object in the 4-tuple format
* defined in class: (s1, s2, c1, c2)
*
* @return String
*/
public String toString()
{
String ret = "(" + machines[0].getMachineState() + "," + machines[1].getMachineState() + "," + machines[0].getCommunicationChannel() + "," + machines[1].getCommunicationChannel() + ")";
return ret;
}
/**
* Returns the object associated with machine 1
*
* @return Returns the machine1CommunicationChannel.
*/
public ExplorationStateContainer getMachine1()
{
return machines[0];
}
/**
* Returns the object associated with machine 2
*
* @return Returns the machine1CommunicationChannel.
*/
public ExplorationStateContainer getMachine2()
{
return machines[1];
}
/**
* Adds a new next state to this ExplorationState
* @param es The new ExplorationState discovered
* @param transitionLabel A descriptive label (e.g. m1: -r)
*/
public void addNextState(ExplorationState es, String transitionLabel)
{
NextStateContainer nsc = new NextStateContainer(es, transitionLabel);
nextStates.add(nsc);
}
/**
* Returns the number of next states
* @return int The number of next states.
*/
public int getNumberOfNextStates()
{
return nextStates.size();
}
/**
* Returns an iterator over the next states.
* @return Iterator
*/
public Iterator nextStateIterator()
{
return nextStates.iterator();
}
/**
* @return Returns the stateIsDeadlocked.
*/
public boolean isStateIsDeadlocked()
{
return stateIsDeadlocked;
}
/**
* Sets this state to deadlocked.
*/
public void setStateIsDeadlocked()
{
this.stateIsDeadlocked = true;
}
/**
* @return Returns the stateIsUnspecifiedReception.
*/
public boolean isStateIsUnspecifiedReception()
{
return stateIsUnspecifiedReception;
}
/**
* Sets this state to an unspecified reception
*/
public void setStateIsUnspecifiedReception()
{
this.stateIsUnspecifiedReception = true;
}
/**
* Sets this state as having been explored.
*/
public void setExplored()
{
explored = true;
}
/**
* @return Returns the explored.
*/
public boolean isExplored()
{
return explored;
}
/**
* @return Returns the associatedAttribute.
*/
public Object getAssociatedAttribute()
{
return associatedAttribute;
}
/**
* @param associatedAttribute The associatedAttribute to set.
*/
public void setAssociatedAttribute(Object associatedAttribute)
{
this.associatedAttribute = associatedAttribute;
}
/**
*
*/
public static void resetKnownStates()
{
knownExplorationStates = new HashMap();
}
}