import java.util.Hashtable; import java.util.Enumeration; public class State extends Object { private String label; // A unique label for this state private Hashtable incoming; // Maps incoming states to their transitions private Hashtable outgoing; // Maps events to their transitions // Constructor: you must specify a label for each state. public State(String label) { this.label=label; incoming = new Hashtable(); outgoing = new Hashtable(); } // Get the label for this state public final String getLabel() { return label; } // Dump this object's contents to stdout, in a LISP-style list, for debugging purposes public void displayMe() { Enumeration e; System.out.print(" (state "+label); e= outgoing.keys(); if (e.hasMoreElements()) System.out.println(); while (e != null && e.hasMoreElements()) { String key = (String)e.nextElement(); Transition t = (Transition)outgoing.get(key); System.out.print(" "+key+" -> "); t.displayMe(); } System.out.print(" )\n"); } // Add a transition for the specified letter. // If there is already a transition for that letter, throw an exception, // because we only want to deal with deterministic finite state machines. public void addTransition(Letter event, State next) throws FSMException { if (event == null || next == null) { throw new NullValueException("Passed null to "+label+".addTransition()"); } else { if (outgoing.containsKey(event.getLabel())) { throw new DuplicateTransition( label + " already has a transition for " + event.getLabel()); } else { Transition t = new Transition(event,next); outgoing.put(event.getLabel(),t); next.addReverseTransition(this,event); } } } // Add a reverse transition that leads to this state. public void addReverseTransition(State prev,Letter event) throws FSMException { if (event == null || prev == null) { throw new NullValueException("Passed null to "+label+".addReverseTransition()"); } else { incoming.put(prev.getLabel(),event.getLabel()); } } // Delete the transition for the specified letter. // If there is no transition for the specified letter, throw an exception. public void deleteTransition(Letter event) throws FSMException { if (event == null) { throw new NullValueException("Passed null to "+label+".deleteTransition()"); } else { if (!outgoing.containsKey(event.getLabel())) { throw new NoSuchTransition(label + " has no transition for '" + event.getLabel()+"'"); } else { State next; next = (State) outgoing.get(event.getLabel()); next.deleteReverseTransition(this); outgoing.remove(event.getLabel()); } } } // Deletes an incoming transition public void deleteReverseTransition(State prev) { incoming.remove(prev.getLabel()); } // Returns the state that corresponds to the event given. // If there is no transition for the specified event, an exception is thrown. public State nextState(Letter event) throws FSMException { if (event == null) { throw new NullValueException("Passed null to "+label+".nextState()"); } else if (!outgoing.containsKey(event.getLabel())) { throw new NoSuchTransition(label + " has no transition for " + "'"+event.getLabel()+"'"); } else { Transition t = (Transition) outgoing.get(event.getLabel()); State next = t.doesTransitionOccur(event); if (next==null) { throw new NullValueException("got null from doesTransitionOccur"); } else { return next; } } } public boolean isEmpty() { // Returns true if this state has no outgoing transitions. return outgoing.isEmpty(); } public boolean isReached() { // Returns true if this state has any incoming transitions. // Note that this does not necessarily mean it is reachable from the // initial state. return !incoming.isEmpty(); } public boolean isTrap() { // Returns true if this state is a trap state, i.e. // it has no transitions that go to another state Enumeration t = outgoing.elements(); while (t.hasMoreElements()) if (outgoing.get(t.nextElement())!=this) return false; return true; } };