/*
* StateMachine.java
*
* Created on 09 August 2006, 10:26
*/
package com.captaindebug.statemachine;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* This is a dumb state machine, demonstrating the 'Finite State Machine'. By
* dumb, I mean that it's decoupled from the processing of the data. Hence, it
* can be used over and again to solve different problem. In UML parlance, we
* can say that a StateMachine HASA number of Actions where each specific action
* class ISA AbstractAction. The StateMachine and the actions are associated
* with an enum - any set of enums
*
* @author Roger
* @Date 09 August 2006
*
* @Date Easter 2012 - updated
*/
public class StateMachine<T extends Enum<?>> {
private final byte[] inputBuffer = new byte[32768];
private T currentState;
private final Map<T, AbstractAction<T>> stateActionMap = new HashMap<T, AbstractAction<T>>();
public StateMachine(T startState) {
this.currentState = startState;
}
/**
* Main method that loops around and processes the input stream
*/
public void processStream(InputStream in) {
// Outer loop - continually refill the buffer until there's nothing
// left to read
try {
processBuffers(in);
terminate();
} catch (Exception ioe) {
throw new StateMachineException("Error processing input stream: "
+ ioe.getMessage(), ioe);
}
}
private void processBuffers(InputStream in) throws Exception {
for (int len = in.read(inputBuffer); (len != -1); len = in
.read(inputBuffer)) {
// Inner loop - process the contents of the Buffer
for (int i = 0; i < len; i++) {
processByte(inputBuffer[i]);
}
}
}
/**
* Deal with each individual byte in the buffer
*/
private void processByte(byte b) throws Exception {
// Get the set of actions associated with this state
AbstractAction<T> action = stateActionMap.get(currentState);
// do the action, get the next state
currentState = action.processByte(b, currentState);
}
/**
* The buffer is empty. Make sue that we tidy up
*/
private void terminate() throws Exception {
AbstractAction<T> action = stateActionMap.get(currentState);
action.terminate(currentState);
}
/**
* Add an action to the machine and associated state to the machine. A state
* can have more than one action associated with it
*/
public void addAction(T state, AbstractAction<T> action) {
stateActionMap.put(state, action);
}
/**
* Remove an action from the state machine
*/
public void removeAction(AbstractAction<T> action) {
stateActionMap.remove(action); // Remove the action - if it's there
}
}