Add configuration model for submachines
- some cleaning - refactoring some state concepts to have a better support for submachines
This commit is contained in:
@@ -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 <S> the type of state
|
||||
@@ -46,7 +55,7 @@ import org.springframework.statemachine.transition.TransitionKind;
|
||||
*/
|
||||
public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> extends LifecycleObjectSupport implements
|
||||
StateMachineFactory<S, E> {
|
||||
|
||||
|
||||
private final StateMachineTransitions<S, E> stateMachineTransitions;
|
||||
|
||||
private final StateMachineStates<S, E> stateMachineStates;
|
||||
@@ -65,16 +74,68 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
|
||||
|
||||
@Override
|
||||
public StateMachine<S, E> 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<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
|
||||
|
||||
// we eventually return this machine which is a last one build
|
||||
StateMachine<S, E> machine = null;
|
||||
|
||||
Map<Object, StateMachineStatesData<S, E>> states = stateMachineStates.getStates();
|
||||
Tree<StateMachineStatesData<S, E>> tree = new Tree<StateMachineStatesData<S, E>>();
|
||||
|
||||
for (Entry<Object, StateMachineStatesData<S, E>> e : states.entrySet()) {
|
||||
Object id = e.getKey();
|
||||
StateMachineStatesData<S, E> value = e.getValue();
|
||||
Object parent = value.getParent();
|
||||
tree.add(value, id, parent);
|
||||
}
|
||||
|
||||
TreeTraverser<Node<StateMachineStatesData<S, E>>> traverser = new TreeTraverser<Node<StateMachineStatesData<S, E>>>() {
|
||||
@Override
|
||||
public Iterable<Node<StateMachineStatesData<S, E>>> children(Node<StateMachineStatesData<S, E>> root) {
|
||||
return root.getChildren();
|
||||
}
|
||||
};
|
||||
|
||||
Stack<StackItem<S, E>> stack = new Stack<StackItem<S, E>>();
|
||||
for (Node<StateMachineStatesData<S, E>> node : traverser.postOrderTraversal(tree.getRoot())) {
|
||||
System.out.println(node.getData());
|
||||
|
||||
Object parent = node.getData().getParent();
|
||||
|
||||
if (!stack.empty()) {
|
||||
StackItem<S, E> pop = stack.pop();
|
||||
machine = buildSubMachine(stateMap, pop.machine, node.getData(), stateMachineTransitions, getBeanFactory());
|
||||
stack.push(new StackItem<S, E>(machine, parent));
|
||||
} else {
|
||||
machine = buildSimpleMachine(stateMap, node.getData(), stateMachineTransitions, getBeanFactory());
|
||||
stack.push(new StackItem<S, E>(machine, parent));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
public StateMachine<S, E> stateMachine() {
|
||||
Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
|
||||
for (StateData<S, E> stateData : stateMachineStates.getStates()) {
|
||||
|
||||
private static class StackItem<S, E> {
|
||||
StateMachine<S, E> machine;
|
||||
Object id;
|
||||
public StackItem(StateMachine<S, E> machine, Object id) {
|
||||
this.machine = machine;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private static <S extends Enum<S>, E extends Enum<E>> StateMachine<S, E> buildSimpleMachine(Map<S, State<S, E>> stateMap, StateMachineStatesData<S, E> statesData, StateMachineTransitions<S, E> transitionsData, BeanFactory beanFactory) {
|
||||
for (StateData<S, E> 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<S, E>(stateData.getState(), stateData.getDeferred(),
|
||||
@@ -82,7 +143,7 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
|
||||
}
|
||||
|
||||
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
|
||||
for (TransitionData<S, E> transitionData : stateMachineTransitions.getTransitions()) {
|
||||
for (TransitionData<S, E> transitionData : transitionsData.getTransitions()) {
|
||||
S source = transitionData.getSource();
|
||||
S target = transitionData.getTarget();
|
||||
E event = transitionData.getEvent();
|
||||
@@ -90,22 +151,57 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
|
||||
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
|
||||
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
|
||||
|
||||
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
|
||||
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
|
||||
transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
transitions.add(transition);
|
||||
}
|
||||
}
|
||||
|
||||
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(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 <S extends Enum<S>, E extends Enum<E>> StateMachine<S, E> buildSubMachine(Map<S, State<S, E>> stateMap, StateMachine<S, E> submachine, StateMachineStatesData<S, E> statesData, StateMachineTransitions<S, E> transitionsData, BeanFactory beanFactory) {
|
||||
Collection<State<S, E>> states = new ArrayList<State<S,E>>();
|
||||
State<S, E> state = null;
|
||||
for (StateData<S, E> stateData : statesData.getStates()) {
|
||||
state = new StateMachineState<S, E>(stateData.getState(), submachine, stateData.getDeferred(),
|
||||
stateData.getEntryActions(), stateData.getExitActions(), new DefaultPseudoState(
|
||||
PseudoStateKind.INITIAL));
|
||||
states.add(state);
|
||||
stateMap.put(stateData.getState(), state);
|
||||
}
|
||||
|
||||
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
|
||||
for (TransitionData<S, E> transitionData : transitionsData.getTransitions()) {
|
||||
S source = transitionData.getSource();
|
||||
S target = transitionData.getTarget();
|
||||
E event = transitionData.getEvent();
|
||||
if (transitionData.getKind() == TransitionKind.EXTERNAL) {
|
||||
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
|
||||
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
|
||||
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
|
||||
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
|
||||
transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
}
|
||||
}
|
||||
|
||||
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(states, transitions, state, null);
|
||||
|
||||
machine.afterPropertiesSet();
|
||||
if (beanFactory != null) {
|
||||
machine.setBeanFactory(beanFactory);
|
||||
}
|
||||
machine.setAutoStartup(isAutoStartup());
|
||||
machine.start();
|
||||
return machine;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineStateBuilder<S, E>
|
||||
extends AbstractConfiguredAnnotationBuilder<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
|
||||
implements StateMachineStateConfigurer<S, E> {
|
||||
|
||||
private Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
private S initialState;
|
||||
private S endState;
|
||||
private final Map<Object, StateMachineStatesData<S, E>> data = new HashMap<Object, StateMachineStatesData<S,E>>();
|
||||
|
||||
public StateMachineStateBuilder() {
|
||||
super();
|
||||
@@ -47,25 +58,26 @@ public class StateMachineStateBuilder<S, E>
|
||||
|
||||
@Override
|
||||
protected StateMachineStates<S, E> performBuild() throws Exception {
|
||||
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, endState, states);
|
||||
return bean;
|
||||
return new StateMachineStates<S, E>(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> withStates() throws Exception {
|
||||
return apply(new DefaultStateConfigurer<S, E>());
|
||||
return apply(new DefaultStateConfigurer<S, E>(null, null));
|
||||
}
|
||||
|
||||
public void add(Collection<StateData<S, E>> states) {
|
||||
this.states.addAll(states);
|
||||
@Override
|
||||
public StateConfigurer<S, E> withStates(Object parent) throws Exception {
|
||||
return apply(new DefaultStateConfigurer<S, E>(null, parent));
|
||||
}
|
||||
|
||||
public void setInitialState(S state) {
|
||||
this.initialState = state;
|
||||
@Override
|
||||
public SubStateConfigurer<S, E> withSubStates(S state, Object parent) throws Exception {
|
||||
return apply(new DefaultSubStateConfigurer<S, E>(state, state, parent));
|
||||
}
|
||||
|
||||
public void setEndState(S endState) {
|
||||
this.endState = endState;
|
||||
|
||||
public void add(Object id, Object parent, Collection<StateData<S, E>> states, S initialState, S endState) {
|
||||
data.put(id, new StateMachineStatesData<S, E>(states, initialState, endState, parent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<S, E> {
|
||||
|
||||
StateConfigurer<S, E> withStates() throws Exception;
|
||||
|
||||
StateConfigurer<S, E> withStates(Object parent) throws Exception;
|
||||
|
||||
SubStateConfigurer<S, E> withSubStates(S state, Object parent) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -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<S, E> {
|
||||
|
||||
private Collection<StateData<S, E>> states;
|
||||
private final Map<Object, StateMachineStatesData<S, E>> states;
|
||||
|
||||
private final S initialState;
|
||||
|
||||
private final S endState;
|
||||
|
||||
public StateMachineStates(S initialState, S endState, Collection<StateData<S, E>> states) {
|
||||
public StateMachineStates(Map<Object, StateMachineStatesData<S, E>> states) {
|
||||
this.states = states;
|
||||
this.initialState = initialState;
|
||||
this.endState = endState;
|
||||
}
|
||||
|
||||
public Collection<StateData<S, E>> getStates() {
|
||||
public Map<Object, StateMachineStatesData<S, E>> getStates() {
|
||||
return states;
|
||||
}
|
||||
|
||||
public S getInitialState() {
|
||||
return initialState;
|
||||
public static class StateMachineStatesData<S, E> {
|
||||
private Collection<StateData<S, E>> states;
|
||||
|
||||
private final S initialState;
|
||||
|
||||
private final S endState;
|
||||
|
||||
private final Object parent;
|
||||
|
||||
public StateMachineStatesData(Collection<StateData<S, E>> states, S initialState, S endState, Object parent) {
|
||||
this.states = states;
|
||||
this.initialState = initialState;
|
||||
this.endState = endState;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Collection<StateData<S, E>> 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<S, E> {
|
||||
private S state;
|
||||
private Collection<E> deferred;
|
||||
@@ -76,6 +103,12 @@ public class StateMachineStates<S, E> {
|
||||
public Collection<Action<S, E>> getExitActions() {
|
||||
return exitActions;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StateData [state=" + state + ", deferred=" + deferred + ", entryActions=" + entryActions
|
||||
+ ", exitActions=" + exitActions + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,17 +30,24 @@ public class DefaultStateConfigurer<S, E>
|
||||
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
|
||||
implements StateConfigurer<S, E> {
|
||||
|
||||
private final Object id;
|
||||
|
||||
private final Object parent;
|
||||
|
||||
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
|
||||
private S initial;
|
||||
|
||||
private S end;
|
||||
|
||||
|
||||
public DefaultStateConfigurer(Object id, Object parent) {
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateBuilder<S, E> 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<S, E>
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> end(S end) {
|
||||
this.end = end;
|
||||
@@ -59,7 +66,7 @@ public class DefaultStateConfigurer<S, E>
|
||||
public StateConfigurer<S, E> state(S state) {
|
||||
return state(state, (E[])null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
states.add(new StateData<S, E>(state, null, entryActions, exitActions));
|
||||
|
||||
@@ -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<S, E>
|
||||
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
|
||||
implements SubStateConfigurer<S, E> {
|
||||
|
||||
private S state;
|
||||
private final Object id;
|
||||
private final Object parent;
|
||||
|
||||
private S initial;
|
||||
private Collection<Action<S, E>> entryActions;
|
||||
private Collection<Action<S, E>> exitActions;
|
||||
|
||||
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
|
||||
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<S, E> builder) throws Exception {
|
||||
states.add(new StateData<S, E>(state, null, entryActions, exitActions));
|
||||
builder.add(id, parent, states, initial, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubStateConfigurer<S, E> initial(S initial) {
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubStateConfigurer<S, E> entry(Collection<Action<S, E>> entryActions) {
|
||||
this.entryActions = entryActions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubStateConfigurer<S, E> exit(Collection<Action<S, E>> exitActions) {
|
||||
this.exitActions = exitActions;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public interface StateConfigurer<S, E> extends
|
||||
StateConfigurer<S, E> state(S state);
|
||||
|
||||
StateConfigurer<S, E> state(S state, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions);
|
||||
|
||||
|
||||
StateConfigurer<S, E> state(S state, E... deferred);
|
||||
|
||||
StateConfigurer<S, E> states(Set<S> states);
|
||||
|
||||
@@ -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<S, E> extends
|
||||
AnnotationConfigurerBuilder<StateMachineStateConfigurer<S, E>> {
|
||||
|
||||
SubStateConfigurer<S, E> initial(S initial);
|
||||
|
||||
SubStateConfigurer<S, E> entry(Collection<Action<S, E>> entryActions);
|
||||
|
||||
SubStateConfigurer<S, E> exit(Collection<Action<S, E>> exitActions);
|
||||
|
||||
}
|
||||
@@ -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 <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
|
||||
|
||||
|
||||
private final Collection<S> 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<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* 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<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* Instantiates a new abstract simple state.
|
||||
*
|
||||
* @param id the id
|
||||
* @param id the state identifier
|
||||
* @param deferred the deferred
|
||||
*/
|
||||
public AbstractSimpleState(S id, Collection<E> deferred) {
|
||||
@@ -69,17 +69,17 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* 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<S, E> extends AbstractState<S, E> {
|
||||
*/
|
||||
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState, Collection<Region<S, E>> regions) {
|
||||
super(deferred, entryActions, exitActions, pseudoState, regions);
|
||||
super(id, deferred, entryActions, exitActions, pseudoState, regions);
|
||||
this.ids = new ArrayList<S>();
|
||||
this.ids.add(id);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* 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<S, E> extends AbstractState<S, E> {
|
||||
*/
|
||||
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState, StateMachine<S, E> submachine) {
|
||||
super(deferred, entryActions, exitActions, pseudoState, submachine);
|
||||
super(id, deferred, entryActions, exitActions, pseudoState, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
this.ids.add(id);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* 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<S, E> extends AbstractState<S, E> {
|
||||
*/
|
||||
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState) {
|
||||
super(deferred, entryActions, exitActions, pseudoState);
|
||||
super(id, deferred, entryActions, exitActions, pseudoState);
|
||||
this.ids = new ArrayList<S>();
|
||||
this.ids.add(id);
|
||||
}
|
||||
|
||||
@@ -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<S, E> implements State<S, E> {
|
||||
|
||||
private final S id;
|
||||
private final PseudoState pseudoState;
|
||||
private final Collection<E> deferred;
|
||||
private final Collection<Action<S, E>> entryActions;
|
||||
@@ -45,76 +45,83 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
/**
|
||||
* 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<E> deferred) {
|
||||
this(deferred, null, null);
|
||||
public AbstractState(S id, Collection<E> 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<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
this(deferred, entryActions, exitActions, null);
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> 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<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> 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<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState, StateMachine<S, E> 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<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState, Collection<Region<S, E>> 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<S, E> implements State<S, E> {
|
||||
* @param regions the regions
|
||||
* @param submachine the submachine
|
||||
*/
|
||||
private AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
private AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState, Collection<Region<S, E>> regions, StateMachine<S, E> submachine) {
|
||||
this.id = id;
|
||||
this.deferred = deferred;
|
||||
this.entryActions = entryActions;
|
||||
this.exitActions = exitActions;
|
||||
@@ -147,6 +155,11 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
@Override
|
||||
public abstract void entry(E event, StateContext<S, E> context);
|
||||
|
||||
@Override
|
||||
public S getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Collection<S> getIds();
|
||||
|
||||
@@ -200,8 +213,9 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
|
||||
@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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,56 +36,61 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
/**
|
||||
* Instantiates a new region state.
|
||||
*
|
||||
* @param id the state identifier
|
||||
* @param regions the regions
|
||||
*/
|
||||
public RegionState(Collection<Region<S, E>> regions) {
|
||||
super(null, null, null, null, regions);
|
||||
public RegionState(S id, Collection<Region<S, E>> 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<Region<S, E>> regions, Collection<E> deferred) {
|
||||
super(deferred, null, null, null, regions);
|
||||
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> 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<Region<S, E>> regions, PseudoState pseudoState) {
|
||||
super(null, null, null, pseudoState, regions);
|
||||
public RegionState(S id, Collection<Region<S, E>> 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<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> 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<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
super(deferred, entryActions, exitActions, null, regions);
|
||||
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
super(id, deferred, entryActions, exitActions, null, regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,6 +54,13 @@ public interface State<S, E> {
|
||||
*/
|
||||
void entry(E event, StateContext<S, E> 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.
|
||||
|
||||
@@ -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<S, E> extends AbstractState<S, E> {
|
||||
|
||||
private final Collection<S> ids;
|
||||
|
||||
/**
|
||||
* Instantiates a new state machine state.
|
||||
*
|
||||
* @param id the state identifier
|
||||
* @param submachine the submachine
|
||||
*/
|
||||
public StateMachineState(StateMachine<S, E> submachine) {
|
||||
super(null, null, null, null, submachine);
|
||||
public StateMachineState(S id, StateMachine<S, E> submachine) {
|
||||
super(id, null, null, null, null, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
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<S, E> submachine, Collection<E> deferred) {
|
||||
super(deferred, null, null, null, submachine);
|
||||
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred) {
|
||||
super(id, deferred, null, null, null, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
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<S, E> submachine, PseudoState pseudoState) {
|
||||
super(null, null, null, pseudoState, submachine);
|
||||
public StateMachineState(S id, StateMachine<S, E> submachine, PseudoState pseudoState) {
|
||||
super(id, null, null, null, pseudoState, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
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<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
|
||||
PseudoState pseudoState) {
|
||||
super(deferred, entryActions, exitActions, pseudoState, submachine);
|
||||
super(id, deferred, entryActions, exitActions, pseudoState, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
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<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
super(deferred, entryActions, exitActions, null, submachine);
|
||||
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
|
||||
super(id, deferred, entryActions, exitActions, null, submachine);
|
||||
this.ids = new ArrayList<S>();
|
||||
this.ids.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<S> getIds() {
|
||||
|
||||
Collection<S> ret = new ArrayList<S>(ids);
|
||||
State<S, E> 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<S, E> extends AbstractState<S, E> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StateMachineState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
|
||||
+ ", toString()=" + super.toString() + "]";
|
||||
return "StateMachineState [getIds()=" + getIds() + ", toString()=" + super.toString() + ", getClass()="
|
||||
+ getClass() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<T> implements Iterator<T> {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <T> the type of node data
|
||||
*/
|
||||
public class Tree<T> {
|
||||
|
||||
private Node<T> root;
|
||||
private final Map<Object, Node<T>> map = new HashMap<Object, Node<T>>();
|
||||
private final List<DataWrap<T>> notMapped = new ArrayList<DataWrap<T>>();
|
||||
|
||||
public Tree() {
|
||||
}
|
||||
|
||||
public Node<T> getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public void add(T data, Object id, Object parent) {
|
||||
notMapped.add(new DataWrap<T>(data, id, parent));
|
||||
tryMapping();
|
||||
}
|
||||
|
||||
private void tryMapping() {
|
||||
int size = notMapped.size();
|
||||
Iterator<DataWrap<T>> iter = notMapped.iterator();
|
||||
while(iter.hasNext()) {
|
||||
DataWrap<T> next = iter.next();
|
||||
if (next.parent == null) {
|
||||
Node<T> n = new Node<T>(next.data);
|
||||
map.put(next.id, n);
|
||||
root = n;
|
||||
iter.remove();
|
||||
} else {
|
||||
if (map.containsKey(next.parent)) {
|
||||
Node<T> n = new Node<T>(next.data);
|
||||
Node<T> 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<T> {
|
||||
private final T data;
|
||||
private final List<Node<T>> children;
|
||||
|
||||
public Node(T data) {
|
||||
this(data, null);
|
||||
}
|
||||
|
||||
public Node(T data, List<Node<T>> children) {
|
||||
this.data = data;
|
||||
this.children = children != null ? children : new ArrayList<Node<T>>();
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public List<Node<T>> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DataWrap<T> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<T> {
|
||||
|
||||
/**
|
||||
* Returns the children of the specified node. Must not contain null.
|
||||
*/
|
||||
public abstract Iterable<T> children(T root);
|
||||
|
||||
public final Iterable<T> postOrderTraversal(final T root) {
|
||||
Assert.notNull(root);
|
||||
return new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return postOrderIterator(root);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Iterator<T> postOrderIterator(T root) {
|
||||
return new PostOrderIterator(root);
|
||||
}
|
||||
|
||||
private static final class PostOrderNode<T> {
|
||||
final T root;
|
||||
final Iterator<T> childIterator;
|
||||
|
||||
PostOrderNode(T root, Iterator<T> childIterator) {
|
||||
Assert.notNull(root);
|
||||
Assert.notNull(childIterator);
|
||||
this.root = root;
|
||||
this.childIterator = childIterator;
|
||||
}
|
||||
}
|
||||
|
||||
private final class PostOrderIterator extends AbstractIterator<T> {
|
||||
private final ArrayDeque<PostOrderNode<T>> stack;
|
||||
|
||||
PostOrderIterator(T root) {
|
||||
this.stack = new ArrayDeque<PostOrderNode<T>>();
|
||||
stack.addLast(expand(root));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while (!stack.isEmpty()) {
|
||||
PostOrderNode<T> 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<T> expand(T t) {
|
||||
return new PostOrderNode<T>(t, children(t).iterator());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class RegionMachineTests {
|
||||
|
||||
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
|
||||
regions.add(machine);
|
||||
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(regions);
|
||||
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(TestStates.S11, regions);
|
||||
|
||||
assertThat(state.isSimple(), is(false));
|
||||
assertThat(state.isComposite(), is(true));
|
||||
@@ -165,7 +165,7 @@ public class RegionMachineTests {
|
||||
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
|
||||
regions.add(machine11);
|
||||
regions.add(machine12);
|
||||
RegionState<TestStates,TestEvents> stateR = new RegionState<TestStates,TestEvents>(regions, null, null, null, pseudoState);
|
||||
RegionState<TestStates,TestEvents> stateR = new RegionState<TestStates,TestEvents>(TestStates.S11, regions, null, null, null, pseudoState);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateR);
|
||||
|
||||
@@ -41,7 +41,8 @@ public class StateMachineFactoryTests extends AbstractStateMachineTests {
|
||||
EnumStateMachineFactory<TestStates, TestEvents> stateMachineFactory =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY, EnumStateMachineFactory.class);
|
||||
StateMachine<TestStates,TestEvents> 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));
|
||||
|
||||
@@ -44,6 +44,7 @@ public class StateMachineTests extends AbstractStateMachineTests {
|
||||
EnumStateMachine<TestStates,TestEvents> 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());
|
||||
|
||||
@@ -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<Action<TestStates, TestEvents>> exitActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS11.add(exitActionS11);
|
||||
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
|
||||
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
substates11.add(stateS11);
|
||||
@@ -100,13 +114,13 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS1.add(exitActionS1);
|
||||
|
||||
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
|
||||
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateS1);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS11ToS1 =
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS111ToS1 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS1, null, TestEvents.E1, null);
|
||||
transitions.add(transitionFromS11ToS1);
|
||||
transitions.add(transitionFromS111ToS1);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(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<Action<TestStates, TestEvents>> entryActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS111.add(entryActionS111);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS111.add(exitActionS111);
|
||||
State<TestStates,TestEvents> stateS111 = new EnumState<TestStates,TestEvents>(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState);
|
||||
|
||||
TestEntryAction entryActionS112 = new TestEntryAction("S112");
|
||||
TestExitAction exitActionS112 = new TestExitAction("S112");
|
||||
Collection<Action<TestStates, TestEvents>> entryActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS112.add(entryActionS112);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS111.add(exitActionS112);
|
||||
State<TestStates,TestEvents> stateS112 = new EnumState<TestStates,TestEvents>(TestStates.S112, null, entryActionsS112, exitActionsS112, null);
|
||||
|
||||
// submachine 1
|
||||
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
substates11.add(stateS111);
|
||||
substates11.add(stateS112);
|
||||
Collection<Transition<TestStates,TestEvents>> subtransitions11 = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
EnumStateMachine<TestStates, TestEvents> submachine11 = new EnumStateMachine<TestStates, TestEvents>(substates11, subtransitions11, stateS111, null);
|
||||
|
||||
// machine
|
||||
TestEntryAction entryActionS1 = new TestEntryAction("S1");
|
||||
TestExitAction exitActionS1 = new TestExitAction("S1");
|
||||
Collection<Action<TestStates, TestEvents>> entryActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS1.add(entryActionS1);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS1.add(exitActionS1);
|
||||
|
||||
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine11, null, entryActionsS1, exitActionsS1, pseudoState);
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateS1);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS111ToS112 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS112, null, TestEvents.E1, null);
|
||||
transitions.add(transitionFromS111ToS112);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(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<Action<TestStates, TestEvents>> exitActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS11.add(exitActionS11);
|
||||
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
|
||||
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
substates11.add(stateS11);
|
||||
@@ -197,7 +301,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS1.add(exitActionS1);
|
||||
|
||||
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
|
||||
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateS1);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
@@ -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<TestStates,TestEvents> 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<TestStates, TestEvents> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> 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<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S111)
|
||||
.target(TestStates.S1)
|
||||
.event(TestEvents.E1);
|
||||
}
|
||||
|
||||
@Bean(name = "entryActionS111")
|
||||
public Action<TestStates, TestEvents> entryActionS111() {
|
||||
return new TestEntryAction("S111");
|
||||
}
|
||||
|
||||
@Bean(name = "exitActionS111")
|
||||
public Action<TestStates, TestEvents> exitActionS111() {
|
||||
return new TestExitAction("S111");
|
||||
}
|
||||
|
||||
@Bean(name = "entryActionS11")
|
||||
public Action<TestStates, TestEvents> entryActionS11() {
|
||||
return new TestEntryAction("S11");
|
||||
}
|
||||
|
||||
@Bean(name = "exitActionS11")
|
||||
public Action<TestStates, TestEvents> exitActionS11() {
|
||||
return new TestExitAction("S11");
|
||||
}
|
||||
|
||||
@Bean(name = "entryActionS1")
|
||||
public Action<TestStates, TestEvents> entryActionS1() {
|
||||
return new TestEntryAction("S1");
|
||||
}
|
||||
|
||||
@Bean(name = "exitActionS1")
|
||||
public Action<TestStates, TestEvents> exitActionS1() {
|
||||
return new TestExitAction("S1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @Configuration
|
||||
// @EnableStateMachine(name = "submachine11Config")
|
||||
// public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
//
|
||||
// @Override
|
||||
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
// states
|
||||
// .withStates()
|
||||
// .initial(TestStates.S111)
|
||||
// .state(TestStates.S111, Arrays.asList(testEntryAction()), Arrays.asList(testExitAction()));
|
||||
// }
|
||||
//
|
||||
// @Bean(name = "entryActionS111")
|
||||
// public Action<TestStates, TestEvents> testEntryAction() {
|
||||
// return new TestEntryAction();
|
||||
// }
|
||||
//
|
||||
// @Bean(name = "exitActionS111")
|
||||
// public Action<TestStates, TestEvents> testExitAction() {
|
||||
// return new TestExitAction();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
// @Configuration
|
||||
// @EnableStateMachine(name = "submachine1Config")
|
||||
// public static class Config2 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
//
|
||||
// @Autowired
|
||||
// @Qualifier("submachine11Config")
|
||||
// public StateMachine<TestStates,TestEvents> submachine;
|
||||
//
|
||||
// @Override
|
||||
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
// states
|
||||
// .withSubmachine()
|
||||
// .submachine(submachine);
|
||||
// }
|
||||
//
|
||||
// @Bean(name = "entryActionS11")
|
||||
// public Action<TestStates, TestEvents> testEntryAction() {
|
||||
// return new TestEntryAction();
|
||||
// }
|
||||
//
|
||||
// @Bean(name = "exitActionS11")
|
||||
// public Action<TestStates, TestEvents> testExitAction() {
|
||||
// return new TestExitAction();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
// @Configuration
|
||||
// @EnableStateMachine(name = "submachineConfig")
|
||||
// public static class Config3 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
//
|
||||
// @Autowired
|
||||
// @Qualifier("submachine1Config")
|
||||
// public StateMachine<TestStates,TestEvents> submachine;
|
||||
//
|
||||
// @Override
|
||||
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
// states
|
||||
// .withSubmachine()
|
||||
// .submachine(submachine);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> 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();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> 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<TestStates, TestEvents> {
|
||||
|
||||
int count = 0;
|
||||
|
||||
|
||||
public TestCountAction() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> 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<TestStates, TestEvents> {
|
||||
@@ -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();
|
||||
|
||||
@@ -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<TestStates,TestEvents> 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<TestStates,TestEvents> 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<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -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<TestStates, TestEvents> 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<TestStates, TestEvents> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> 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<AbstractStateMachineEvent> {
|
||||
|
||||
CountDownLatch onEventLatch = new CountDownLatch(6);
|
||||
@@ -134,6 +135,6 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
onEventLatch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates, TestEvents> stateContext = new DefaultStateContext<TestStates, TestEvents>(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<TestStates,TestEvents> 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<TestStates, TestEvents> {
|
||||
@@ -99,7 +100,7 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("false");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> machine =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
machine.start();
|
||||
|
||||
TestStateMachineListener listener = new TestStateMachineListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
@@ -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<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
machine.start();
|
||||
assertThat(machine, notNullValue());
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.E1);
|
||||
|
||||
@@ -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<TestStates,TestEvents> 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
|
||||
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
|
||||
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
|
||||
@@ -71,20 +71,20 @@ public class RegionStateTests extends AbstractStateMachineTests {
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
|
||||
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
|
||||
regions.add(machine);
|
||||
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(regions);
|
||||
|
||||
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(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));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
|
||||
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
|
||||
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
|
||||
@@ -70,18 +70,18 @@ public class SubmachineStateTests extends AbstractStateMachineTests {
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
StateMachineState<TestStates,TestEvents> state = new StateMachineState<TestStates,TestEvents>(machine);
|
||||
|
||||
|
||||
StateMachineState<TestStates,TestEvents> state = new StateMachineState<TestStates,TestEvents>(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));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> tree = new Tree<String>();
|
||||
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<Node<String>> traverser = new TreeTraverser<Node<String>>() {
|
||||
@Override
|
||||
public Iterable<Node<String>> children(Node<String> root) {
|
||||
return root.getChildren();
|
||||
}
|
||||
};
|
||||
|
||||
for (Node<String> node : traverser.postOrderTraversal(tree.getRoot())) {
|
||||
System.out.println(node.getData());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTree2() {
|
||||
Tree<String> tree = new Tree<String>();
|
||||
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<Node<String>> traverser = new TreeTraverser<Node<String>>() {
|
||||
@Override
|
||||
public Iterable<Node<String>> children(Node<String> root) {
|
||||
return root.getChildren();
|
||||
}
|
||||
};
|
||||
|
||||
for (Node<String> node : traverser.postOrderTraversal(tree.getRoot())) {
|
||||
System.out.println(node.getData());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<TestStates,TestEvents> 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<TestStates,TestEvents> 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<TestStates, TestEvents> testEntryAction() {
|
||||
return new TestEntryAction();
|
||||
@@ -161,7 +161,7 @@ public class TransitionTests extends AbstractStateMachineTests {
|
||||
public Action<TestStates, TestEvents> testExitAction() {
|
||||
return new TestExitAction();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public Action<TestStates, TestEvents> externalTestAction() {
|
||||
return new TestAction();
|
||||
@@ -171,7 +171,7 @@ public class TransitionTests extends AbstractStateMachineTests {
|
||||
public Action<TestStates, TestEvents> internalTestAction() {
|
||||
return new TestAction();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user