Add support for default history state
- Add mechanism for transition into a default state vertex in cases where i.e. substate has never been entered, thus meaning there is no history. Without default vertex, submachine now does default entry logic to that region. - Fixes #204
This commit is contained in:
@@ -40,6 +40,7 @@ import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.model.ChoiceData;
|
||||
import org.springframework.statemachine.config.model.EntryData;
|
||||
import org.springframework.statemachine.config.model.ExitData;
|
||||
import org.springframework.statemachine.config.model.HistoryData;
|
||||
import org.springframework.statemachine.config.model.JunctionData;
|
||||
import org.springframework.statemachine.config.model.StateData;
|
||||
import org.springframework.statemachine.config.model.StateMachineModel;
|
||||
@@ -412,6 +413,7 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private StateMachine<S, E> buildMachine(Map<Object, StateMachine<S, E>> machineMap, Map<S, State<S, E>> stateMap,
|
||||
Map<S, StateHolder<S, E>> holderMap, Collection<StateData<S, E>> stateDatas, Collection<TransitionData<S, E>> transitionsData,
|
||||
BeanFactory beanFactory, Boolean contextEvents, DefaultExtendedState defaultExtendedState,
|
||||
@@ -458,11 +460,9 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
} else if (stateData.isEnd()) {
|
||||
pseudoState = new DefaultPseudoState<S, E>(PseudoStateKind.END);
|
||||
} else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_SHALLOW) {
|
||||
pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_SHALLOW);
|
||||
historyState = pseudoState;
|
||||
continue;
|
||||
} else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_DEEP) {
|
||||
pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_DEEP);
|
||||
historyState = pseudoState;
|
||||
continue;
|
||||
} else if (stateData.getPseudoStateKind() == PseudoStateKind.JOIN) {
|
||||
continue;
|
||||
} else if (stateData.getPseudoStateKind() == PseudoStateKind.FORK) {
|
||||
@@ -489,6 +489,48 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
}
|
||||
|
||||
for (StateData<S, E> stateData : stateDatas) {
|
||||
if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_SHALLOW) {
|
||||
State<S, E> defaultState = null;
|
||||
S s = stateData.getState();
|
||||
Collection<HistoryData<S,E>> historys = stateMachineTransitions.getHistorys();
|
||||
for (HistoryData<S,E> history : historys) {
|
||||
if (history.getSource().equals(s)) {
|
||||
defaultState = stateMap.get(history.getTarget());
|
||||
}
|
||||
}
|
||||
StateHolder<S, E> defaultStateHolder = new StateHolder<S, E>(defaultState);
|
||||
StateHolder<S, E> containingStateHolder = new StateHolder<S, E>(stateMap.get(stateData.getParent()));
|
||||
if (containingStateHolder.getState() == null) {
|
||||
holderMap.put((S)stateData.getParent(), containingStateHolder);
|
||||
}
|
||||
PseudoState<S, E> pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_SHALLOW, defaultStateHolder, containingStateHolder);
|
||||
state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
|
||||
stateData.getExitActions(), pseudoState);
|
||||
states.add(state);
|
||||
stateMap.put(stateData.getState(), state);
|
||||
historyState = pseudoState;
|
||||
} else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_DEEP) {
|
||||
State<S, E> defaultState = null;
|
||||
S s = stateData.getState();
|
||||
Collection<HistoryData<S,E>> historys = stateMachineTransitions.getHistorys();
|
||||
for (HistoryData<S,E> history : historys) {
|
||||
if (history.getSource().equals(s)) {
|
||||
defaultState = stateMap.get(history.getTarget());
|
||||
}
|
||||
}
|
||||
StateHolder<S, E> defaultStateHolder = new StateHolder<S, E>(defaultState);
|
||||
StateHolder<S, E> containingStateHolder = new StateHolder<S, E>(stateMap.get(stateData.getParent()));
|
||||
if (containingStateHolder.getState() == null) {
|
||||
holderMap.put((S)stateData.getParent(), containingStateHolder);
|
||||
}
|
||||
PseudoState<S, E> pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_DEEP, defaultStateHolder, containingStateHolder);
|
||||
state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
|
||||
stateData.getExitActions(), pseudoState);
|
||||
states.add(state);
|
||||
stateMap.put(stateData.getState(), state);
|
||||
historyState = pseudoState;
|
||||
}
|
||||
|
||||
if (stateData.getPseudoStateKind() == PseudoStateKind.CHOICE) {
|
||||
S s = stateData.getState();
|
||||
List<ChoiceData<S, E>> list = stateMachineTransitions.getChoices().get(s);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.statemachine.config.configurers.DefaultEntryTransitio
|
||||
import org.springframework.statemachine.config.configurers.DefaultExitTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultForkTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultHistoryTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultInternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultJoinTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultJunctionTransitionConfigurer;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.statemachine.config.configurers.EntryTransitionConfig
|
||||
import org.springframework.statemachine.config.configurers.ExitTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.ForkTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.HistoryTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.JoinTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.JunctionTransitionConfigurer;
|
||||
@@ -47,6 +49,7 @@ import org.springframework.statemachine.config.model.ChoiceData;
|
||||
import org.springframework.statemachine.config.model.ConfigurationData;
|
||||
import org.springframework.statemachine.config.model.EntryData;
|
||||
import org.springframework.statemachine.config.model.ExitData;
|
||||
import org.springframework.statemachine.config.model.HistoryData;
|
||||
import org.springframework.statemachine.config.model.JunctionData;
|
||||
import org.springframework.statemachine.config.model.TransitionData;
|
||||
import org.springframework.statemachine.config.model.TransitionsData;
|
||||
@@ -74,6 +77,7 @@ public class StateMachineTransitionBuilder<S, E>
|
||||
private final Map<S, List<S>> joins = new HashMap<S, List<S>>();
|
||||
private final Collection<EntryData<S, E>> entryData = new ArrayList<EntryData<S, E>>();
|
||||
private final Collection<ExitData<S, E>> exitData = new ArrayList<ExitData<S, E>>();
|
||||
private final Collection<HistoryData<S, E>> historyData = new ArrayList<HistoryData<S, E>>();
|
||||
|
||||
/**
|
||||
* Instantiates a new state machine transition builder.
|
||||
@@ -104,7 +108,7 @@ public class StateMachineTransitionBuilder<S, E>
|
||||
|
||||
@Override
|
||||
protected TransitionsData<S, E> performBuild() throws Exception {
|
||||
return new TransitionsData<S, E>(transitionData, choices, junctions, forks, joins, entryData, exitData);
|
||||
return new TransitionsData<S, E>(transitionData, choices, junctions, forks, joins, entryData, exitData, historyData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,6 +156,11 @@ public class StateMachineTransitionBuilder<S, E>
|
||||
return apply(new DefaultExitTransitionConfigurer<S, E>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistoryTransitionConfigurer<S, E> withHistory() throws Exception {
|
||||
return apply(new DefaultHistoryTransitionConfigurer<S, E>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the transition.
|
||||
*
|
||||
@@ -236,4 +245,14 @@ public class StateMachineTransitionBuilder<S, E>
|
||||
public void addJoin(S target, List<S> sources) {
|
||||
this.joins.put(target, sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the default history.
|
||||
*
|
||||
* @param source the source
|
||||
* @param target the target
|
||||
*/
|
||||
public void addDefaultHistory(S source, S target) {
|
||||
this.historyData.add(new HistoryData<S, E>(source, target));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.statemachine.config.configurers.EntryTransitionConfig
|
||||
import org.springframework.statemachine.config.configurers.ExitTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.ForkTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.HistoryTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.JoinTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.JunctionTransitionConfigurer;
|
||||
@@ -114,4 +115,12 @@ public interface StateMachineTransitionConfigurer<S, E> {
|
||||
* @throws Exception if configuration error happens
|
||||
*/
|
||||
ExitTransitionConfigurer<S, E> withExit() throws Exception;
|
||||
|
||||
/**
|
||||
* Gets a configurer for default history transition.
|
||||
*
|
||||
* @return {@link HistoryTransitionConfigurer} for chaining
|
||||
* @throws Exception if configuration error happens
|
||||
*/
|
||||
HistoryTransitionConfigurer<S, E> withHistory() throws Exception;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2016 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 org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.model.TransitionsData;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link HistoryTransitionConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class DefaultHistoryTransitionConfigurer<S, E>
|
||||
extends AnnotationConfigurerAdapter<TransitionsData<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
|
||||
implements HistoryTransitionConfigurer<S, E> {
|
||||
|
||||
private S source;
|
||||
private S target;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
|
||||
builder.addDefaultHistory(source, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistoryTransitionConfigurer<S, E> source(S source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistoryTransitionConfigurer<S, E> target(S target) {
|
||||
this.target = target;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2016 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 org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code TransitionConfigurer} interface for configuring {@link Transition}
|
||||
* with default history state.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface HistoryTransitionConfigurer<S, E>
|
||||
extends AnnotationConfigurerBuilder<StateMachineTransitionConfigurer<S, E>> {
|
||||
|
||||
/**
|
||||
* Specify a source state {@code S} for this {@link Transition}.
|
||||
*
|
||||
* @param source the source state {@code S}
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
HistoryTransitionConfigurer<S, E> source(S source);
|
||||
|
||||
/**
|
||||
* Specify a target state {@code S} for this {@link Transition}.
|
||||
*
|
||||
* @param target the target state {@code S}
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
HistoryTransitionConfigurer<S, E> target(S target);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 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.model;
|
||||
|
||||
/**
|
||||
* A simple data object keeping history related configs in a same place.
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class HistoryData<S, E> {
|
||||
private final S source;
|
||||
private final S target;
|
||||
|
||||
/**
|
||||
* Instantiates a new history data.
|
||||
*
|
||||
* @param source the source
|
||||
* @param target the target
|
||||
*/
|
||||
public HistoryData(S source, S target) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source.
|
||||
*
|
||||
* @return the source
|
||||
*/
|
||||
public S getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target.
|
||||
*
|
||||
* @return the target
|
||||
*/
|
||||
public S getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public class TransitionsData<S, E> {
|
||||
private final Map<S, List<S>> joins;
|
||||
private final Collection<EntryData<S, E>> entrys;
|
||||
private final Collection<ExitData<S, E>> exits;
|
||||
private final Collection<HistoryData<S, E>> historys;
|
||||
|
||||
/**
|
||||
* Instantiates a new transitions data.
|
||||
@@ -43,7 +44,7 @@ public class TransitionsData<S, E> {
|
||||
* @param transitionsData the transitions data
|
||||
*/
|
||||
public TransitionsData(Collection<TransitionData<S, E>> transitionsData) {
|
||||
this(transitionsData, null, null, null, null, null, null);
|
||||
this(transitionsData, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,10 +57,11 @@ public class TransitionsData<S, E> {
|
||||
* @param joins the joins
|
||||
* @param entrys the entrys
|
||||
* @param exits the exits
|
||||
* @param historys the historys
|
||||
*/
|
||||
public TransitionsData(Collection<TransitionData<S, E>> transitionsData, Map<S, List<ChoiceData<S, E>>> choices,
|
||||
Map<S, List<JunctionData<S, E>>> junctions, Map<S, List<S>> forks, Map<S, List<S>> joins, Collection<EntryData<S, E>> entrys,
|
||||
Collection<ExitData<S, E>> exits) {
|
||||
Collection<ExitData<S, E>> exits, Collection<HistoryData<S, E>> historys) {
|
||||
this.transitions = transitionsData;
|
||||
this.choices = choices;
|
||||
this.junctions = junctions;
|
||||
@@ -67,6 +69,7 @@ public class TransitionsData<S, E> {
|
||||
this.joins = joins;
|
||||
this.entrys = entrys;
|
||||
this.exits = exits;
|
||||
this.historys = historys;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,4 +134,13 @@ public class TransitionsData<S, E> {
|
||||
public Collection<ExitData<S, E>> getExits() {
|
||||
return exits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the historys.
|
||||
*
|
||||
* @return the historys
|
||||
*/
|
||||
public Collection<HistoryData<S, E>> getHistorys() {
|
||||
return historys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,24 +28,57 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class HistoryPseudoState<S, E> extends AbstractPseudoState<S, E> {
|
||||
|
||||
private final StateHolder<S, E> defaultState;
|
||||
private final StateHolder<S, E> containingState;
|
||||
private State<S, E> state;
|
||||
|
||||
/**
|
||||
* Instantiates a new history pseudo state.
|
||||
*
|
||||
* @param kind the kind
|
||||
* @param containingState the parent containing state
|
||||
*/
|
||||
public HistoryPseudoState(PseudoStateKind kind) {
|
||||
public HistoryPseudoState(PseudoStateKind kind, StateHolder<S, E> containingState) {
|
||||
this(kind, containingState, new StateHolder<S, E>(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new history pseudo state.
|
||||
*
|
||||
* @param kind the kind
|
||||
* @param defaultState the default history state
|
||||
* @param containingState the parent containing state
|
||||
*/
|
||||
public HistoryPseudoState(PseudoStateKind kind, StateHolder<S, E> defaultState, StateHolder<S, E> containingState) {
|
||||
super(kind);
|
||||
Assert.isTrue(PseudoStateKind.HISTORY_SHALLOW == kind || PseudoStateKind.HISTORY_DEEP == kind,
|
||||
"Pseudo state must be either shallow or deep");
|
||||
Assert.notNull(defaultState, "Holder defaultState must be set");
|
||||
Assert.notNull(containingState, "Holder containingState must be set");
|
||||
this.defaultState = defaultState;
|
||||
this.containingState = containingState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S, E> entry(StateContext<S, E> context) {
|
||||
return state;
|
||||
// if no logged history or history is final state,
|
||||
// go to default state. go to containing parent if
|
||||
// we have no history and there's no default state.
|
||||
if (state == null) {
|
||||
if (defaultState.getState() == null) {
|
||||
return containingState.getState();
|
||||
} else {
|
||||
return defaultState.getState();
|
||||
}
|
||||
} else {
|
||||
if (defaultState.getState() != null && state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.END) {
|
||||
return defaultState.getState();
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current recorded state.
|
||||
*
|
||||
@@ -63,5 +96,4 @@ public class HistoryPseudoState<S, E> extends AbstractPseudoState<S, E> {
|
||||
public State<S, E> getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,23 +73,23 @@ public abstract class AbstractStateMachineTests {
|
||||
}
|
||||
|
||||
public enum TestEvents {
|
||||
E1,E2,E3,E4,EF
|
||||
E1,E2,E3,E4,EF,EH
|
||||
}
|
||||
|
||||
public static enum TestStates2 {
|
||||
BUSY, PLAYING, PAUSED,
|
||||
IDLE, CLOSED, OPEN,
|
||||
PAUSED1, PAUSED2
|
||||
BUSY, PLAYING, PAUSED,
|
||||
IDLE, CLOSED, OPEN,
|
||||
PAUSED1, PAUSED2
|
||||
}
|
||||
|
||||
public static enum TestStates3 {
|
||||
READY,
|
||||
FORK, JOIN,
|
||||
TASKS, T1, T1E, T2, T2E, T3, T3E
|
||||
READY,
|
||||
FORK, JOIN,
|
||||
TASKS, T1, T1E, T2, T2E, T3, T3E
|
||||
}
|
||||
|
||||
public static enum TestEvents2 {
|
||||
PLAY, STOP, PAUSE, EJECT, LOAD
|
||||
PLAY, STOP, PAUSE, EJECT, LOAD
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -77,7 +77,7 @@ public class StateMachineModelTests {
|
||||
Map<String, List<JunctionData<String, String>>> junctions = new HashMap<>();
|
||||
Map<String, List<String>> forks = new HashMap<>();
|
||||
Map<String, List<String>> joins = new HashMap<>();
|
||||
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitions, choices, junctions, forks, joins, null, null);
|
||||
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitions, choices, junctions, forks, joins, null, null, null);
|
||||
|
||||
StateMachineModel<String, String> stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData);
|
||||
ObjectStateMachineFactory<String, String> factory = new ObjectStateMachineFactory<>(stateMachineModel);
|
||||
|
||||
@@ -55,6 +55,20 @@ public class HistoryStateTests extends AbstractStateMachineTests {
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S2, TestStates.S21));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testShallowNoHistoryDefaultsNormalEntry() {
|
||||
context.register(BaseConfig.class, Config1.class);
|
||||
context.refresh();
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
machine.start();
|
||||
machine.sendEvent(TestEvents.E4);
|
||||
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S2, TestStates.S20));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDeep() {
|
||||
@@ -88,6 +102,40 @@ public class HistoryStateTests extends AbstractStateMachineTests {
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S2, TestStates.S21, TestStates.S211));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDefaultNotEntered() {
|
||||
context.register(BaseConfig.class, Config4.class);
|
||||
context.refresh();
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1));
|
||||
machine.sendEvent(TestEvents.EH);
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S3, TestStates.S33));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDefaultHistoryIsFinal() {
|
||||
context.register(BaseConfig.class, Config4.class);
|
||||
context.refresh();
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1));
|
||||
machine.sendEvent(TestEvents.E1);
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S3, TestStates.S30));
|
||||
machine.sendEvent(TestEvents.EF);
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S3, TestStates.SF));
|
||||
machine.sendEvent(TestEvents.E4);
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1));
|
||||
machine.sendEvent(TestEvents.EH);
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S3, TestStates.S33));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -106,7 +154,6 @@ public class HistoryStateTests extends AbstractStateMachineTests {
|
||||
.state(TestStates.S20)
|
||||
.state(TestStates.S21)
|
||||
.history(TestStates.SH, History.SHALLOW);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,4 +286,64 @@ public class HistoryStateTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config4 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S1)
|
||||
.state(TestStates.S3)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(TestStates.S3)
|
||||
.initial(TestStates.S30)
|
||||
.state(TestStates.S31)
|
||||
.state(TestStates.S32)
|
||||
.state(TestStates.S33)
|
||||
.end(TestStates.SF)
|
||||
.history(TestStates.SH, History.SHALLOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S30)
|
||||
.target(TestStates.S31)
|
||||
.event(TestEvents.E2)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S31)
|
||||
.target(TestStates.S32)
|
||||
.event(TestEvents.E3)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S30)
|
||||
.target(TestStates.SF)
|
||||
.event(TestEvents.EF)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S3)
|
||||
.target(TestStates.S1)
|
||||
.event(TestEvents.E4)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.SH)
|
||||
.event(TestEvents.EH)
|
||||
.and()
|
||||
.withHistory()
|
||||
.source(TestStates.SH)
|
||||
.target(TestStates.S33);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user