Pronto State

Pronto's state module contains a "state transition graph" API in the package com.tomgibara.pronto.state package. The module provides the ability to:

Editing Graphs

We take the simple state graph below as an example. This general state pattern should be familiar to developers who have used any of the common Java frameworks.

We begin by creating classes which model our states and transitions. Because our example is so simple, we can use two enums.

public enum State { constructed, initialized, started, destroyed } public enum Label { init, start, stop, destroy }

Now we define the graph by obtaining a factory, an empty graph from that factory and an editor from the empty graph. The editor features lots of methods for editing graphs but in this case we just need to add transitions. Note that adding the transitions via an editor automatically adds the associated states.

StateGraph createGraph() { //create the editor StateFactory factory = StateFactory.getInstance(); StateGraph<State, Label> empty = factory.emptyStateGraph(); StateGraphEditor<State, Label> editor = empty.newEditor(); //define the graph editor.addTransition(State.constructed, Label.init, State.initialized); editor.addTransition(State.initialized, Label.start, State.started); editor.addTransition(State.started, Label.stop, State.initialized); editor.addTransition(State.initialized, Label.destroy, State.destroyed); //create the graph return editor.getGraph(); }

Creating Engines

Now we create an engine that can use this graph to move between a set of states. Any actions to be performed in response to state changes should be performed by activators; engines are safe for concurrent use and do the work of ensuring that their associated activator receives events sequentially and in the correct order.

StateEngine createEngine() { StateGraph<State, Label> graph = createGraph(); StateFactory factory = StateFactory.getInstance(); StateActivator<State, Label, ?> activator = new StateActivator<State, Label, Object>() { public void changeState(State state) { System.out.println(state); } public void transitionState( StateTransition<State, Label> transition, Object parameter ) { System.out.println(transition); } }; return factory.newEngine(graph, activator); }

Java's generics make this look very convoluted, but it is very simple; we create a graph, create an activator that echos its calls to stdout and create an engine that combines the two.

At present, a StateActivator is required to create a StateEngine, this may be relaxed in future versions of the api.

Using Engines

Now we are in a position to use the engine, as in the following method.

void performTransitions() { StateEngine engine = createEngine(); //set the state explicitly engine.setState(State.constructed); //transition along a label engine.transition(null, Label.init, null); //transition to a state engine.transition(State.started, null, null); //transition to the same state engine.pathTransition(State.started, null, PathType.uniqueTransitions, null); //use the graph to identify a state State terminal = engine.getGraph().getTerminalState(); //transition to a state via a label engine.pathTransition(terminal, null, PathType.uniqueStates, null); }

Executing this method produces the following output. A line-by-line explanation follows.

constructed [transition from constructed to initialized via init] [transition from initialized to started via start] [transition from started to initialized via stop] [transition from initialized to started via start] [transition from started to initialized via stop] [transition from initialized to destroyed via destroy]

Other features of the API