Merge branch 'release/2.1.7'

This commit is contained in:
Vyacheslav Boyko 2022-07-12 23:27:44 +03:00
commit 7dd25dbfb5
11 changed files with 120 additions and 29 deletions

View File

@ -7,7 +7,7 @@
<groupId>me.bvn13.fsm</groupId> <groupId>me.bvn13.fsm</groupId>
<artifactId>fsm</artifactId> <artifactId>fsm</artifactId>
<version>2.1.6</version> <version>2.1.7</version>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -13,6 +13,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import static java.lang.String.format;
/** /**
* <p> * <p>
* <b>Final State Machine</b> * <b>Final State Machine</b>
@ -239,6 +241,20 @@ public class Fsm<T extends Fsm, E> {
addTransition(fromState, toState.getName(), condition); addTransition(fromState, toState.getName(), condition);
} }
/**
* Provides a possibility to initialize FSM in custom State
* @param name State name (must be added before)
*/
protected void setCurrentState(String name) {
try {
this.currentState = this.states.get(name);
} catch (NullPointerException e) {
throw new NotInitializedException(format("Unable to find state '%s'", name), e);
}
this.done = currentState.isFinish();
this.currentState.beforeEvent();
}
private void switchToNextState(E event) { private void switchToNextState(E event) {
if (!transitions.containsKey(currentState.getName())) { if (!transitions.containsKey(currentState.getName())) {
throw new TransitionMissedException(currentState.getName()); throw new TransitionMissedException(currentState.getName());
@ -263,9 +279,9 @@ public class Fsm<T extends Fsm, E> {
} }
private void nextState(State<E> state, E event) { private void nextState(State<E> state, E event) {
state.beforeEvent();
previousState = currentState; previousState = currentState;
currentState = state; currentState = state;
currentState.beforeEvent();
} }
private void checkStateExist(String name) throws StateAlreadyExistsException { private void checkStateExist(String name) throws StateAlreadyExistsException {

View File

@ -31,6 +31,11 @@ public class FsmBuilder<T extends Fsm, E> {
return fsm; return fsm;
} }
public T startingAt(String name) {
fsm.setCurrentState(name);
return fsm;
}
T getFsm() { T getFsm() {
return fsm; return fsm;
} }

View File

@ -7,6 +7,6 @@ package me.bvn13.fsm;
@FunctionalInterface @FunctionalInterface
public interface StateHandler<T extends Fsm> { public interface StateHandler<T extends Fsm> {
void handle(T fms); void handle(T fsm);
} }

View File

@ -8,6 +8,6 @@ package me.bvn13.fsm;
@FunctionalInterface @FunctionalInterface
public interface StateProcessor<T extends Fsm, E> { public interface StateProcessor<T extends Fsm, E> {
void process(T fms, E event); void process(T fsm, E event);
} }

View File

@ -5,7 +5,7 @@ import me.bvn13.fsm.StateHandler;
public class DummyHandler<T extends Fsm> implements StateHandler<T> { public class DummyHandler<T extends Fsm> implements StateHandler<T> {
@Override @Override
public void handle(T fms) { public void handle(T fsm) {
} }
} }

View File

@ -5,7 +5,7 @@ import me.bvn13.fsm.StateProcessor;
public class DummyProcessor<T extends Fsm,E> implements StateProcessor<T,E> { public class DummyProcessor<T extends Fsm,E> implements StateProcessor<T,E> {
@Override @Override
public void process(T fms, E event) { public void process(T fsm, E event) {
} }
} }

View File

@ -2,6 +2,8 @@ package me.bvn13.fsm.exceptions;
import java.util.List; import java.util.List;
import static java.lang.String.format;
/** /**
* is thrown if there are more than 1 appropriate transition from current state * is thrown if there are more than 1 appropriate transition from current state
*/ */
@ -10,11 +12,14 @@ public class AmbiguousTransitionException extends FsmException {
super(message); super(message);
} }
public AmbiguousTransitionException(String from, List<String> next) { public AmbiguousTransitionException(String from, List<String> next) {
super(""); super(format("Ambiguous transition from state %s. Candidates are: %s", from, join(next)));
String msg = ""; }
for (String to : next) {
msg += (msg.length() > 0 ? ", " : "") + to; private static String join(List<String> list) {
StringBuilder msg = new StringBuilder();
for (String to : list) {
msg.append(msg.length() > 0 ? ", " : "").append(to);
} }
this.message = String.format("Ambiguous transition from state %s. Candidates are: %s", from, msg); return msg.toString();
} }
} }

View File

@ -1,23 +1,14 @@
package me.bvn13.fsm.exceptions; package me.bvn13.fsm.exceptions;
import java.io.PrintWriter;
import java.io.StringWriter;
/** /**
* Parent FSM exception class * 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; super(message);
} }
protected String getStackTraceString() {
StringWriter sw = new StringWriter(); public FsmException(String message, Throwable cause) {
PrintWriter pw = new PrintWriter(sw); super(message, cause);
this.printStackTrace(pw);
return sw.toString();
}
public void printStackTrace() {
System.out.println(String.format("FSMException: %s / %s", message, getStackTraceString()));
} }
} }

View File

@ -7,7 +7,12 @@ public class NotInitializedException extends FsmException {
public NotInitializedException(String message) { public NotInitializedException(String message) {
super(message); super(message);
} }
public NotInitializedException(String message, Exception e) {
super(message, e);
}
public NotInitializedException() { public NotInitializedException() {
super("FSM is not inited"); super("FSM is not initialized");
} }
} }

View File

@ -92,6 +92,8 @@ public class FsmTest {
.withAfterHandler(fsm -> initAfter.set(true)) .withAfterHandler(fsm -> initAfter.set(true))
.withProcessor((fsm, event) -> initProcess.set(true)) .withProcessor((fsm, event) -> initProcess.set(true))
.end() .end()
.state("intermediate")
.end()
.finish("finish") .finish("finish")
.withBeforeHandler(fsm -> finishBefore.set(true)) .withBeforeHandler(fsm -> finishBefore.set(true))
.withAfterHandler(fsm -> finishAfter.set(true)) .withAfterHandler(fsm -> finishAfter.set(true))
@ -99,17 +101,21 @@ public class FsmTest {
.end() .end()
.withTransition() .withTransition()
.from("init") .from("init")
.to("intermediate")
.checking((fsm, event) -> true)
.end()
.withTransition()
.from("intermediate")
.to("finish") .to("finish")
.checking((fsm, event) -> true) .checking((fsm, event) -> true)
.end() .end()
.create() .create();
;
// @formatter:on // @formatter:on
simpleFsm.process("");
simpleFsm.process(""); simpleFsm.process("");
//Assert.assertEquals("finish", simpleFsm.getCurrentState().getName()); Assert.assertEquals("finish", simpleFsm.getCurrentState().getName());
Assert.assertTrue(initBefore.get()); Assert.assertTrue(initBefore.get());
Assert.assertTrue(initProcess.get()); Assert.assertTrue(initProcess.get());
Assert.assertTrue(initAfter.get()); Assert.assertTrue(initAfter.get());
@ -119,4 +125,67 @@ public class FsmTest {
} }
@Test
public void newSyntaxCustomState() {
AtomicBoolean initBefore = new AtomicBoolean(false);
AtomicBoolean initAfter = new AtomicBoolean(false);
AtomicBoolean initProcess = new AtomicBoolean(false);
AtomicBoolean intermediateBefore = new AtomicBoolean(false);
AtomicBoolean intermediateAfter = new AtomicBoolean(false);
AtomicBoolean intermediateProcess = new AtomicBoolean(false);
AtomicBoolean finishBefore = new AtomicBoolean(false);
AtomicBoolean finishAfter = new AtomicBoolean(false);
AtomicBoolean finishProcess = new AtomicBoolean(false);
// @formatter:off
SimpleFsm<String> simpleFsm = Fsm
.<SimpleFsm<String>, String>from(SimpleFsm::new)
.withStates()
.from("init")
.withBeforeHandler(fsm -> initBefore.set(true))
.withAfterHandler(fsm -> initAfter.set(true))
.withProcessor((fsm, event) -> initProcess.set(true))
.end()
.state("intermediate")
.withBeforeHandler(fsm -> intermediateBefore.set(true))
.withAfterHandler(fsm -> intermediateAfter.set(true))
.withProcessor((fsm, event) -> intermediateProcess.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("intermediate")
.checking((fsm, event) -> true)
.end()
.withTransition()
.from("intermediate")
.to("finish")
.checking((fsm, event) -> true)
.end()
.startingAt("intermediate")
;
// @formatter:on
simpleFsm.process("");
Assert.assertEquals("finish", simpleFsm.getCurrentState().getName());
Assert.assertFalse(initBefore.get());
Assert.assertFalse(initProcess.get());
Assert.assertFalse(initAfter.get());
Assert.assertTrue(intermediateBefore.get());
Assert.assertTrue(intermediateAfter.get());
Assert.assertTrue(intermediateProcess.get());
Assert.assertTrue(finishBefore.get());
Assert.assertFalse(finishProcess.get());
Assert.assertFalse(finishAfter.get());
}
} }