new fluent syntax
This commit is contained in:
parent
4428c70007
commit
cf19f75872
9
src/main/java/me/bvn13/fsm/Condition.java
Normal file
9
src/main/java/me/bvn13/fsm/Condition.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Condition of transitions
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Condition<T extends Fsm, E> {
|
||||||
|
boolean check(T fsm, E event);
|
||||||
|
}
|
34
src/main/java/me/bvn13/fsm/ConditionBuilder.java
Normal file
34
src/main/java/me/bvn13/fsm/ConditionBuilder.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
public class ConditionBuilder<T extends Fsm, E> {
|
||||||
|
|
||||||
|
private final FsmBuilder<T,E> fsmBuilder;
|
||||||
|
private String from;
|
||||||
|
private String to;
|
||||||
|
private Condition<T,E> condition;
|
||||||
|
|
||||||
|
ConditionBuilder(FsmBuilder<T,E> fsmBuilder) {
|
||||||
|
this.fsmBuilder = fsmBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionBuilder<T,E> from(String from) {
|
||||||
|
this.from = from;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionBuilder<T,E> to(String to) {
|
||||||
|
this.to = to;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionBuilder<T,E> checking(Condition<T,E> condition) {
|
||||||
|
this.condition = condition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsmBuilder<T,E> end() {
|
||||||
|
fsmBuilder.addTransition(from, to, condition);
|
||||||
|
return fsmBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
267
src/main/java/me/bvn13/fsm/Fsm.java
Normal file
267
src/main/java/me/bvn13/fsm/Fsm.java
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.exceptions.AmbiguousTransitionException;
|
||||||
|
import me.bvn13.fsm.exceptions.BrokenTransitionException;
|
||||||
|
import me.bvn13.fsm.exceptions.ConditionAlreadyExistsException;
|
||||||
|
import me.bvn13.fsm.exceptions.FsmException;
|
||||||
|
import me.bvn13.fsm.exceptions.NotInitializedException;
|
||||||
|
import me.bvn13.fsm.exceptions.StateAlreadyExistsException;
|
||||||
|
import me.bvn13.fsm.exceptions.TransitionMissedException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* <b>Final State Machine</b><br/>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* <ol>
|
||||||
|
* Each state machine must be prepared with:
|
||||||
|
* <li>Initial state</li>
|
||||||
|
* <li>Finish state - may be not several states</li>
|
||||||
|
* <li>Intermediate states - optionally</li>
|
||||||
|
* <li>All transitions needed</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <ol>
|
||||||
|
* Each {@link State} may be specified with handlers:
|
||||||
|
* <li>Before handler - is called right before FSM changes INTO this state</li>
|
||||||
|
* <li>After handler - is called right before FSM changes FROM this state to another</li>
|
||||||
|
* <li>Processor - the method to process events</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transition is the Rule provides FSM the possibility to change between states.
|
||||||
|
*
|
||||||
|
* Each transition must be determined in terms of:
|
||||||
|
* <ol>
|
||||||
|
* <li>From State - mandatory</li>
|
||||||
|
* <li>To State - mandatory</li>
|
||||||
|
* <li>Condition - optionally. If specified, the FSM will check the condition in order to check the possibility
|
||||||
|
* to change from FROM State into TO State</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Simple way to use it - to construct an inherited class specified with the type of events to be processed
|
||||||
|
* during transitions.
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* SimpleFsm<String> simpleFsm = SimpleFsm
|
||||||
|
* .<SimpleFsm<String>, String>withStates(SimpleFsm::new)
|
||||||
|
* .from("init")
|
||||||
|
* .withBeforeHandler(fsm -> initBefore.set(true))
|
||||||
|
* .withAfterHandler(fsm -> initAfter.set(true))
|
||||||
|
* .withProcessor((fsm, event) -> initProcess.set(true))
|
||||||
|
* .end()
|
||||||
|
* .finish("finish")
|
||||||
|
* .withBeforeHandler(fsm -> finishBefore.set(true))
|
||||||
|
* .withAfterHandler(fsm -> finishAfter.set(true))
|
||||||
|
* .withProcessor((fsm, event) -> finishProcess.set(true))
|
||||||
|
* .end()
|
||||||
|
* .withTransition()
|
||||||
|
* .from("init")
|
||||||
|
* .to("finish")
|
||||||
|
* .checking((fsm, event) -> true)
|
||||||
|
* .end()
|
||||||
|
* .create();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* {@link SimpleFsm}
|
||||||
|
*/
|
||||||
|
public class Fsm<T extends Fsm, E> {
|
||||||
|
|
||||||
|
private boolean done = false;
|
||||||
|
private State<E> initialState;
|
||||||
|
private State<E> currentState;
|
||||||
|
private State<E> previousState;
|
||||||
|
private final Map<String, State<E>> states = new HashMap<>();
|
||||||
|
private final Map<String, Map<String, Condition<T,E>>> transitions = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a builder
|
||||||
|
*
|
||||||
|
* @param supplier the original FSM inherited class constructor. You may specify '{@code () -> new SimpleFsm()}' in parameter
|
||||||
|
* @return FsmBuilder
|
||||||
|
* @param <T> the original FSM inherited class type
|
||||||
|
* @param <E> the class type of Events to be processed
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Fsm,E> FsmBuilder<T,E> withStates(Supplier<T> supplier) {
|
||||||
|
return new FsmBuilder<>(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To initialize FSM into initial state
|
||||||
|
* @throws NotInitializedException
|
||||||
|
*/
|
||||||
|
public void init() throws NotInitializedException {
|
||||||
|
currentState = initialState;
|
||||||
|
if (currentState == null) {
|
||||||
|
throw new NotInitializedException();
|
||||||
|
}
|
||||||
|
done = false;
|
||||||
|
previousState = null;
|
||||||
|
currentState.beforeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current state
|
||||||
|
*
|
||||||
|
* @return {@link State}
|
||||||
|
*/
|
||||||
|
public State<E> getCurrentState() {
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns previous state
|
||||||
|
*
|
||||||
|
* @return {@link State}
|
||||||
|
*/
|
||||||
|
public State<E> getPreviousState() {
|
||||||
|
return previousState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method to handle every event
|
||||||
|
*
|
||||||
|
* @param event event
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void process(E event) throws FsmException {
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentState.afterEvent();
|
||||||
|
if (currentState.isFinish()) {
|
||||||
|
done = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!transitions.containsKey(currentState.getName())) {
|
||||||
|
throw new TransitionMissedException(currentState.getName());
|
||||||
|
}
|
||||||
|
Map<String, Condition<T,E>> conditions = transitions.get(currentState.getName());
|
||||||
|
List<String> nextStates = new ArrayList<>();
|
||||||
|
for (String key : conditions.keySet()) {
|
||||||
|
if (conditions.get(key) == null) {
|
||||||
|
nextStates.add(key);
|
||||||
|
} else if(conditions.get(key).check((T) this, event)) {
|
||||||
|
nextStates.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextStates.size() > 1) {
|
||||||
|
throw new AmbiguousTransitionException(currentState.getName(), nextStates);
|
||||||
|
}
|
||||||
|
if (nextStates.size() == 0) {
|
||||||
|
throw new BrokenTransitionException(currentState.getName());
|
||||||
|
}
|
||||||
|
State<E> nextState = states.get(nextStates.get(0));
|
||||||
|
nextState(nextState, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To specify initial state
|
||||||
|
*
|
||||||
|
* @param state {@link State}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void initState(State<E> state) throws FsmException {
|
||||||
|
state.setFSM(this);
|
||||||
|
addState(state);
|
||||||
|
initialState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To add another state
|
||||||
|
*
|
||||||
|
* @param state {@link State}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void addState(State<E> state) throws FsmException {
|
||||||
|
checkStateExist(state.getName());
|
||||||
|
state.setFSM(this);
|
||||||
|
this.states.put(state.getName(), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To set the transition up
|
||||||
|
*
|
||||||
|
* @param fromState {@link State}
|
||||||
|
* @param toState {@link State}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void addTransition(String fromState, String toState) throws FsmException {
|
||||||
|
storeTransition(fromState, toState, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To set the transition up
|
||||||
|
*
|
||||||
|
* @param fromState {@link State}
|
||||||
|
* @param toState {@link State}
|
||||||
|
* @param condition {@link Condition}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void addTransition(String fromState, String toState, Condition<T,E> condition) throws FsmException {
|
||||||
|
storeTransition(fromState, toState, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To set the transition up
|
||||||
|
*
|
||||||
|
* @param fromState {@link State}
|
||||||
|
* @param toState {@link State}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void addTransition(String fromState, State<E> toState) throws FsmException {
|
||||||
|
addState(toState);
|
||||||
|
addTransition(fromState, toState.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To set the transition up
|
||||||
|
*
|
||||||
|
* @param fromState {@link State}
|
||||||
|
* @param toState {@link State}
|
||||||
|
* @param condition {@link Condition}
|
||||||
|
* @throws FsmException
|
||||||
|
*/
|
||||||
|
public void addTransition(String fromState, State<E> toState, Condition<T,E> condition) throws FsmException {
|
||||||
|
addState(toState);
|
||||||
|
addTransition(fromState, toState.getName(), condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void nextState(State<E> state, E event) {
|
||||||
|
state.beforeEvent();
|
||||||
|
previousState = currentState;
|
||||||
|
currentState = state;
|
||||||
|
currentState.process(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkStateExist(String name) throws StateAlreadyExistsException {
|
||||||
|
if (this.states.containsKey(name)) {
|
||||||
|
throw new StateAlreadyExistsException(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeTransition(String fromState, String toState, Condition<T,E> condition) throws FsmException {
|
||||||
|
if (!transitions.containsKey(fromState)) {
|
||||||
|
transitions.put(fromState, new HashMap<>());
|
||||||
|
}
|
||||||
|
if (transitions.get(fromState).containsKey(toState)) {
|
||||||
|
throw new ConditionAlreadyExistsException(fromState, toState);
|
||||||
|
}
|
||||||
|
transitions.get(fromState).put(toState, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
src/main/java/me/bvn13/fsm/FsmBuilder.java
Normal file
52
src/main/java/me/bvn13/fsm/FsmBuilder.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class FsmBuilder<T extends Fsm, E> {
|
||||||
|
|
||||||
|
private final T fsm;
|
||||||
|
|
||||||
|
FsmBuilder(Supplier<T> supplier) {
|
||||||
|
fsm = supplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> from(String state) {
|
||||||
|
return new StateBuilder<>(this, state, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> state(String state) {
|
||||||
|
return new StateBuilder<>(this, state, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> finish(String state) {
|
||||||
|
return new StateBuilder<>(this, state, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionBuilder<T,E> withTransition() {
|
||||||
|
return new ConditionBuilder<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T create() {
|
||||||
|
fsm.init();
|
||||||
|
return fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getFsm() {
|
||||||
|
return fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void addState(State<E> state, boolean isInitial) {
|
||||||
|
if (isInitial) {
|
||||||
|
fsm.initState(state);
|
||||||
|
} else {
|
||||||
|
fsm.addState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void addTransition(String from, String to, Condition<T,E> condition) {
|
||||||
|
fsm.addTransition(from, to, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9
src/main/java/me/bvn13/fsm/SimpleFsm.java
Normal file
9
src/main/java/me/bvn13/fsm/SimpleFsm.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple FSM
|
||||||
|
* @param <E> the type of Events
|
||||||
|
*/
|
||||||
|
public class SimpleFsm<E> extends Fsm<SimpleFsm, E> {
|
||||||
|
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
package ru.bvn13.fsm;
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by bvn13 on 28.12.2017.
|
* Created by bvn13 on 28.12.2017.
|
||||||
*/
|
*/
|
||||||
public class State implements StateBehaviour {
|
public class State<E> implements StateBehaviour<E> {
|
||||||
|
|
||||||
private String name;
|
private final String name;
|
||||||
private boolean finish;
|
private boolean finish;
|
||||||
|
|
||||||
private FSM fsm;
|
private Fsm fsm;
|
||||||
protected void setFSM(FSM fsm) {
|
protected void setFSM(Fsm fsm) {
|
||||||
this.fsm = fsm;
|
this.fsm = fsm;
|
||||||
}
|
}
|
||||||
public FSM getFSM() {
|
public Fsm getFSM() {
|
||||||
return this.fsm;
|
return this.fsm;
|
||||||
}
|
}
|
||||||
|
|
20
src/main/java/me/bvn13/fsm/StateBehaviour.java
Normal file
20
src/main/java/me/bvn13/fsm/StateBehaviour.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State behavior
|
||||||
|
*/
|
||||||
|
public interface StateBehaviour<E> {
|
||||||
|
|
||||||
|
default void beforeEvent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void afterEvent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void process(E event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/main/java/me/bvn13/fsm/StateBuilder.java
Normal file
59
src/main/java/me/bvn13/fsm/StateBuilder.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.dummy.DummyHandler;
|
||||||
|
import me.bvn13.fsm.dummy.DummyProcessor;
|
||||||
|
|
||||||
|
public class StateBuilder<T extends Fsm, E> {
|
||||||
|
|
||||||
|
private final FsmBuilder<T,E> fsmBuilder;
|
||||||
|
private final String name;
|
||||||
|
private final boolean isInitial;
|
||||||
|
private final boolean isFinishing;
|
||||||
|
private StateHandler<T> beforeHandler = new DummyHandler<>();
|
||||||
|
private StateHandler<T> afterHandler = new DummyHandler<>();
|
||||||
|
private StateProcessor<T,E> processor = new DummyProcessor<>();
|
||||||
|
|
||||||
|
StateBuilder(FsmBuilder<T,E> fsmBuilder, String name, boolean isInitial, boolean isFinishing) {
|
||||||
|
assert !(isInitial && isFinishing);
|
||||||
|
this.fsmBuilder = fsmBuilder;
|
||||||
|
this.name = name;
|
||||||
|
this.isInitial = isInitial;
|
||||||
|
this.isFinishing = isFinishing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsmBuilder<T,E> end() {
|
||||||
|
fsmBuilder.addState(new State<E>(name, isFinishing) {
|
||||||
|
@Override
|
||||||
|
public void beforeEvent() {
|
||||||
|
beforeHandler.handle(fsmBuilder.getFsm());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEvent() {
|
||||||
|
afterHandler.handle(fsmBuilder.getFsm());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(E event) {
|
||||||
|
processor.process(fsmBuilder.getFsm(), event);
|
||||||
|
}
|
||||||
|
}, isInitial);
|
||||||
|
return fsmBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> withBeforeHandler(StateHandler<T> handler) {
|
||||||
|
this.beforeHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> withAfterHandler(StateHandler<T> handler) {
|
||||||
|
this.afterHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateBuilder<T,E> withProcessor(StateProcessor<T,E> processor) {
|
||||||
|
this.processor = processor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/main/java/me/bvn13/fsm/StateHandler.java
Normal file
12
src/main/java/me/bvn13/fsm/StateHandler.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State handler
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface StateHandler<T extends Fsm> {
|
||||||
|
|
||||||
|
void handle(T fms);
|
||||||
|
|
||||||
|
}
|
13
src/main/java/me/bvn13/fsm/StateProcessor.java
Normal file
13
src/main/java/me/bvn13/fsm/StateProcessor.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package me.bvn13.fsm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State processor
|
||||||
|
* @param <T> the type of FSM inherited class
|
||||||
|
* @param <E> the type of Events
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface StateProcessor<T extends Fsm, E> {
|
||||||
|
|
||||||
|
void process(T fms, E event);
|
||||||
|
|
||||||
|
}
|
11
src/main/java/me/bvn13/fsm/dummy/DummyHandler.java
Normal file
11
src/main/java/me/bvn13/fsm/dummy/DummyHandler.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package me.bvn13.fsm.dummy;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.Fsm;
|
||||||
|
import me.bvn13.fsm.StateHandler;
|
||||||
|
|
||||||
|
public class DummyHandler<T extends Fsm> implements StateHandler<T> {
|
||||||
|
@Override
|
||||||
|
public void handle(T fms) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java
Normal file
11
src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package me.bvn13.fsm.dummy;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.Fsm;
|
||||||
|
import me.bvn13.fsm.StateProcessor;
|
||||||
|
|
||||||
|
public class DummyProcessor<T extends Fsm,E> implements StateProcessor<T,E> {
|
||||||
|
@Override
|
||||||
|
public void process(T fms, E event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by bvn13 on 28.12.2017.
|
* is thrown if there are more than 1 appropriate transition from current state
|
||||||
*/
|
*/
|
||||||
public class AmbiguousTransitionException extends FSMException {
|
public class AmbiguousTransitionException extends FsmException {
|
||||||
public AmbiguousTransitionException(String message) {
|
public AmbiguousTransitionException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is thrown if there are no further states from the current one
|
||||||
|
*/
|
||||||
|
public class BrokenTransitionException extends FsmException {
|
||||||
|
public BrokenTransitionException(String from) {
|
||||||
|
super("Broken transition from: "+from);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is thrown in case of adding a transition FROM->TO, but it is already defined
|
||||||
|
*/
|
||||||
|
public class ConditionAlreadyExistsException extends FsmException {
|
||||||
|
public ConditionAlreadyExistsException(String from, String to) {
|
||||||
|
super(String.format("Condition exists: %s -> %s", from, to));
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by bvn13 on 28.12.2017.
|
* Parent FSM exception class
|
||||||
*/
|
*/
|
||||||
public class FSMException extends RuntimeException {
|
public class FsmException extends RuntimeException {
|
||||||
protected String message;
|
protected String message;
|
||||||
public FSMException(String message) {
|
public FsmException(String message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
protected String getStackTraceString() {
|
protected String getStackTraceString() {
|
@ -0,0 +1,13 @@
|
|||||||
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is thrown in case of using FSM before being initialized
|
||||||
|
*/
|
||||||
|
public class NotInitializedException extends FsmException {
|
||||||
|
public NotInitializedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
public NotInitializedException() {
|
||||||
|
super("FSM is not inited");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is thrown when trying to add the state which was already added before
|
||||||
|
*/
|
||||||
|
public class StateAlreadyExistsException extends FsmException {
|
||||||
|
public StateAlreadyExistsException(String stateName) {
|
||||||
|
super(String.format("State exist: %s", stateName));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package me.bvn13.fsm.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is thrown if there are no registered transitions from current state
|
||||||
|
*/
|
||||||
|
public class TransitionMissedException extends FsmException {
|
||||||
|
public TransitionMissedException(String from) {
|
||||||
|
super(String.format("Missed conditions from: %s", from));
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package ru.bvn13.fsm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class Condition implements ConditionBehaviour {
|
|
||||||
|
|
||||||
private FSM fsm;
|
|
||||||
protected void setFSM(FSM fsm) {
|
|
||||||
this.fsm = fsm;
|
|
||||||
}
|
|
||||||
public FSM getFSM() {
|
|
||||||
return this.fsm;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package ru.bvn13.fsm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public interface ConditionBehaviour {
|
|
||||||
boolean check();
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class BrokenTransitionException extends FSMException {
|
|
||||||
public BrokenTransitionException(String from) {
|
|
||||||
super("Broken transition from: "+from);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class ConditionExistsException extends FSMException {
|
|
||||||
public ConditionExistsException(String from, String to) {
|
|
||||||
super(String.format("Condition exists: %s -> %s", from, to));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class NotInitedException extends FSMException {
|
|
||||||
public NotInitedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
public NotInitedException() {
|
|
||||||
super("FSM is not inited");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class StateExistsException extends FSMException {
|
|
||||||
public StateExistsException(String stateName) {
|
|
||||||
super(String.format("State exist: %s", stateName));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.bvn13.fsm.Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class TransitionMissedException extends FSMException {
|
|
||||||
public TransitionMissedException(String from) {
|
|
||||||
super(String.format("Missed conditions from: %s", from));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
package ru.bvn13.fsm;
|
|
||||||
|
|
||||||
import ru.bvn13.fsm.Exceptions.*;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class FSM {
|
|
||||||
|
|
||||||
private boolean done = false;
|
|
||||||
private State initialState;
|
|
||||||
private State currentState;
|
|
||||||
private State previousState;
|
|
||||||
private final Map<String, State> states;
|
|
||||||
private final Map<String, Map<String, ConditionBehaviour>> transitions;
|
|
||||||
|
|
||||||
public FSM() {
|
|
||||||
this.states = new HashMap<>();
|
|
||||||
this.transitions = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init() throws NotInitedException {
|
|
||||||
currentState = initialState;
|
|
||||||
if (currentState == null) {
|
|
||||||
throw new NotInitedException();
|
|
||||||
}
|
|
||||||
done = false;
|
|
||||||
previousState = null;
|
|
||||||
currentState.beforeEvent();
|
|
||||||
currentState.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
public State getCurrentState() {
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public State getPreviousState() {
|
|
||||||
return previousState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void next() throws FSMException {
|
|
||||||
if (done) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentState.afterEvent();
|
|
||||||
if (currentState.isFinish()) {
|
|
||||||
done = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!transitions.containsKey(currentState.getName())) {
|
|
||||||
throw new TransitionMissedException(currentState.getName());
|
|
||||||
}
|
|
||||||
Map<String, ConditionBehaviour> conditions = transitions.get(currentState.getName());
|
|
||||||
List<String> nextStates = new ArrayList<>();
|
|
||||||
for (String key : conditions.keySet()) {
|
|
||||||
if (conditions.get(key) == null) {
|
|
||||||
nextStates.add(key);
|
|
||||||
} else if(conditions.get(key).check()) {
|
|
||||||
nextStates.add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nextStates.size() > 1) {
|
|
||||||
throw new AmbiguousTransitionException(currentState.getName(), nextStates);
|
|
||||||
}
|
|
||||||
if (nextStates.size() == 0) {
|
|
||||||
throw new BrokenTransitionException(currentState.getName());
|
|
||||||
}
|
|
||||||
State nextState = states.get(nextStates.get(0));
|
|
||||||
nextState(nextState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void prev() throws FSMException {
|
|
||||||
if (done) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentState.afterEvent();
|
|
||||||
if (getPreviousState() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nextState(getPreviousState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initState(State state) throws FSMException {
|
|
||||||
state.setFSM(this);
|
|
||||||
addState(state);
|
|
||||||
initialState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addState(State state) throws FSMException {
|
|
||||||
checkStateExist(state.getName());
|
|
||||||
state.setFSM(this);
|
|
||||||
this.states.put(state.getName(), state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransition(String fromState, String toState) throws FSMException {
|
|
||||||
storeTransition(fromState, toState, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransition(String fromState, String toState, Condition condition) throws FSMException {
|
|
||||||
condition.setFSM(this);
|
|
||||||
storeTransition(fromState, toState, condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransition(String fromState, State toState) throws FSMException {
|
|
||||||
addState(toState);
|
|
||||||
addTransition(fromState, toState.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransition(String fromState, State toState, Condition condition) throws FSMException {
|
|
||||||
addState(toState);
|
|
||||||
addTransition(fromState, toState.getName(), condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nextState(State state) {
|
|
||||||
state.beforeEvent();
|
|
||||||
previousState = currentState;
|
|
||||||
currentState = state;
|
|
||||||
currentState.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkStateExist(String name) throws StateExistsException {
|
|
||||||
if (this.states.containsKey(name)) {
|
|
||||||
throw new StateExistsException(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeTransition(String fromState, String toState, Condition condition) throws FSMException {
|
|
||||||
if (!transitions.containsKey(fromState)) {
|
|
||||||
transitions.put(fromState, new HashMap<>());
|
|
||||||
}
|
|
||||||
if (transitions.get(fromState).containsKey(toState)) {
|
|
||||||
throw new ConditionExistsException(fromState, toState);
|
|
||||||
}
|
|
||||||
transitions.get(fromState).put(toState, condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package ru.bvn13.fsm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public interface StateBehaviour {
|
|
||||||
|
|
||||||
default void beforeEvent() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
default void afterEvent() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
default void process() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package ru.bvn13.fsm.examples;
|
|
||||||
|
|
||||||
import ru.bvn13.fsm.Condition;
|
|
||||||
import ru.bvn13.fsm.Exceptions.FSMException;
|
|
||||||
import ru.bvn13.fsm.Exceptions.NotInitedException;
|
|
||||||
import ru.bvn13.fsm.FSM;
|
|
||||||
import ru.bvn13.fsm.State;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class DialogApplication {
|
|
||||||
|
|
||||||
public static class DialogFSM extends FSM {
|
|
||||||
public String command;
|
|
||||||
private BufferedReader br = null;
|
|
||||||
|
|
||||||
public void readCommand() {
|
|
||||||
System.out.print("Command: ");
|
|
||||||
br = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
try {
|
|
||||||
command = br.readLine();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String argc[]) {
|
|
||||||
|
|
||||||
DialogFSM fsm = new DialogFSM();
|
|
||||||
|
|
||||||
try {
|
|
||||||
fsm.initState(new State("greeting") {
|
|
||||||
@Override
|
|
||||||
public void beforeEvent() {
|
|
||||||
System.out.println("Welcome!");
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
fsm.readCommand();
|
|
||||||
try {
|
|
||||||
fsm.next();
|
|
||||||
} catch (FSMException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void afterEvent() {
|
|
||||||
System.out.println("Your command: "+fsm.command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (FSMException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
fsm.addTransition("greeting", new State("hello", true) {
|
|
||||||
@Override
|
|
||||||
public void beforeEvent() {
|
|
||||||
System.out.println("Hello!");
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void afterEvent() {
|
|
||||||
System.out.println("DONE");
|
|
||||||
}
|
|
||||||
}, new Condition() {
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return fsm.command.equalsIgnoreCase("hello");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (FSMException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
fsm.addTransition("greeting", "greeting", new Condition() {
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return !fsm.command.equalsIgnoreCase("hello");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (FSMException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
fsm.init();
|
|
||||||
} catch (NotInitedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
123
src/test/java/me/bvn13/fsm/tests/FsmTest.java
Normal file
123
src/test/java/me/bvn13/fsm/tests/FsmTest.java
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package me.bvn13.fsm.tests;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.ConditionBuilder;
|
||||||
|
import me.bvn13.fsm.Fsm;
|
||||||
|
import me.bvn13.fsm.FsmBuilder;
|
||||||
|
import me.bvn13.fsm.SimpleFsm;
|
||||||
|
import me.bvn13.fsm.State;
|
||||||
|
import me.bvn13.fsm.StateBuilder;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by bvn13 on 28.12.2017.
|
||||||
|
*/
|
||||||
|
public class FsmTest {
|
||||||
|
|
||||||
|
public static class NamedFsm extends Fsm<NamedFsm, String> {
|
||||||
|
|
||||||
|
public NamedFsm() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public NamedFsm setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void creatingFSM() {
|
||||||
|
|
||||||
|
NamedFsm namedFsm = (new NamedFsm()).setName("TEST FSM");
|
||||||
|
|
||||||
|
AtomicBoolean initStatedProcessed = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean firstStatedProcessed = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean anotherStatedProcessed = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
namedFsm.initState(new State<String>("init") {
|
||||||
|
@Override
|
||||||
|
public void process(String event) {
|
||||||
|
initStatedProcessed.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
namedFsm.addTransition("init", new State<String>("first", true) {
|
||||||
|
@Override
|
||||||
|
public void process(String event) {
|
||||||
|
firstStatedProcessed.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
namedFsm.addTransition("init", new State<String>("another", true) {
|
||||||
|
@Override
|
||||||
|
public void process(String event) {
|
||||||
|
anotherStatedProcessed.set(true);
|
||||||
|
}
|
||||||
|
}, (fsm, event) -> false);
|
||||||
|
|
||||||
|
namedFsm.init();
|
||||||
|
namedFsm.process(null);
|
||||||
|
|
||||||
|
Assert.assertEquals("first", namedFsm.getCurrentState().getName());
|
||||||
|
Assert.assertTrue(initStatedProcessed.get());
|
||||||
|
Assert.assertTrue(firstStatedProcessed.get());
|
||||||
|
Assert.assertFalse(anotherStatedProcessed.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newSyntax() {
|
||||||
|
|
||||||
|
AtomicBoolean initBefore = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean initAfter = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean initProcess = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean finishBefore = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean finishAfter = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean finishProcess = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
|
||||||
|
SimpleFsm<String> simpleFsm = SimpleFsm
|
||||||
|
.<SimpleFsm<String>, String>withStates(SimpleFsm::new)
|
||||||
|
.from("init")
|
||||||
|
.withBeforeHandler(fsm -> initBefore.set(true))
|
||||||
|
.withAfterHandler(fsm -> initAfter.set(true))
|
||||||
|
.withProcessor((fsm, event) -> initProcess.set(true))
|
||||||
|
.end()
|
||||||
|
.finish("finish")
|
||||||
|
.withBeforeHandler(fsm -> finishBefore.set(true))
|
||||||
|
.withAfterHandler(fsm -> finishAfter.set(true))
|
||||||
|
.withProcessor((fsm, event) -> finishProcess.set(true))
|
||||||
|
.end()
|
||||||
|
.withTransition()
|
||||||
|
.from("init")
|
||||||
|
.to("finish")
|
||||||
|
.checking((fsm, event) -> true)
|
||||||
|
.end()
|
||||||
|
.create()
|
||||||
|
;
|
||||||
|
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
simpleFsm.process("");
|
||||||
|
|
||||||
|
Assert.assertEquals("finish", simpleFsm.getCurrentState().getName());
|
||||||
|
Assert.assertTrue(initBefore.get());
|
||||||
|
Assert.assertTrue(initAfter.get());
|
||||||
|
Assert.assertFalse(initProcess.get());
|
||||||
|
Assert.assertTrue(finishBefore.get());
|
||||||
|
Assert.assertFalse(finishAfter.get());
|
||||||
|
Assert.assertTrue(finishProcess.get());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package me.bvn13.fsm.tests.examples;
|
||||||
|
|
||||||
|
import me.bvn13.fsm.Fsm;
|
||||||
|
import me.bvn13.fsm.State;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by bvn13 on 28.12.2017.
|
||||||
|
*/
|
||||||
|
public class DialogApplication {
|
||||||
|
|
||||||
|
public static class DialogFSM extends Fsm<DialogFSM, String> {
|
||||||
|
public String command;
|
||||||
|
|
||||||
|
public void readCommand() {
|
||||||
|
System.out.print("Command: ");
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
try {
|
||||||
|
command = br.readLine();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] argv) {
|
||||||
|
|
||||||
|
DialogFSM fsm = new DialogFSM();
|
||||||
|
|
||||||
|
fsm.initState(new State<String>("greeting") {
|
||||||
|
@Override
|
||||||
|
public void beforeEvent() {
|
||||||
|
System.out.println("Welcome!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(String event) {
|
||||||
|
fsm.readCommand();
|
||||||
|
fsm.process(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEvent() {
|
||||||
|
System.out.println("Your command: " + fsm.command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fsm.addTransition("greeting", new State<String>("hello", true) {
|
||||||
|
@Override
|
||||||
|
public void beforeEvent() {
|
||||||
|
System.out.println("Hello!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(String event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEvent() {
|
||||||
|
System.out.println("DONE");
|
||||||
|
}
|
||||||
|
}, (fsm12, event) -> fsm12.command.equalsIgnoreCase("hello"));
|
||||||
|
|
||||||
|
|
||||||
|
fsm.addTransition("greeting", "greeting", (fsm1, event) -> !fsm1.command.equalsIgnoreCase("hello"));
|
||||||
|
|
||||||
|
|
||||||
|
fsm.init();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
package ru.bvn13.fsm.tests;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import ru.bvn13.fsm.Condition;
|
|
||||||
import ru.bvn13.fsm.FSM;
|
|
||||||
import ru.bvn13.fsm.State;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bvn13 on 28.12.2017.
|
|
||||||
*/
|
|
||||||
public class FsmTest {
|
|
||||||
|
|
||||||
public static class NamedFSM extends FSM {
|
|
||||||
|
|
||||||
public NamedFSM() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public NamedFSM setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void creatingFSM() {
|
|
||||||
|
|
||||||
NamedFSM fsm = (new NamedFSM()).setName("TEST FSM");
|
|
||||||
|
|
||||||
fsm.initState(new State("init") {
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
System.out.println("" + fsm + " -> " + getName() + ": processed init");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fsm.addTransition("init", new State("first", true) {
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
System.out.println("" + fsm + " -> " + getName() + ": processed first");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fsm.addTransition("init", new State("another", true) {
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
System.out.println("" + fsm + " -> " + getName() + ": processed first");
|
|
||||||
}
|
|
||||||
}, new Condition() {
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fsm.init();
|
|
||||||
fsm.next();
|
|
||||||
|
|
||||||
Assert.assertEquals("first", fsm.getCurrentState().getName());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user