diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java index be494231..a67703eb 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java @@ -33,11 +33,19 @@ import org.springframework.statemachine.transition.TransitionKind; */ public class StateMachineTransitions { - private Collection> transitions; - private Map>> choices; - private Map> forks; - private Map> joins; + private final Collection> transitions; + private final Map>> choices; + private final Map> forks; + private final Map> joins; + /** + * Instantiates a new state machine transitions. + * + * @param transitions the transitions + * @param choices the choices + * @param forks the forks + * @param joins the joins + */ public StateMachineTransitions(Collection> transitions, Map>> choices, Map> forks, Map> joins) { this.transitions = transitions; @@ -46,32 +54,72 @@ public class StateMachineTransitions { this.joins = joins; } + /** + * Gets the transitions. + * + * @return the transitions + */ public Collection> getTransitions() { return transitions; } + /** + * Gets the choices. + * + * @return the choices + */ public Map>> getChoices() { return choices; } + /** + * Gets the forks. + * + * @return the forks + */ public Map> getForks() { return forks; } + /** + * Gets the joins. + * + * @return the joins + */ public Map> getJoins() { return joins; } + /** + * A simple data object keeping transition related configs in a same place. + * + * @param the type of state + * @param the type of event + */ public static class TransitionData { - S source; - S target; - S state; - E event; - Long period; - Collection> actions; - Guard guard; - TransitionKind kind; - public TransitionData(S source, S target, S state, E event, Long period, Collection> actions, Guard guard, TransitionKind kind) { + private final S source; + private final S target; + private final S state; + private final E event; + private final Long period; + private final Collection> actions; + private final Guard guard; + private final TransitionKind kind; + + /** + * Instantiates a new transition data. + * + * @param source the source + * @param target the target + * @param state the state + * @param event the event + * @param period the period + * @param actions the actions + * @param guard the guard + * @param kind the kind + */ + public TransitionData(S source, S target, S state, E event, Long period, Collection> actions, + Guard guard, TransitionKind kind) { this.source = source; this.target = target; this.state = state; @@ -81,51 +129,130 @@ public class StateMachineTransitions { this.guard = guard; this.kind = kind; } + + /** + * Gets the source. + * + * @return the source + */ public S getSource() { return source; } + + /** + * Gets the target. + * + * @return the target + */ public S getTarget() { return target; } + + /** + * Gets the state. + * + * @return the state + */ public S getState() { return state; } + + /** + * Gets the event. + * + * @return the event + */ public E getEvent() { return event; } + + /** + * Gets the period. + * + * @return the period + */ public Long getPeriod() { return period; } + + /** + * Gets the actions. + * + * @return the actions + */ public Collection> getActions() { return actions; } + + /** + * Gets the guard. + * + * @return the guard + */ public Guard getGuard() { return guard; } + + /** + * Gets the kind. + * + * @return the kind + */ public TransitionKind getKind() { return kind; } } + /** + * A simple data object keeping choice related configs in a same place. + * + * @param the type of state + * @param the type of event + */ public static class ChoiceData { private final S source; private final S target; private final Guard guard; + + /** + * Instantiates a new choice data. + * + * @param source the source + * @param target the target + * @param guard the guard + */ public ChoiceData(S source, S target, Guard guard) { this.source = source; this.target = target; this.guard = guard; } + + /** + * Gets the source. + * + * @return the source + */ public S getSource() { return source; } + + /** + * Gets the target. + * + * @return the target + */ public S getTarget() { return target; } + + /** + * Gets the guard. + * + * @return the guard + */ public Guard getGuard() { return guard; } } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultChoiceTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultChoiceTransitionConfigurer.java index 7b187812..a9577ede 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultChoiceTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultChoiceTransitionConfigurer.java @@ -38,19 +38,20 @@ public class DefaultChoiceTransitionConfigurer implements ChoiceTransitionConfigurer { private S source; - private ChoiceData first; - private final List> thens = new ArrayList>(); - private ChoiceData last; @Override public void configure(StateMachineTransitionBuilder builder) throws Exception { List> choices = new ArrayList>(); - choices.add(first); + if (first != null) { + choices.add(first); + } choices.addAll(thens); - choices.add(last); + if (last != null) { + choices.add(last); + } builder.add(source, choices); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/ChoicePseudoState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/ChoicePseudoState.java index 7cc60f8b..52d0ad46 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/ChoicePseudoState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/ChoicePseudoState.java @@ -30,8 +30,13 @@ import org.springframework.statemachine.guard.Guard; */ public class ChoicePseudoState implements PseudoState { - private List> choices; + private final List> choices; + /** + * Instantiates a new choice pseudo state. + * + * @param choices the choices + */ public ChoicePseudoState(List> choices) { this.choices = choices; } @@ -52,7 +57,7 @@ public class ChoicePseudoState implements PseudoState { } return s; } - + @Override public void exit(StateContext context) { } @@ -61,16 +66,42 @@ public class ChoicePseudoState implements PseudoState { public void addPseudoStateListener(PseudoStateListener listener) { } + /** + * Data class wrapping choice {@link State} and {@link Guard} + * together. + * + * @param the type of state + * @param the type of event + */ public static class ChoiceStateData { private final State state; private final Guard guard; + + /** + * Instantiates a new choice state data. + * + * @param state the state + * @param guard the guard + */ public ChoiceStateData(State state, Guard guard) { this.state = state; this.guard = guard; } + + /** + * Gets the state. + * + * @return the state + */ public State getState() { return state; } + + /** + * Gets the guard. + * + * @return the guard + */ public Guard getGuard() { return guard; } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/ChoiceStateTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/ChoiceStateTests.java index cd9982d4..1a8e68f2 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/ChoiceStateTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/ChoiceStateTests.java @@ -100,6 +100,20 @@ public class ChoiceStateTests extends AbstractStateMachineTests { assertThat(machine.getState().getIds(), contains(TestStates.S33)); } + @Test + @SuppressWarnings("unchecked") + public void testOnlyLast() { + context.register(BaseConfig.class, Config2.class); + context.refresh(); + ObjectStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class); + assertThat(machine, notNullValue()); + machine.start(); + machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build()); + + assertThat(machine.getState().getIds(), contains(TestStates.S33)); + } + @Configuration @EnableStateMachine static class Config1 extends EnumStateMachineConfigurerAdapter { @@ -146,6 +160,53 @@ public class ChoiceStateTests extends AbstractStateMachineTests { } } + @Configuration + @EnableStateMachine + static class Config2 extends EnumStateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(TestStates.SI) + .states(EnumSet.allOf(TestStates.class)) + .choice(TestStates.S3) + .end(TestStates.SF); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions + .withExternal() + .source(TestStates.SI) + .target(TestStates.S3) + .event(TestEvents.E1) + .and() + .withChoice() + .source(TestStates.S3) +// .first(TestStates.S30, s30Guard()) +// .then(TestStates.S31, s31Guard()) +// .then(TestStates.S32, s32Guard()) + .last(TestStates.S33); + } + + @Bean + public Guard s30Guard() { + return new ChoiceGuard("s30"); + } + + @Bean + public Guard s31Guard() { + return new ChoiceGuard("s31"); + } + + @Bean + public Guard s32Guard() { + return new ChoiceGuard("s32"); + } + + } + private static class ChoiceGuard implements Guard {