new fluent syntax

develop
Vyacheslav Boyko 2022-07-07 02:15:12 +03:00
parent 4428c70007
commit cf19f75872
32 changed files with 763 additions and 435 deletions

View 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);
}

View 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;
}
}

View 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);
}
}

View 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);
}
}

View 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> {
}

View File

@ -1,18 +1,18 @@
package ru.bvn13.fsm;
package me.bvn13.fsm;
/**
* 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 FSM fsm;
protected void setFSM(FSM fsm) {
private Fsm fsm;
protected void setFSM(Fsm fsm) {
this.fsm = fsm;
}
public FSM getFSM() {
public Fsm getFSM() {
return this.fsm;
}

View 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) {
}
}

View 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;
}
}

View File

@ -0,0 +1,12 @@
package me.bvn13.fsm;
/**
* State handler
* @param <T>
*/
@FunctionalInterface
public interface StateHandler<T extends Fsm> {
void handle(T fms);
}

View 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);
}

View 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) {
}
}

View 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) {
}
}

View File

@ -1,11 +1,11 @@
package ru.bvn13.fsm.Exceptions;
package me.bvn13.fsm.exceptions;
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) {
super(message);
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -1,14 +1,14 @@
package ru.bvn13.fsm.Exceptions;
package me.bvn13.fsm.exceptions;
import java.io.PrintWriter;
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;
public FSMException(String message) {
public FsmException(String message) {
this.message = message;
}
protected String getStackTraceString() {

View File

@ -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");
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -1,8 +0,0 @@
package ru.bvn13.fsm;
/**
* Created by bvn13 on 28.12.2017.
*/
public interface ConditionBehaviour {
boolean check();
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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");
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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() {
}
}

View File

@ -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();
}
}
}

View 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());
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}