diff --git a/pom.xml b/pom.xml index ddeb71e..e9e560c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ me.bvn13.fsm fsm - 2.1.6 + 2.1.7 jar diff --git a/src/main/java/me/bvn13/fsm/Fsm.java b/src/main/java/me/bvn13/fsm/Fsm.java index 10c31c9..1253454 100644 --- a/src/main/java/me/bvn13/fsm/Fsm.java +++ b/src/main/java/me/bvn13/fsm/Fsm.java @@ -13,6 +13,8 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import static java.lang.String.format; + /** *

* Final State Machine @@ -239,6 +241,20 @@ public class Fsm { 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) { if (!transitions.containsKey(currentState.getName())) { throw new TransitionMissedException(currentState.getName()); @@ -263,9 +279,9 @@ public class Fsm { } private void nextState(State state, E event) { - state.beforeEvent(); previousState = currentState; currentState = state; + currentState.beforeEvent(); } private void checkStateExist(String name) throws StateAlreadyExistsException { diff --git a/src/main/java/me/bvn13/fsm/FsmBuilder.java b/src/main/java/me/bvn13/fsm/FsmBuilder.java index 42862e1..8bd0631 100644 --- a/src/main/java/me/bvn13/fsm/FsmBuilder.java +++ b/src/main/java/me/bvn13/fsm/FsmBuilder.java @@ -31,6 +31,11 @@ public class FsmBuilder { return fsm; } + public T startingAt(String name) { + fsm.setCurrentState(name); + return fsm; + } + T getFsm() { return fsm; } diff --git a/src/main/java/me/bvn13/fsm/StateHandler.java b/src/main/java/me/bvn13/fsm/StateHandler.java index d1cef68..31a4c7c 100644 --- a/src/main/java/me/bvn13/fsm/StateHandler.java +++ b/src/main/java/me/bvn13/fsm/StateHandler.java @@ -7,6 +7,6 @@ package me.bvn13.fsm; @FunctionalInterface public interface StateHandler { - void handle(T fms); + void handle(T fsm); } diff --git a/src/main/java/me/bvn13/fsm/StateProcessor.java b/src/main/java/me/bvn13/fsm/StateProcessor.java index f9fd70e..9371816 100644 --- a/src/main/java/me/bvn13/fsm/StateProcessor.java +++ b/src/main/java/me/bvn13/fsm/StateProcessor.java @@ -8,6 +8,6 @@ package me.bvn13.fsm; @FunctionalInterface public interface StateProcessor { - void process(T fms, E event); + void process(T fsm, E event); } diff --git a/src/main/java/me/bvn13/fsm/dummy/DummyHandler.java b/src/main/java/me/bvn13/fsm/dummy/DummyHandler.java index 9559db3..8e2f567 100644 --- a/src/main/java/me/bvn13/fsm/dummy/DummyHandler.java +++ b/src/main/java/me/bvn13/fsm/dummy/DummyHandler.java @@ -5,7 +5,7 @@ import me.bvn13.fsm.StateHandler; public class DummyHandler implements StateHandler { @Override - public void handle(T fms) { + public void handle(T fsm) { } } diff --git a/src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java b/src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java index d308a73..2d087b4 100644 --- a/src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java +++ b/src/main/java/me/bvn13/fsm/dummy/DummyProcessor.java @@ -5,7 +5,7 @@ import me.bvn13.fsm.StateProcessor; public class DummyProcessor implements StateProcessor { @Override - public void process(T fms, E event) { + public void process(T fsm, E event) { } } diff --git a/src/main/java/me/bvn13/fsm/exceptions/AmbiguousTransitionException.java b/src/main/java/me/bvn13/fsm/exceptions/AmbiguousTransitionException.java index ee6e210..c10c909 100644 --- a/src/main/java/me/bvn13/fsm/exceptions/AmbiguousTransitionException.java +++ b/src/main/java/me/bvn13/fsm/exceptions/AmbiguousTransitionException.java @@ -2,6 +2,8 @@ package me.bvn13.fsm.exceptions; import java.util.List; +import static java.lang.String.format; + /** * is thrown if there are more than 1 appropriate transition from current state */ @@ -10,11 +12,14 @@ public class AmbiguousTransitionException extends FsmException { super(message); } public AmbiguousTransitionException(String from, List next) { - super(""); - String msg = ""; - for (String to : next) { - msg += (msg.length() > 0 ? ", " : "") + to; + super(format("Ambiguous transition from state %s. Candidates are: %s", from, join(next))); + } + + private static String join(List 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(); } } diff --git a/src/main/java/me/bvn13/fsm/exceptions/FsmException.java b/src/main/java/me/bvn13/fsm/exceptions/FsmException.java index 5205ae9..8a48568 100644 --- a/src/main/java/me/bvn13/fsm/exceptions/FsmException.java +++ b/src/main/java/me/bvn13/fsm/exceptions/FsmException.java @@ -1,23 +1,14 @@ package me.bvn13.fsm.exceptions; -import java.io.PrintWriter; -import java.io.StringWriter; - /** * Parent FSM exception class */ public class FsmException extends RuntimeException { - protected String message; public FsmException(String message) { - this.message = message; + super(message); } - protected String getStackTraceString() { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - this.printStackTrace(pw); - return sw.toString(); - } - public void printStackTrace() { - System.out.println(String.format("FSMException: %s / %s", message, getStackTraceString())); + + public FsmException(String message, Throwable cause) { + super(message, cause); } } diff --git a/src/main/java/me/bvn13/fsm/exceptions/NotInitializedException.java b/src/main/java/me/bvn13/fsm/exceptions/NotInitializedException.java index a002848..3afad58 100644 --- a/src/main/java/me/bvn13/fsm/exceptions/NotInitializedException.java +++ b/src/main/java/me/bvn13/fsm/exceptions/NotInitializedException.java @@ -7,7 +7,12 @@ public class NotInitializedException extends FsmException { public NotInitializedException(String message) { super(message); } + + public NotInitializedException(String message, Exception e) { + super(message, e); + } + public NotInitializedException() { - super("FSM is not inited"); + super("FSM is not initialized"); } } diff --git a/src/test/java/me/bvn13/fsm/tests/FsmTest.java b/src/test/java/me/bvn13/fsm/tests/FsmTest.java index 38a3140..f79ff1e 100644 --- a/src/test/java/me/bvn13/fsm/tests/FsmTest.java +++ b/src/test/java/me/bvn13/fsm/tests/FsmTest.java @@ -92,6 +92,8 @@ public class FsmTest { .withAfterHandler(fsm -> initAfter.set(true)) .withProcessor((fsm, event) -> initProcess.set(true)) .end() + .state("intermediate") + .end() .finish("finish") .withBeforeHandler(fsm -> finishBefore.set(true)) .withAfterHandler(fsm -> finishAfter.set(true)) @@ -99,17 +101,21 @@ public class FsmTest { .end() .withTransition() .from("init") + .to("intermediate") + .checking((fsm, event) -> true) + .end() + .withTransition() + .from("intermediate") .to("finish") .checking((fsm, event) -> true) .end() - .create() - ; + .create(); // @formatter:on - + simpleFsm.process(""); simpleFsm.process(""); - //Assert.assertEquals("finish", simpleFsm.getCurrentState().getName()); + Assert.assertEquals("finish", simpleFsm.getCurrentState().getName()); Assert.assertTrue(initBefore.get()); Assert.assertTrue(initProcess.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 simpleFsm = Fsm + ., 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()); + + } + }