diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/EnumStateMachineFactory.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/EnumStateMachineFactory.java index 001b9f8a..3df04ac2 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/EnumStateMachineFactory.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/EnumStateMachineFactory.java @@ -19,26 +19,35 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.Stack; +import org.springframework.beans.factory.BeanFactory; import org.springframework.statemachine.EnumStateMachine; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.config.builders.StateMachineStates; -import org.springframework.statemachine.config.builders.StateMachineTransitions; import org.springframework.statemachine.config.builders.StateMachineStates.StateData; +import org.springframework.statemachine.config.builders.StateMachineStates.StateMachineStatesData; +import org.springframework.statemachine.config.builders.StateMachineTransitions; import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData; import org.springframework.statemachine.state.DefaultPseudoState; import org.springframework.statemachine.state.EnumState; import org.springframework.statemachine.state.PseudoState; import org.springframework.statemachine.state.PseudoStateKind; import org.springframework.statemachine.state.State; +import org.springframework.statemachine.state.StateMachineState; import org.springframework.statemachine.support.LifecycleObjectSupport; +import org.springframework.statemachine.support.tree.Tree; +import org.springframework.statemachine.support.tree.Tree.Node; +import org.springframework.statemachine.support.tree.TreeTraverser; import org.springframework.statemachine.transition.DefaultExternalTransition; import org.springframework.statemachine.transition.DefaultInternalTransition; import org.springframework.statemachine.transition.Transition; import org.springframework.statemachine.transition.TransitionKind; /** - * + * {@link StateMachineFactory} implementation using enums to build {@link StateMachine}s. + * * @author Janne Valkealahti * * @param the type of state @@ -46,7 +55,7 @@ import org.springframework.statemachine.transition.TransitionKind; */ public class EnumStateMachineFactory, E extends Enum> extends LifecycleObjectSupport implements StateMachineFactory { - + private final StateMachineTransitions stateMachineTransitions; private final StateMachineStates stateMachineStates; @@ -65,16 +74,68 @@ public class EnumStateMachineFactory, E extends Enum> exten @Override public StateMachine getStateMachine() { - return stateMachine(); + // we store mappings from state id's to states which gets + // created during the process. This is needed for transitions to + // find a correct mappings because they use state id's, not actual + // states. + final Map> stateMap = new HashMap>(); + + // we eventually return this machine which is a last one build + StateMachine machine = null; + + Map> states = stateMachineStates.getStates(); + Tree> tree = new Tree>(); + + for (Entry> e : states.entrySet()) { + Object id = e.getKey(); + StateMachineStatesData value = e.getValue(); + Object parent = value.getParent(); + tree.add(value, id, parent); + } + + TreeTraverser>> traverser = new TreeTraverser>>() { + @Override + public Iterable>> children(Node> root) { + return root.getChildren(); + } + }; + + Stack> stack = new Stack>(); + for (Node> node : traverser.postOrderTraversal(tree.getRoot())) { + System.out.println(node.getData()); + + Object parent = node.getData().getParent(); + + if (!stack.empty()) { + StackItem pop = stack.pop(); + machine = buildSubMachine(stateMap, pop.machine, node.getData(), stateMachineTransitions, getBeanFactory()); + stack.push(new StackItem(machine, parent)); + } else { + machine = buildSimpleMachine(stateMap, node.getData(), stateMachineTransitions, getBeanFactory()); + stack.push(new StackItem(machine, parent)); + } + + + } + + return machine; } - public StateMachine stateMachine() { - Map> stateMap = new HashMap>(); - for (StateData stateData : stateMachineStates.getStates()) { - + private static class StackItem { + StateMachine machine; + Object id; + public StackItem(StateMachine machine, Object id) { + this.machine = machine; + this.id = id; + } + } + + private static , E extends Enum> StateMachine buildSimpleMachine(Map> stateMap, StateMachineStatesData statesData, StateMachineTransitions transitionsData, BeanFactory beanFactory) { + for (StateData stateData : statesData.getStates()) { + // TODO: doesn't feel right to tweak initial kind like this PseudoState pseudoState = null; - if (stateData.getState() == stateMachineStates.getInitialState()) { + if (stateData.getState() == statesData.getInitialState()) { pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL); } stateMap.put(stateData.getState(), new EnumState(stateData.getState(), stateData.getDeferred(), @@ -82,7 +143,7 @@ public class EnumStateMachineFactory, E extends Enum> exten } Collection> transitions = new ArrayList>(); - for (TransitionData transitionData : stateMachineTransitions.getTransitions()) { + for (TransitionData transitionData : transitionsData.getTransitions()) { S source = transitionData.getSource(); S target = transitionData.getTarget(); E event = transitionData.getEvent(); @@ -90,22 +151,57 @@ public class EnumStateMachineFactory, E extends Enum> exten DefaultExternalTransition transition = new DefaultExternalTransition(stateMap.get(source), stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard()); transitions.add(transition); - + } else if (transitionData.getKind() == TransitionKind.INTERNAL) { DefaultInternalTransition transition = new DefaultInternalTransition(stateMap.get(source), transitionData.getActions(), event, transitionData.getGuard()); - transitions.add(transition); + transitions.add(transition); } } EnumStateMachine machine = new EnumStateMachine(stateMap.values(), transitions, - stateMap.get(stateMachineStates.getInitialState()), stateMap.get(stateMachineStates.getEndState())); + stateMap.get(statesData.getInitialState()), stateMap.get(statesData.getEndState())); machine.afterPropertiesSet(); - if (getBeanFactory() != null) { - machine.setBeanFactory(getBeanFactory()); + if (beanFactory != null) { + machine.setBeanFactory(beanFactory); + } + return machine; + } + + private static , E extends Enum> StateMachine buildSubMachine(Map> stateMap, StateMachine submachine, StateMachineStatesData statesData, StateMachineTransitions transitionsData, BeanFactory beanFactory) { + Collection> states = new ArrayList>(); + State state = null; + for (StateData stateData : statesData.getStates()) { + state = new StateMachineState(stateData.getState(), submachine, stateData.getDeferred(), + stateData.getEntryActions(), stateData.getExitActions(), new DefaultPseudoState( + PseudoStateKind.INITIAL)); + states.add(state); + stateMap.put(stateData.getState(), state); + } + + Collection> transitions = new ArrayList>(); + for (TransitionData transitionData : transitionsData.getTransitions()) { + S source = transitionData.getSource(); + S target = transitionData.getTarget(); + E event = transitionData.getEvent(); + if (transitionData.getKind() == TransitionKind.EXTERNAL) { + DefaultExternalTransition transition = new DefaultExternalTransition(stateMap.get(source), + stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard()); + transitions.add(transition); + + } else if (transitionData.getKind() == TransitionKind.INTERNAL) { + DefaultInternalTransition transition = new DefaultInternalTransition(stateMap.get(source), + transitionData.getActions(), event, transitionData.getGuard()); + transitions.add(transition); + } + } + + EnumStateMachine machine = new EnumStateMachine(states, transitions, state, null); + + machine.afterPropertiesSet(); + if (beanFactory != null) { + machine.setBeanFactory(beanFactory); } - machine.setAutoStartup(isAutoStartup()); - machine.start(); return machine; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateBuilder.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateBuilder.java index 9d9786e5..eed1e6c5 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateBuilder.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateBuilder.java @@ -15,22 +15,33 @@ */ package org.springframework.statemachine.config.builders; -import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.springframework.statemachine.config.builders.StateMachineStates.StateData; +import org.springframework.statemachine.config.builders.StateMachineStates.StateMachineStatesData; import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder; +import org.springframework.statemachine.config.common.annotation.AnnotationBuilder; import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor; import org.springframework.statemachine.config.configurers.DefaultStateConfigurer; +import org.springframework.statemachine.config.configurers.DefaultSubStateConfigurer; import org.springframework.statemachine.config.configurers.StateConfigurer; +import org.springframework.statemachine.config.configurers.SubStateConfigurer; +/** + * {@link AnnotationBuilder} for {@link StateMachineStates}. + * + * @author Janne Valkealahti + * + * @param the type of state + * @param the type of event + */ public class StateMachineStateBuilder extends AbstractConfiguredAnnotationBuilder, StateMachineStateConfigurer, StateMachineStateBuilder> implements StateMachineStateConfigurer { - private Collection> states = new ArrayList>(); - private S initialState; - private S endState; + private final Map> data = new HashMap>(); public StateMachineStateBuilder() { super(); @@ -47,25 +58,26 @@ public class StateMachineStateBuilder @Override protected StateMachineStates performBuild() throws Exception { - StateMachineStates bean = new StateMachineStates(initialState, endState, states); - return bean; + return new StateMachineStates(data); } @Override public StateConfigurer withStates() throws Exception { - return apply(new DefaultStateConfigurer()); + return apply(new DefaultStateConfigurer(null, null)); } - public void add(Collection> states) { - this.states.addAll(states); + @Override + public StateConfigurer withStates(Object parent) throws Exception { + return apply(new DefaultStateConfigurer(null, parent)); } - public void setInitialState(S state) { - this.initialState = state; + @Override + public SubStateConfigurer withSubStates(S state, Object parent) throws Exception { + return apply(new DefaultSubStateConfigurer(state, state, parent)); } - - public void setEndState(S endState) { - this.endState = endState; + + public void add(Object id, Object parent, Collection> states, S initialState, S endState) { + data.put(id, new StateMachineStatesData(states, initialState, endState, parent)); } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateConfigurer.java index c2b053d1..80bdade5 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStateConfigurer.java @@ -16,9 +16,14 @@ package org.springframework.statemachine.config.builders; import org.springframework.statemachine.config.configurers.StateConfigurer; +import org.springframework.statemachine.config.configurers.SubStateConfigurer; public interface StateMachineStateConfigurer { StateConfigurer withStates() throws Exception; + StateConfigurer withStates(Object parent) throws Exception; + + SubStateConfigurer withSubStates(S state, Object parent) throws Exception; + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java index e83488d9..fcad7cd3 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java @@ -18,35 +18,62 @@ package org.springframework.statemachine.config.builders; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Map; import org.springframework.statemachine.action.Action; public class StateMachineStates { - private Collection> states; + private final Map> states; - private final S initialState; - - private final S endState; - - public StateMachineStates(S initialState, S endState, Collection> states) { + public StateMachineStates(Map> states) { this.states = states; - this.initialState = initialState; - this.endState = endState; } - public Collection> getStates() { + public Map> getStates() { return states; } - public S getInitialState() { - return initialState; + public static class StateMachineStatesData { + private Collection> states; + + private final S initialState; + + private final S endState; + + private final Object parent; + + public StateMachineStatesData(Collection> states, S initialState, S endState, Object parent) { + this.states = states; + this.initialState = initialState; + this.endState = endState; + this.parent = parent; + } + + public Collection> getStates() { + return states; + } + + public S getInitialState() { + return initialState; + } + + public S getEndState() { + return endState; + } + + public Object getParent() { + return parent; + } + + @Override + public String toString() { + return "StateMachineStatesData [states=" + states + ", initialState=" + initialState + ", endState=" + + endState + ", parent=" + parent + "]"; + } + } - public S getEndState() { - return endState; - } - public static class StateData { private S state; private Collection deferred; @@ -76,6 +103,12 @@ public class StateMachineStates { public Collection> getExitActions() { return exitActions; } + @Override + public String toString() { + return "StateData [state=" + state + ", deferred=" + deferred + ", entryActions=" + entryActions + + ", exitActions=" + exitActions + "]"; + } + } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java index fb107a2e..be97816a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java @@ -30,17 +30,24 @@ public class DefaultStateConfigurer extends AnnotationConfigurerAdapter, StateMachineStateConfigurer, StateMachineStateBuilder> implements StateConfigurer { + private final Object id; + + private final Object parent; + private final Collection> states = new ArrayList>(); private S initial; private S end; - + + public DefaultStateConfigurer(Object id, Object parent) { + this.id = id; + this.parent = parent; + } + @Override public void configure(StateMachineStateBuilder builder) throws Exception { - builder.add(states); - builder.setInitialState(initial); - builder.setEndState(end); + builder.add(id, parent, states, initial, end); } @Override @@ -48,7 +55,7 @@ public class DefaultStateConfigurer this.initial = initial; return this; } - + @Override public StateConfigurer end(S end) { this.end = end; @@ -59,7 +66,7 @@ public class DefaultStateConfigurer public StateConfigurer state(S state) { return state(state, (E[])null); } - + @Override public StateConfigurer state(S state, Collection> entryActions, Collection> exitActions) { states.add(new StateData(state, null, entryActions, exitActions)); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultSubStateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultSubStateConfigurer.java new file mode 100644 index 00000000..14c8257c --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultSubStateConfigurer.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.config.configurers; + +import java.util.ArrayList; +import java.util.Collection; + +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.builders.StateMachineStateBuilder; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStates; +import org.springframework.statemachine.config.builders.StateMachineStates.StateData; +import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter; + +public class DefaultSubStateConfigurer + extends AnnotationConfigurerAdapter, StateMachineStateConfigurer, StateMachineStateBuilder> + implements SubStateConfigurer { + + private S state; + private final Object id; + private final Object parent; + + private S initial; + private Collection> entryActions; + private Collection> exitActions; + + private final Collection> states = new ArrayList>(); + + public DefaultSubStateConfigurer(S state, Object id) { + this(state, id, null); + } + + public DefaultSubStateConfigurer(S state, Object id, Object parent) { + this.state = state; + this.id = id; + this.parent = parent; + } + + @Override + public void configure(StateMachineStateBuilder builder) throws Exception { + states.add(new StateData(state, null, entryActions, exitActions)); + builder.add(id, parent, states, initial, null); + } + + @Override + public SubStateConfigurer initial(S initial) { + this.initial = initial; + return this; + } + + @Override + public SubStateConfigurer entry(Collection> entryActions) { + this.entryActions = entryActions; + return this; + } + + @Override + public SubStateConfigurer exit(Collection> exitActions) { + this.exitActions = exitActions; + return this; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java index bc64e4fc..2786c350 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java @@ -30,7 +30,7 @@ public interface StateConfigurer extends StateConfigurer state(S state); StateConfigurer state(S state, Collection> entryActions, Collection> exitActions); - + StateConfigurer state(S state, E... deferred); StateConfigurer states(Set states); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/SubStateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/SubStateConfigurer.java new file mode 100644 index 00000000..60538e7b --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/SubStateConfigurer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.config.configurers; + +import java.util.Collection; + +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder; + +public interface SubStateConfigurer extends + AnnotationConfigurerBuilder> { + + SubStateConfigurer initial(S initial); + + SubStateConfigurer entry(Collection> entryActions); + + SubStateConfigurer exit(Collection> exitActions); + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java index 30408e72..e1460572 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java @@ -25,20 +25,20 @@ import org.springframework.statemachine.region.Region; /** * Base implementation of a {@link State} having a single state identifier. - * + * * @author Janne Valkealahti * * @param the type of state * @param the type of event */ public abstract class AbstractSimpleState extends AbstractState { - + private final Collection ids; /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier */ public AbstractSimpleState(S id) { this(id, null, null, null, null); @@ -47,7 +47,7 @@ public abstract class AbstractSimpleState extends AbstractState { /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions @@ -59,7 +59,7 @@ public abstract class AbstractSimpleState extends AbstractState { /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param deferred the deferred */ public AbstractSimpleState(S id, Collection deferred) { @@ -69,17 +69,17 @@ public abstract class AbstractSimpleState extends AbstractState { /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param pseudoState the pseudo state */ public AbstractSimpleState(S id, PseudoState pseudoState) { this(id, null, null, null, pseudoState); } - + /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions @@ -88,7 +88,7 @@ public abstract class AbstractSimpleState extends AbstractState { */ public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions) { - super(deferred, entryActions, exitActions, pseudoState, regions); + super(id, deferred, entryActions, exitActions, pseudoState, regions); this.ids = new ArrayList(); this.ids.add(id); } @@ -96,7 +96,7 @@ public abstract class AbstractSimpleState extends AbstractState { /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions @@ -105,7 +105,7 @@ public abstract class AbstractSimpleState extends AbstractState { */ public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, StateMachine submachine) { - super(deferred, entryActions, exitActions, pseudoState, submachine); + super(id, deferred, entryActions, exitActions, pseudoState, submachine); this.ids = new ArrayList(); this.ids.add(id); } @@ -113,7 +113,7 @@ public abstract class AbstractSimpleState extends AbstractState { /** * Instantiates a new abstract simple state. * - * @param id the id + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions @@ -121,7 +121,7 @@ public abstract class AbstractSimpleState extends AbstractState { */ public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { - super(deferred, entryActions, exitActions, pseudoState); + super(id, deferred, entryActions, exitActions, pseudoState); this.ids = new ArrayList(); this.ids.add(id); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java index 3cce2515..30164e1a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java @@ -23,7 +23,6 @@ import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.region.Region; -import org.springframework.util.StringUtils; /** * Base implementation of a {@link State}. @@ -35,6 +34,7 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractState implements State { + private final S id; private final PseudoState pseudoState; private final Collection deferred; private final Collection> entryActions; @@ -45,76 +45,83 @@ public abstract class AbstractState implements State { /** * Instantiates a new abstract state. * + * @param id the state identifier * @param pseudoState the pseudo state */ - public AbstractState(PseudoState pseudoState) { - this(null, null, null, pseudoState); + public AbstractState(S id, PseudoState pseudoState) { + this(id, null, null, null, pseudoState); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred */ - public AbstractState(Collection deferred) { - this(deferred, null, null); + public AbstractState(S id, Collection deferred) { + this(id, deferred, null, null); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions */ - public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions) { - this(deferred, entryActions, exitActions, null); + public AbstractState(S id, Collection deferred, Collection> entryActions, Collection> exitActions) { + this(id, deferred, entryActions, exitActions, null); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, + public AbstractState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { - this(deferred, entryActions, exitActions, pseudoState, null, null); + this(id, deferred, entryActions, exitActions, pseudoState, null, null); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions * @param pseudoState the pseudo state * @param submachine the submachine */ - public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, + public AbstractState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, StateMachine submachine) { - this(deferred, entryActions, exitActions, pseudoState, null, submachine); + this(id, deferred, entryActions, exitActions, pseudoState, null, submachine); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions * @param pseudoState the pseudo state * @param regions the regions */ - public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, + public AbstractState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions) { - this(deferred, entryActions, exitActions, pseudoState, regions, null); + this(id, deferred, entryActions, exitActions, pseudoState, regions, null); } /** * Instantiates a new abstract state. * + * @param id the state identifier * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions @@ -122,8 +129,9 @@ public abstract class AbstractState implements State { * @param regions the regions * @param submachine the submachine */ - private AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, + private AbstractState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions, StateMachine submachine) { + this.id = id; this.deferred = deferred; this.entryActions = entryActions; this.exitActions = exitActions; @@ -147,6 +155,11 @@ public abstract class AbstractState implements State { @Override public abstract void entry(E event, StateContext context); + @Override + public S getId() { + return id; + } + @Override public abstract Collection getIds(); @@ -200,8 +213,9 @@ public abstract class AbstractState implements State { @Override public String toString() { - return "AbstractState [ids=" + StringUtils.collectionToCommaDelimitedString(getIds()) + ", pseudoState=" + pseudoState + ", deferred=" + deferred - + ", entryActions=" + entryActions + ", exitActions=" + exitActions + "]"; + return "AbstractState [id=" + id + ", pseudoState=" + pseudoState + ", deferred=" + deferred + + ", entryActions=" + entryActions + ", exitActions=" + exitActions + ", regions=" + regions + + ", submachine=" + submachine + "]"; } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java index a8681f81..f1e13e8c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java @@ -36,56 +36,61 @@ public class RegionState extends AbstractState { /** * Instantiates a new region state. * + * @param id the state identifier * @param regions the regions */ - public RegionState(Collection> regions) { - super(null, null, null, null, regions); + public RegionState(S id, Collection> regions) { + super(id, null, null, null, null, regions); } /** * Instantiates a new region state. * + * @param id the state identifier * @param regions the regions * @param deferred the deferred */ - public RegionState(Collection> regions, Collection deferred) { - super(deferred, null, null, null, regions); + public RegionState(S id, Collection> regions, Collection deferred) { + super(id, deferred, null, null, null, regions); } /** * Instantiates a new region state. * + * @param id the state identifier * @param regions the regions * @param pseudoState the pseudo state */ - public RegionState(Collection> regions, PseudoState pseudoState) { - super(null, null, null, pseudoState, regions); + public RegionState(S id, Collection> regions, PseudoState pseudoState) { + super(id, null, null, null, pseudoState, regions); } /** * Instantiates a new region state. * + * @param id the state identifier * @param regions the regions * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public RegionState(Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions, + public RegionState(S id, Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { - super(deferred, entryActions, exitActions, pseudoState, regions); + super(id, deferred, entryActions, exitActions, pseudoState, regions); } /** * Instantiates a new region state. * + * @param id the state identifier * @param regions the regions * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions */ - public RegionState(Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions) { - super(deferred, entryActions, exitActions, null, regions); + public RegionState(S id, Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions) { + super(id, deferred, entryActions, exitActions, null, regions); } @Override diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java index 6d3bb818..1903ac1e 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java @@ -54,6 +54,13 @@ public interface State { */ void entry(E event, StateContext context); + /** + * Gets the state identifier. + * + * @return the state identifiers + */ + S getId(); + /** * Gets the state identifiers. Usually returned collection contains only one * identifier except in a case where state is an orthogonal. diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java index e106f2a6..19148516 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java @@ -15,8 +15,8 @@ */ package org.springframework.statemachine.state; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; @@ -34,69 +34,87 @@ import org.springframework.statemachine.transition.TransitionKind; */ public class StateMachineState extends AbstractState { + private final Collection ids; + /** * Instantiates a new state machine state. * + * @param id the state identifier * @param submachine the submachine */ - public StateMachineState(StateMachine submachine) { - super(null, null, null, null, submachine); + public StateMachineState(S id, StateMachine submachine) { + super(id, null, null, null, null, submachine); + this.ids = new ArrayList(); + this.ids.add(id); } /** * Instantiates a new state machine state. * + * @param id the state identifier * @param submachine the submachine * @param deferred the deferred */ - public StateMachineState(StateMachine submachine, Collection deferred) { - super(deferred, null, null, null, submachine); + public StateMachineState(S id, StateMachine submachine, Collection deferred) { + super(id, deferred, null, null, null, submachine); + this.ids = new ArrayList(); + this.ids.add(id); } /** * Instantiates a new state machine state. * + * @param id the state identifier * @param submachine the submachine * @param pseudoState the pseudo state */ - public StateMachineState(StateMachine submachine, PseudoState pseudoState) { - super(null, null, null, pseudoState, submachine); + public StateMachineState(S id, StateMachine submachine, PseudoState pseudoState) { + super(id, null, null, null, pseudoState, submachine); + this.ids = new ArrayList(); + this.ids.add(id); } /** * Instantiates a new state machine state. * + * @param id the state identifier * @param submachine the submachine * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public StateMachineState(StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions, + public StateMachineState(S id, StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { - super(deferred, entryActions, exitActions, pseudoState, submachine); + super(id, deferred, entryActions, exitActions, pseudoState, submachine); + this.ids = new ArrayList(); + this.ids.add(id); } /** * Instantiates a new state machine state. * + * @param id the state identifier * @param submachine the submachine * @param deferred the deferred * @param entryActions the entry actions * @param exitActions the exit actions */ - public StateMachineState(StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions) { - super(deferred, entryActions, exitActions, null, submachine); + public StateMachineState(S id, StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions) { + super(id, deferred, entryActions, exitActions, null, submachine); + this.ids = new ArrayList(); + this.ids.add(id); } @Override public Collection getIds() { + + Collection ret = new ArrayList(ids); State state = getSubmachine().getState(); if (state != null) { - return state.getIds(); - } else { - return Collections.emptyList(); + ret.addAll(state.getIds()); } + return ret; } @Override @@ -137,8 +155,8 @@ public class StateMachineState extends AbstractState { @Override public String toString() { - return "StateMachineState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() - + ", toString()=" + super.toString() + "]"; + return "StateMachineState [getIds()=" + getIds() + ", toString()=" + super.toString() + ", getClass()=" + + getClass() + "]"; } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/AbstractIterator.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/AbstractIterator.java new file mode 100644 index 00000000..598e3f7a --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/AbstractIterator.java @@ -0,0 +1,85 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.support.tree; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.springframework.util.Assert; + +public abstract class AbstractIterator implements Iterator { + + private State state = State.NOT_READY; + + private enum State { + READY, NOT_READY, DONE, FAILED, + } + + private T next; + + protected abstract T computeNext(); + + protected final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + Assert.state(state != State.FAILED); + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + T result = next; + next = null; + return result; + } + + public final T peek() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return next; + } + + public final void remove() { + throw new UnsupportedOperationException("remove"); + } + +} \ No newline at end of file diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/Tree.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/Tree.java new file mode 100644 index 00000000..8dff85fb --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/Tree.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.support.tree; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Utility class which can be used to represent a tree based data structure. + * + * @author Janne Valkealahti + * + * @param the type of node data + */ +public class Tree { + + private Node root; + private final Map> map = new HashMap>(); + private final List> notMapped = new ArrayList>(); + + public Tree() { + } + + public Node getRoot() { + return root; + } + + public void add(T data, Object id, Object parent) { + notMapped.add(new DataWrap(data, id, parent)); + tryMapping(); + } + + private void tryMapping() { + int size = notMapped.size(); + Iterator> iter = notMapped.iterator(); + while(iter.hasNext()) { + DataWrap next = iter.next(); + if (next.parent == null) { + Node n = new Node(next.data); + map.put(next.id, n); + root = n; + iter.remove(); + } else { + if (map.containsKey(next.parent)) { + Node n = new Node(next.data); + Node node = map.get(next.parent); + map.put(next.id, n); + node.getChildren().add(n); + iter.remove(); + } + } + } + if (notMapped.size() < size) { + tryMapping(); + } + } + + public static class Node { + private final T data; + private final List> children; + + public Node(T data) { + this(data, null); + } + + public Node(T data, List> children) { + this.data = data; + this.children = children != null ? children : new ArrayList>(); + } + + public T getData() { + return data; + } + + public List> getChildren() { + return children; + } + + } + + private static class DataWrap { + final T data; + final Object id; + final Object parent; + public DataWrap(T data, Object id, Object parent) { + this.data = data; + this.id = id; + this.parent = parent; + } + } + +} \ No newline at end of file diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/TreeTraverser.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/TreeTraverser.java new file mode 100644 index 00000000..870a3350 --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/tree/TreeTraverser.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.support.tree; + +import java.util.ArrayDeque; +import java.util.Iterator; + +import org.springframework.util.Assert; + +public abstract class TreeTraverser { + + /** + * Returns the children of the specified node. Must not contain null. + */ + public abstract Iterable children(T root); + + public final Iterable postOrderTraversal(final T root) { + Assert.notNull(root); + return new Iterable() { + @Override + public Iterator iterator() { + return postOrderIterator(root); + } + }; + } + + Iterator postOrderIterator(T root) { + return new PostOrderIterator(root); + } + + private static final class PostOrderNode { + final T root; + final Iterator childIterator; + + PostOrderNode(T root, Iterator childIterator) { + Assert.notNull(root); + Assert.notNull(childIterator); + this.root = root; + this.childIterator = childIterator; + } + } + + private final class PostOrderIterator extends AbstractIterator { + private final ArrayDeque> stack; + + PostOrderIterator(T root) { + this.stack = new ArrayDeque>(); + stack.addLast(expand(root)); + } + + @Override + protected T computeNext() { + while (!stack.isEmpty()) { + PostOrderNode top = stack.getLast(); + if (top.childIterator.hasNext()) { + T child = top.childIterator.next(); + stack.addLast(expand(child)); + } else { + stack.removeLast(); + return top.root; + } + } + return endOfData(); + } + + private PostOrderNode expand(T t) { + return new PostOrderNode(t, children(t).iterator()); + } + } + +} \ No newline at end of file diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/RegionMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/RegionMachineTests.java index 3e985de2..2625749b 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/RegionMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/RegionMachineTests.java @@ -91,7 +91,7 @@ public class RegionMachineTests { Collection> regions = new ArrayList>(); regions.add(machine); - RegionState state = new RegionState(regions); + RegionState state = new RegionState(TestStates.S11, regions); assertThat(state.isSimple(), is(false)); assertThat(state.isComposite(), is(true)); @@ -165,7 +165,7 @@ public class RegionMachineTests { Collection> regions = new ArrayList>(); regions.add(machine11); regions.add(machine12); - RegionState stateR = new RegionState(regions, null, null, null, pseudoState); + RegionState stateR = new RegionState(TestStates.S11, regions, null, null, null, pseudoState); Collection> states = new ArrayList>(); states.add(stateR); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineFactoryTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineFactoryTests.java index 70719c26..a894898a 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineFactoryTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineFactoryTests.java @@ -41,7 +41,8 @@ public class StateMachineFactoryTests extends AbstractStateMachineTests { EnumStateMachineFactory stateMachineFactory = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY, EnumStateMachineFactory.class); StateMachine machine = stateMachineFactory.getStateMachine(); - + machine.start(); + assertThat(machine.getState().getIds(), contains(TestStates.S1)); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build()); assertThat(machine.getState().getIds(), contains(TestStates.S2)); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java index 85b136b0..77581dc7 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java @@ -44,6 +44,7 @@ public class StateMachineTests extends AbstractStateMachineTests { EnumStateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); assertThat(machine, notNullValue()); + machine.start(); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).setHeader("foo", "jee1").build()); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).setHeader("foo", "jee2").build()); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E4).setHeader("foo", "jee2").build()); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java index 46a17d0e..4a2bbf58 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java @@ -17,14 +17,23 @@ package org.springframework.statemachine; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; import org.springframework.statemachine.state.DefaultPseudoState; import org.springframework.statemachine.state.EnumState; import org.springframework.statemachine.state.PseudoState; @@ -37,6 +46,11 @@ import org.springframework.statemachine.transition.Transition; public class SubStateMachineTests extends AbstractStateMachineTests { + @Override + protected AnnotationConfigApplicationContext buildContext() { + return new AnnotationConfigApplicationContext(); + } + @Test public void testExternalTransition() throws Exception { /** @@ -85,7 +99,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests { entryActionsS11.add(entryActionS11); Collection> exitActionsS11 = new ArrayList>(); exitActionsS11.add(exitActionS11); - StateMachineState stateS11 = new StateMachineState(submachine11, null, entryActionsS11, exitActionsS11, pseudoState); + StateMachineState stateS11 = new StateMachineState(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState); Collection> substates11 = new ArrayList>(); substates11.add(stateS11); @@ -100,13 +114,13 @@ public class SubStateMachineTests extends AbstractStateMachineTests { Collection> exitActionsS1 = new ArrayList>(); exitActionsS1.add(exitActionS1); - StateMachineState stateS1 = new StateMachineState(submachine1, null, entryActionsS1, exitActionsS1, pseudoState); + StateMachineState stateS1 = new StateMachineState(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState); Collection> states = new ArrayList>(); states.add(stateS1); Collection> transitions = new ArrayList>(); - DefaultExternalTransition transitionFromS11ToS1 = + DefaultExternalTransition transitionFromS111ToS1 = new DefaultExternalTransition(stateS111, stateS1, null, TestEvents.E1, null); - transitions.add(transitionFromS11ToS1); + transitions.add(transitionFromS111ToS1); EnumStateMachine machine = new EnumStateMachine(states, transitions, stateS1, null); @@ -134,6 +148,96 @@ public class SubStateMachineTests extends AbstractStateMachineTests { assertThat(exitActionS1.stateContexts.size(), is(1)); } +// @Test + public void testExternalTransition2() throws Exception { + + /** + * +---------------------------------------------------+ + * *-init->| S1 | + * +---------------------------------------------------+ + * | entry/ | + * | exit/ | + * | +-----------+ +-----------+ | + * | *-->| S111 | | S112 | | + * | +-----------+ +-----------+ | + * | | entry/ | | entry/ | | + * | | exit/ |----E1--->| exit/ | | + * | | | | | | + * | | |<---E2----| | | + * | | | | | | + * | +-----------+ +-----------+ | + * | | + * +---------------------------------------------------+ + */ + + + PseudoState pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL); + + TestEntryAction entryActionS111 = new TestEntryAction("S111"); + TestExitAction exitActionS111 = new TestExitAction("S111"); + Collection> entryActionsS111 = new ArrayList>(); + entryActionsS111.add(entryActionS111); + Collection> exitActionsS111 = new ArrayList>(); + exitActionsS111.add(exitActionS111); + State stateS111 = new EnumState(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState); + + TestEntryAction entryActionS112 = new TestEntryAction("S112"); + TestExitAction exitActionS112 = new TestExitAction("S112"); + Collection> entryActionsS112 = new ArrayList>(); + entryActionsS112.add(entryActionS112); + Collection> exitActionsS112 = new ArrayList>(); + exitActionsS111.add(exitActionS112); + State stateS112 = new EnumState(TestStates.S112, null, entryActionsS112, exitActionsS112, null); + + // submachine 1 + Collection> substates11 = new ArrayList>(); + substates11.add(stateS111); + substates11.add(stateS112); + Collection> subtransitions11 = new ArrayList>(); + EnumStateMachine submachine11 = new EnumStateMachine(substates11, subtransitions11, stateS111, null); + + // machine + TestEntryAction entryActionS1 = new TestEntryAction("S1"); + TestExitAction exitActionS1 = new TestExitAction("S1"); + Collection> entryActionsS1 = new ArrayList>(); + entryActionsS1.add(entryActionS1); + Collection> exitActionsS1 = new ArrayList>(); + exitActionsS1.add(exitActionS1); + + StateMachineState stateS1 = new StateMachineState(TestStates.S1, submachine11, null, entryActionsS1, exitActionsS1, pseudoState); + Collection> states = new ArrayList>(); + states.add(stateS1); + Collection> transitions = new ArrayList>(); + DefaultExternalTransition transitionFromS111ToS112 = + new DefaultExternalTransition(stateS111, stateS112, null, TestEvents.E1, null); + transitions.add(transitionFromS111ToS112); + EnumStateMachine machine = new EnumStateMachine(states, transitions, stateS1, null); + + + SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); + machine.setTaskExecutor(taskExecutor); + machine.afterPropertiesSet(); + machine.start(); + submachine11.setTaskExecutor(taskExecutor); + + machine.sendEvent(TestEvents.E1); + + assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(entryActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); + assertThat(entryActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); + + assertThat(entryActionS111.stateContexts.size(), is(1)); + assertThat(exitActionS111.stateContexts.size(), is(1)); + assertThat(entryActionS112.stateContexts.size(), is(1)); + assertThat(exitActionS112.stateContexts.size(), is(0)); + assertThat(entryActionS1.stateContexts.size(), is(1)); + assertThat(exitActionS1.stateContexts.size(), is(0)); + } + + @Test public void testLocalTransition() throws Exception { /** @@ -182,7 +286,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests { entryActionsS11.add(entryActionS11); Collection> exitActionsS11 = new ArrayList>(); exitActionsS11.add(exitActionS11); - StateMachineState stateS11 = new StateMachineState(submachine11, null, entryActionsS11, exitActionsS11, pseudoState); + StateMachineState stateS11 = new StateMachineState(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState); Collection> substates11 = new ArrayList>(); substates11.add(stateS11); @@ -197,7 +301,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests { Collection> exitActionsS1 = new ArrayList>(); exitActionsS1.add(exitActionS1); - StateMachineState stateS1 = new StateMachineState(submachine1, null, entryActionsS1, exitActionsS1, pseudoState); + StateMachineState stateS1 = new StateMachineState(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState); Collection> states = new ArrayList>(); states.add(stateS1); Collection> transitions = new ArrayList>(); @@ -231,4 +335,192 @@ public class SubStateMachineTests extends AbstractStateMachineTests { assertThat(exitActionS1.stateContexts.size(), is(0)); } + @Test + public void testExternalTransition3() throws Exception { + context.register(BaseConfig.class, Config1.class); + context.refresh(); + assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); + @SuppressWarnings("unchecked") + EnumStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + + machine.start(); + + machine.sendEvent(TestEvents.E1); + + TestEntryAction entryActionS111 = context.getBean("entryActionS111", TestEntryAction.class); + TestExitAction exitActionS111 = context.getBean("exitActionS111", TestExitAction.class); + TestEntryAction entryActionS11 = context.getBean("entryActionS11", TestEntryAction.class); + TestExitAction exitActionS11 = context.getBean("exitActionS11", TestExitAction.class); + TestEntryAction entryActionS1 = context.getBean("entryActionS1", TestEntryAction.class); + TestExitAction exitActionS1 = context.getBean("exitActionS1", TestExitAction.class); + + assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(entryActionS11.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS11.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(entryActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(exitActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + + assertThat(entryActionS11.stateContexts.size(), is(2)); + assertThat(exitActionS11.stateContexts.size(), is(1)); + assertThat(entryActionS11.stateContexts.size(), is(2)); + assertThat(exitActionS11.stateContexts.size(), is(1)); + assertThat(entryActionS1.stateContexts.size(), is(2)); + assertThat(exitActionS1.stateContexts.size(), is(1)); + + } + + + @Configuration + @EnableStateMachine + public static class Config1 extends EnumStateMachineConfigurerAdapter { + + @SuppressWarnings("unchecked") + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withSubStates(TestStates.S1, null) +// .initial(TestStates.S11) + .entry(Arrays.asList(entryActionS1())) + .exit(Arrays.asList(exitActionS1())) + .and() + .withSubStates(TestStates.S11, TestStates.S1) +// .initial(TestStates.S111) + .entry(Arrays.asList(entryActionS11())) + .exit(Arrays.asList(exitActionS11())) + .and() + .withStates(TestStates.S11) + .initial(TestStates.S111) + .state(TestStates.S111, Arrays.asList(entryActionS111()), Arrays.asList(exitActionS111())); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions + .withExternal() + .source(TestStates.S111) + .target(TestStates.S1) + .event(TestEvents.E1); + } + + @Bean(name = "entryActionS111") + public Action entryActionS111() { + return new TestEntryAction("S111"); + } + + @Bean(name = "exitActionS111") + public Action exitActionS111() { + return new TestExitAction("S111"); + } + + @Bean(name = "entryActionS11") + public Action entryActionS11() { + return new TestEntryAction("S11"); + } + + @Bean(name = "exitActionS11") + public Action exitActionS11() { + return new TestExitAction("S11"); + } + + @Bean(name = "entryActionS1") + public Action entryActionS1() { + return new TestEntryAction("S1"); + } + + @Bean(name = "exitActionS1") + public Action exitActionS1() { + return new TestExitAction("S1"); + } + + } + +// @Configuration +// @EnableStateMachine(name = "submachine11Config") +// public static class Config1 extends EnumStateMachineConfigurerAdapter { +// +// @Override +// public void configure(StateMachineStateConfigurer states) throws Exception { +// states +// .withStates() +// .initial(TestStates.S111) +// .state(TestStates.S111, Arrays.asList(testEntryAction()), Arrays.asList(testExitAction())); +// } +// +// @Bean(name = "entryActionS111") +// public Action testEntryAction() { +// return new TestEntryAction(); +// } +// +// @Bean(name = "exitActionS111") +// public Action testExitAction() { +// return new TestExitAction(); +// } +// +// } + +// @Configuration +// @EnableStateMachine(name = "submachine1Config") +// public static class Config2 extends EnumStateMachineConfigurerAdapter { +// +// @Autowired +// @Qualifier("submachine11Config") +// public StateMachine submachine; +// +// @Override +// public void configure(StateMachineStateConfigurer states) throws Exception { +// states +// .withSubmachine() +// .submachine(submachine); +// } +// +// @Bean(name = "entryActionS11") +// public Action testEntryAction() { +// return new TestEntryAction(); +// } +// +// @Bean(name = "exitActionS11") +// public Action testExitAction() { +// return new TestExitAction(); +// } +// +// } + +// @Configuration +// @EnableStateMachine(name = "submachineConfig") +// public static class Config3 extends EnumStateMachineConfigurerAdapter { +// +// @Autowired +// @Qualifier("submachine1Config") +// public StateMachine submachine; +// +// @Override +// public void configure(StateMachineStateConfigurer states) throws Exception { +// states +// .withSubmachine() +// .submachine(submachine); +// } +// +// @Override +// public void configure(StateMachineTransitionConfigurer transitions) throws Exception { +// transitions +// .withExternal() +// .source(TestStates.S111) +// .target(TestStates.S1) +// .event(TestEvents.E1); +// } +// +// @Bean(name = "entryActionS1") +// public TestEntryAction testEntryAction() { +// return new TestEntryAction(); +// } +// +// @Bean(name = "exitActionS1") +// public TestExitAction testExitAction() { +// return new TestExitAction(); +// } +// +// } + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java index d94afc11..00a86f28 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java @@ -37,7 +37,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo /** * Tests for state machine actions. - * + * * @author Janne Valkealahti * */ @@ -50,6 +50,7 @@ public class ActionTests extends AbstractStateMachineTests { assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); StateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class); + machine.start(); TestCountAction testAction1 = ctx.getBean("testAction1", TestCountAction.class); TestCountAction testAction2 = ctx.getBean("testAction2", TestCountAction.class); @@ -71,7 +72,7 @@ public class ActionTests extends AbstractStateMachineTests { private static class TestCountAction implements Action { int count = 0; - + public TestCountAction() { count = 0; } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/annotation/MethodAnnotationTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/annotation/MethodAnnotationTests.java index 18bd8bcf..e8c0d8d0 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/annotation/MethodAnnotationTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/annotation/MethodAnnotationTests.java @@ -37,7 +37,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo import org.springframework.statemachine.processor.StateMachineAnnotationPostProcessor; public class MethodAnnotationTests extends AbstractStateMachineTests { - + @Test @SuppressWarnings("unchecked") public void testMethodAnnotations() throws Exception { @@ -46,24 +46,25 @@ public class MethodAnnotationTests extends AbstractStateMachineTests { EnumStateMachine machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); assertThat(context.containsBean("fooMachine"), is(true)); + machine.start(); Bean1 bean1 = context.getBean(Bean1.class); - + // this event should cause 'method1' to get called machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build()); - + assertThat(bean1.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true)); assertThat(bean1.onOnTransitionFromS2ToS3Latch.await(2, TimeUnit.SECONDS), is(false)); - + context.close(); } - + @WithStateMachine(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE) static class Bean1 { - + CountDownLatch onMethod1Latch = new CountDownLatch(1); CountDownLatch onOnTransitionFromS2ToS3Latch = new CountDownLatch(1); - + @OnTransition(source = "S1", target = "S2") public void method1() { onMethod1Latch.countDown(); @@ -73,8 +74,8 @@ public class MethodAnnotationTests extends AbstractStateMachineTests { public void onTransitionFromS2ToS3() { onOnTransitionFromS2ToS3Latch.countDown(); } - - + + } @Configuration @@ -84,20 +85,20 @@ public class MethodAnnotationTests extends AbstractStateMachineTests { public Bean1 bean1() { return new Bean1(); } - + } - - + + @Configuration static class AnnoConfig { - + @Bean(name="org.springframework.statemachine.internal.springStateMachineAnnotationPostProcessor") public StateMachineAnnotationPostProcessor springStateMachineAnnotationPostProcessor() { return new StateMachineAnnotationPostProcessor(); } - + } - + @Configuration @EnableStateMachine(name = {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, "fooMachine"}) static class Config1 extends EnumStateMachineConfigurerAdapter { @@ -125,12 +126,12 @@ public class MethodAnnotationTests extends AbstractStateMachineTests { .target(TestStates.S3) .event(TestEvents.E2); } - + @Bean public TestGuard testGuard() { return new TestGuard(); } - + @Bean public TestAction testAction() { return new TestAction(); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java index c31ea492..cfda6f87 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java @@ -31,48 +31,62 @@ import org.springframework.statemachine.AbstractStateMachineTests; import org.springframework.statemachine.EnumStateMachine; import org.springframework.statemachine.StateMachineSystemConstants; import org.springframework.statemachine.TestUtils; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; /** * Tests for state machine configuration. - * + * * @author Janne Valkealahti * */ public class ConfigurationTests extends AbstractStateMachineTests { + @Override + protected AnnotationConfigApplicationContext buildContext() { + return new AnnotationConfigApplicationContext(); + } + @SuppressWarnings({ "unchecked" }) @Test public void testStates() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config1.class); - assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); + context.register(Config1.class); + context.refresh(); + assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = - ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); assertThat(machine, notNullValue()); - TestAction testAction = ctx.getBean("testAction", TestAction.class); - TestGuard testGuard = ctx.getBean("testGuard", TestGuard.class); + TestAction testAction = context.getBean("testAction", TestAction.class); + TestGuard testGuard = context.getBean("testGuard", TestGuard.class); assertThat(testAction, notNullValue()); assertThat(testGuard, notNullValue()); - ctx.close(); } - + @SuppressWarnings({ "unchecked" }) @Test public void testEndState() throws Exception { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config3.class); - assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); + context.register(Config3.class); + context.refresh(); + assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = - ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); assertThat(machine, notNullValue()); Object endState = TestUtils.readField("endState", machine); assertThat(endState, notNullValue()); - ctx.close(); } - + + @SuppressWarnings({ "unchecked" }) + @Test + public void testSimpleSubmachine() throws Exception { + context.register(Config4.class); + context.refresh(); + assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); + EnumStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + assertThat(machine, notNullValue()); + } + @Configuration @EnableStateMachine public static class Config1 extends EnumStateMachineConfigurerAdapter { @@ -103,7 +117,7 @@ public class ConfigurationTests extends AbstractStateMachineTests { public TestAction testAction() { return new TestAction(); } - + @Bean public TestGuard testGuard() { return new TestGuard(); @@ -152,12 +166,15 @@ public class ConfigurationTests extends AbstractStateMachineTests { @Override public void configure(StateMachineStateConfigurer states) throws Exception { states - .withStates() + .withSubStates(TestStates.S1, null) + .initial(TestStates.S1) + .and() + .withStates(TestStates.S1) .initial(TestStates.S1) .end(TestStates.SF) .states(EnumSet.allOf(TestStates.class)); } - + @Override public void configure(StateMachineTransitionConfigurer transitions) throws Exception { transitions @@ -169,9 +186,14 @@ public class ConfigurationTests extends AbstractStateMachineTests { .withLocal() .source(TestStates.S1) .target(TestStates.S2) - .event(TestEvents.E2); + .event(TestEvents.E2) + .and() + .withInternal() + .source(TestStates.S2) + .event(TestEvents.E3); } } - + + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java index 64c0728a..420b9756 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java @@ -40,7 +40,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo /** * Tests from state machine app context events. - * + * * @author Janne Valkealahti * */ @@ -50,7 +50,7 @@ public class StateMachineEventTests extends AbstractStateMachineTests { protected AnnotationConfigApplicationContext buildContext() { return new AnnotationConfigApplicationContext(); } - + @Test public void testContextEvents() throws Exception { context.register(BaseConfig.class, StateMachineEventPublisherConfiguration.class, Config1.class); @@ -60,13 +60,14 @@ public class StateMachineEventTests extends AbstractStateMachineTests { @SuppressWarnings("unchecked") EnumStateMachine machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + machine.start(); assertThat(machine, notNullValue()); machine.sendEvent(TestEvents.E1); machine.sendEvent(TestEvents.E2); machine.sendEvent(TestEvents.E3); machine.sendEvent(TestEvents.E4); machine.sendEvent(TestEvents.EF); - + // 6 events instead of 5, first one is initial transition // to SI where source state is null assertThat(listener.onEventLatch.await(5, TimeUnit.SECONDS), is(true)); @@ -119,9 +120,9 @@ public class StateMachineEventTests extends AbstractStateMachineTests { public TestEventListener testEventListener() { return new TestEventListener(); } - + } - + static class TestEventListener implements ApplicationListener { CountDownLatch onEventLatch = new CountDownLatch(6); @@ -134,6 +135,6 @@ public class StateMachineEventTests extends AbstractStateMachineTests { onEventLatch.countDown(); } - } + } } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java index 82b0342e..cd3ccb95 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java @@ -44,7 +44,7 @@ import org.springframework.statemachine.support.DefaultStateContext; /** * Tests for using spel expressions in guards. - * + * * @author Janne Valkealahti * */ @@ -60,10 +60,10 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests { map.put("foo", "bar"); MessageHeaders headers = new MessageHeaders(map); DefaultStateContext stateContext = new DefaultStateContext(headers, null, null); - + assertThat(guard.evaluate(stateContext), is(true)); } - + @SuppressWarnings({ "unchecked" }) @Test public void testGuardDenyStateChange() throws Exception { @@ -71,13 +71,14 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests { assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + machine.start(); assertThat(machine.getState().getIds(), contains(TestStates.S1)); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build()); assertThat(machine.getState().getIds(), contains(TestStates.S1)); ctx.close(); } - + @Configuration @EnableStateMachine public static class Config1 extends EnumStateMachineConfigurerAdapter { @@ -99,7 +100,7 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests { .event(TestEvents.E1) .guardExpression("false"); } - + } - + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java index 1574d9e0..0902c56e 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java @@ -45,7 +45,7 @@ import org.springframework.statemachine.state.State; /** * Tests for state machine listener functionality. - * + * * @author Janne Valkealahti * */ @@ -58,6 +58,7 @@ public class ListenerTests extends AbstractStateMachineTests { @SuppressWarnings("unchecked") EnumStateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + machine.start(); TestStateMachineListener listener = new TestStateMachineListener(); machine.addStateListener(listener); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/EndStateTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/EndStateTests.java index 687d0c49..3cf48333 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/EndStateTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/EndStateTests.java @@ -42,7 +42,7 @@ public class EndStateTests extends AbstractStateMachineTests { protected AnnotationConfigApplicationContext buildContext() { return new AnnotationConfigApplicationContext(); } - + @Test public void testEndStateCompletes() { context.register(Config1.class); @@ -51,6 +51,7 @@ public class EndStateTests extends AbstractStateMachineTests { @SuppressWarnings("unchecked") EnumStateMachine machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + machine.start(); assertThat(machine, notNullValue()); assertThat(machine.isComplete(), is(false)); machine.sendEvent(TestEvents.E1); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/InitialStateTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/InitialStateTests.java index 97c7623f..4ca751f0 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/InitialStateTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/InitialStateTests.java @@ -34,7 +34,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo /** * Tests for functionality around state machine initial state. - * + * * @author Janne Valkealahti * */ @@ -48,10 +48,10 @@ public class InitialStateTests extends AbstractStateMachineTests { assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); - + machine.start(); assertThat(machine.getState().getIds(), contains(TestStates.S1)); } - + @Test(expected = Exception.class) public void testInitialStateMissingFailure() throws Exception { context.register(BaseConfig.class, Config2.class); @@ -110,10 +110,10 @@ public class InitialStateTests extends AbstractStateMachineTests { } } - + @Override protected AnnotationConfigApplicationContext buildContext() { return new AnnotationConfigApplicationContext(); } - + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/RegionStateTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/RegionStateTests.java index 86ba05ae..ed0613cc 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/RegionStateTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/RegionStateTests.java @@ -15,8 +15,8 @@ */ package org.springframework.statemachine.state; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.util.ArrayList; @@ -32,7 +32,7 @@ import org.springframework.statemachine.transition.Transition; /** * Tests for states using a submachine. - * + * * @author Janne Valkealahti * */ @@ -40,7 +40,7 @@ public class RegionStateTests extends AbstractStateMachineTests { @Test public void testSimpleRegionState() { - + State stateSI = new EnumState(TestStates.SI); State stateS1 = new EnumState(TestStates.S1); State stateS2 = new EnumState(TestStates.S2); @@ -71,20 +71,20 @@ public class RegionStateTests extends AbstractStateMachineTests { EnumStateMachine machine = new EnumStateMachine(states, transitions, stateSI, null); machine.setTaskExecutor(taskExecutor); machine.start(); - + Collection> regions = new ArrayList>(); regions.add(machine); - RegionState state = new RegionState(regions); - + RegionState state = new RegionState(TestStates.S11, regions); + assertThat(state.isSimple(), is(false)); assertThat(state.isComposite(), is(true)); assertThat(state.isOrthogonal(), is(false)); assertThat(state.isSubmachineState(), is(false)); - + assertThat(state.getIds(), contains(TestStates.SI)); - - - + + + } - + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/SubmachineStateTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/SubmachineStateTests.java index 13e97c98..aa2ada63 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/SubmachineStateTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/SubmachineStateTests.java @@ -31,7 +31,7 @@ import org.springframework.statemachine.transition.Transition; /** * Tests for states using a submachine. - * + * * @author Janne Valkealahti * */ @@ -39,7 +39,7 @@ public class SubmachineStateTests extends AbstractStateMachineTests { @Test public void testSimpleSubmachineState() { - + State stateSI = new EnumState(TestStates.SI); State stateS1 = new EnumState(TestStates.S1); State stateS2 = new EnumState(TestStates.S2); @@ -70,18 +70,18 @@ public class SubmachineStateTests extends AbstractStateMachineTests { EnumStateMachine machine = new EnumStateMachine(states, transitions, stateSI, null); machine.setTaskExecutor(taskExecutor); machine.start(); - - StateMachineState state = new StateMachineState(machine); - + + StateMachineState state = new StateMachineState(TestStates.S4, machine); + assertThat(state.isSimple(), is(false)); assertThat(state.isComposite(), is(false)); assertThat(state.isOrthogonal(), is(false)); assertThat(state.isSubmachineState(), is(true)); - - assertThat(state.getIds(), contains(TestStates.SI)); - - - + + assertThat(state.getIds(), contains(TestStates.S4, TestStates.SI)); + + + } - + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/tree/TreeTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/tree/TreeTests.java new file mode 100644 index 00000000..455c1fff --- /dev/null +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/tree/TreeTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.support.tree; + +import org.junit.Test; +import org.springframework.statemachine.support.tree.Tree.Node; + +public class TreeTests { + + @Test + public void testTree1() { + Tree tree = new Tree(); + tree.add("S", "S", null); + tree.add("S1", "S1", "S"); + tree.add("S2", "S2", "S"); + tree.add("S11", "S11", "S1"); + tree.add("S12", "S12", "S1"); + tree.add("S13", "S13", "S1"); + tree.add("S21", "S21", "S2"); + + TreeTraverser> traverser = new TreeTraverser>() { + @Override + public Iterable> children(Node root) { + return root.getChildren(); + } + }; + + for (Node node : traverser.postOrderTraversal(tree.getRoot())) { + System.out.println(node.getData()); + } + } + + @Test + public void testTree2() { + Tree tree = new Tree(); + tree.add("S2", "S2", "S"); + tree.add("S13", "S13", "S1"); + tree.add("S11", "S11", "S1"); + tree.add("S", "S", null); + tree.add("S12", "S12", "S1"); + tree.add("S21", "S21", "S2"); + tree.add("S1", "S1", "S"); + + TreeTraverser> traverser = new TreeTraverser>() { + @Override + public Iterable> children(Node root) { + return root.getChildren(); + } + }; + + for (Node node : traverser.postOrderTraversal(tree.getRoot())) { + System.out.println(node.getData()); + } + } + +} diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java index 4084adfc..03d8a370 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java @@ -41,7 +41,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo /** * Tests for state machine transitions. - * + * * @author Janne Valkealahti * */ @@ -54,14 +54,14 @@ public class TransitionTests extends AbstractStateMachineTests { assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); - + machine.start(); assertThat(machine.getState().getIds(), contains(TestStates.S1)); machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build()); assertThat(machine.getState().getIds(), contains(TestStates.S3)); ctx.close(); } - + @SuppressWarnings({ "unchecked" }) @Test public void testInternalTransition() throws Exception { @@ -69,28 +69,28 @@ public class TransitionTests extends AbstractStateMachineTests { assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)); EnumStateMachine machine = ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); - + machine.start(); TestExitAction testExitAction = ctx.getBean("testExitAction", TestExitAction.class); TestEntryAction testEntryAction = ctx.getBean("testEntryAction", TestEntryAction.class); TestAction externalTestAction = ctx.getBean("externalTestAction", TestAction.class); TestAction internalTestAction = ctx.getBean("internalTestAction", TestAction.class); - - assertThat(machine.getState().getIds(), contains(TestStates.S1)); + + assertThat(machine.getState().getIds(), contains(TestStates.S1)); assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); - assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); - + assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); + machine.sendEvent(TestEvents.E1); assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); - assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); + assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false)); assertThat(internalTestAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); - + machine.sendEvent(TestEvents.E2); assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); - assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); assertThat(externalTestAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true)); - + assertThat(machine.getState().getIds(), contains(TestStates.S2)); - ctx.close(); + ctx.close(); } @Configuration @@ -151,7 +151,7 @@ public class TransitionTests extends AbstractStateMachineTests { .event(TestEvents.E2) .action(externalTestAction()); } - + @Bean public Action testEntryAction() { return new TestEntryAction(); @@ -161,7 +161,7 @@ public class TransitionTests extends AbstractStateMachineTests { public Action testExitAction() { return new TestExitAction(); } - + @Bean public Action externalTestAction() { return new TestAction(); @@ -171,7 +171,7 @@ public class TransitionTests extends AbstractStateMachineTests { public Action internalTestAction() { return new TestAction(); } - + } - + }