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:
Janne Valkealahti
2016-04-22 07:46:17 +01:00
parent 9339f6c742
commit 1adca702f6
16 changed files with 723 additions and 22 deletions

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -46,6 +46,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.StateMachineComponentResolver;
@@ -72,6 +73,7 @@ public class UmlModelParser {
private final Collection<TransitionData<String, String>> transitionDatas = new ArrayList<TransitionData<String, String>>();
private final Collection<EntryData<String, String>> entrys = new ArrayList<EntryData<String, String>>();
private final Collection<ExitData<String, String>> exits = new ArrayList<ExitData<String, String>>();
private final Collection<HistoryData<String, String>> historys = new ArrayList<HistoryData<String, String>>();
private final Map<String, LinkedList<ChoiceData<String, String>>> choices = new HashMap<String, LinkedList<ChoiceData<String,String>>>();
private final Map<String, LinkedList<JunctionData<String, String>>> junctions = new HashMap<String, LinkedList<JunctionData<String,String>>>();
private final Map<String, List<String>> forks = new HashMap<String, List<String>>();
@@ -112,7 +114,7 @@ public class UmlModelParser {
HashMap<String, List<JunctionData<String, String>>> junctionsCopy = new HashMap<String, List<JunctionData<String, String>>>();
junctionsCopy.putAll(junctions);
return new DataHolder(new StatesData<>(stateDatas),
new TransitionsData<String, String>(transitionDatas, choicesCopy, junctionsCopy, forks, joins, entrys, exits));
new TransitionsData<String, String>(transitionDatas, choicesCopy, junctionsCopy, forks, joins, entrys, exits, historys));
}
private void handleRegion(Region region) {
@@ -259,6 +261,10 @@ public class UmlModelParser {
forks.put(transition.getSource().getName(), list);
}
list.add(transition.getTarget().getName());
} else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.SHALLOW_HISTORY_LITERAL) {
historys.add(new HistoryData<String, String>(transition.getSource().getName(), transition.getTarget().getName()));
} else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.DEEP_HISTORY_LITERAL) {
historys.add(new HistoryData<String, String>(transition.getSource().getName(), transition.getTarget().getName()));
}
}
if (transition.getTarget() instanceof Pseudostate) {

View File

@@ -462,6 +462,18 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
}
}
@Test
@SuppressWarnings("unchecked")
public void testSimpleHistoryDefault() {
context.register(Config12.class);
context.refresh();
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
stateMachine.start();
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1"));
stateMachine.sendEvent("E4");
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2", "S22"));
}
@Configuration
@EnableStateMachine
public static class Config2 extends StateMachineConfigurerAdapter<String, String> {
@@ -681,6 +693,23 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
}
}
@Configuration
@EnableStateMachine
public static class Config12 extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
model
.withModel()
.factory(modelFactory());
}
@Bean
public StateMachineModelFactory<String, String> modelFactory() {
return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/uml/simple-history-default.uml");
}
}
public static class LatchAction implements Action<String, String> {
CountDownLatch latch = new CountDownLatch(1);
@Override

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"/>

View File

@@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:style="http://www.eclipse.org/papyrus/infra/viewpoints/policy/style" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_T1ONMAPSEeaXyaQL1WyV3A" type="PapyrusUMLStateMachineDiagram" name="StateMachine Diagram" measurementUnit="Pixel">
<children xmi:type="notation:Shape" xmi:id="_T1ONMQPSEeaXyaQL1WyV3A" type="2000">
<children xmi:type="notation:DecorationNode" xmi:id="_T1ONMgPSEeaXyaQL1WyV3A" type="2001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T1ONMwPSEeaXyaQL1WyV3A" width="700" height="23"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_T1ONNAPSEeaXyaQL1WyV3A" type="2002">
<children xmi:type="notation:Shape" xmi:id="_T1ONNQPSEeaXyaQL1WyV3A" type="3000">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_T1ONNgPSEeaXyaQL1WyV3A" source="RegionAnnotationKey">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_T1ONNwPSEeaXyaQL1WyV3A" key="RegionZoneKey" value=""/>
</eAnnotations>
<children xmi:type="notation:DecorationNode" xmi:id="_T1ONOAPSEeaXyaQL1WyV3A" type="3002">
<children xmi:type="notation:Shape" xmi:id="_e88IgAPSEeaXyaQL1WyV3A" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_e88IggPSEeaXyaQL1WyV3A" type="6001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_hF7Y0APTEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_e88IgwPSEeaXyaQL1WyV3A" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_e88IhAPSEeaXyaQL1WyV3A" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_e88IhQPSEeaXyaQL1WyV3A" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_e88IhgPSEeaXyaQL1WyV3A" y="-1"/>
</children>
<element xmi:type="uml:State" href="simple-history-default.uml#_e85FMAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_e88IgQPSEeaXyaQL1WyV3A" x="110" y="87" height="161"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_hkW2sAPSEeaXyaQL1WyV3A" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_hkW2sgPSEeaXyaQL1WyV3A" type="6001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_j1AqsAPSEeaXyaQL1WyV3A" width="341" height="23"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_hkXdwAPSEeaXyaQL1WyV3A" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_hkXdwQPSEeaXyaQL1WyV3A" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_hkXdwgPSEeaXyaQL1WyV3A" type="6002">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_kZ9K0APSEeaXyaQL1WyV3A" source="PapyrusCSSForceValue">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_kZ9K0QPSEeaXyaQL1WyV3A" key="visible" value="true"/>
</eAnnotations>
<children xmi:type="notation:Shape" xmi:id="_kZ9x4APSEeaXyaQL1WyV3A" type="3000">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_kZ9x5APSEeaXyaQL1WyV3A" source="RegionAnnotationKey">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_kZ9x5QPSEeaXyaQL1WyV3A" key="RegionZoneKey" value=""/>
</eAnnotations>
<children xmi:type="notation:DecorationNode" xmi:id="_kZ9x4gPSEeaXyaQL1WyV3A" type="3002">
<children xmi:type="notation:Shape" xmi:id="_kZ_AAAPSEeaXyaQL1WyV3A" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_kZ_AAgPSEeaXyaQL1WyV3A" type="6001"/>
<children xmi:type="notation:DecorationNode" xmi:id="_kZ_AAwPSEeaXyaQL1WyV3A" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_kZ_ABAPSEeaXyaQL1WyV3A" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_kZ_ABQPSEeaXyaQL1WyV3A" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kZ_ABgPSEeaXyaQL1WyV3A"/>
</children>
<element xmi:type="uml:State" href="simple-history-default.uml#_kZ-Y8APSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kZ_AAQPSEeaXyaQL1WyV3A" x="100" y="37"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_kxSswAPSEeaXyaQL1WyV3A" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_kxSswgPSEeaXyaQL1WyV3A" type="6001"/>
<children xmi:type="notation:DecorationNode" xmi:id="_kxSswwPSEeaXyaQL1WyV3A" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_kxSsxAPSEeaXyaQL1WyV3A" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_kxSsxQPSEeaXyaQL1WyV3A" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kxSsxgPSEeaXyaQL1WyV3A"/>
</children>
<element xmi:type="uml:State" href="simple-history-default.uml#_kxNNMAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kxSswQPSEeaXyaQL1WyV3A" x="240" y="37"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_yTtv8APSEeaXyaQL1WyV3A" type="8000">
<children xmi:type="notation:DecorationNode" xmi:id="_yTtv8gPSEeaXyaQL1WyV3A" type="8001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_yTtv8wPSEeaXyaQL1WyV3A" x="25" y="3"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_yTtv9APSEeaXyaQL1WyV3A" type="8002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_yTtv9QPSEeaXyaQL1WyV3A" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="simple-history-default.uml#_yTlNEAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_yTtv8QPSEeaXyaQL1WyV3A" x="20" y="37"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_9MwWEAPSEeaXyaQL1WyV3A" type="13000">
<children xmi:type="notation:DecorationNode" xmi:id="_9Mw9IAPSEeaXyaQL1WyV3A" type="13001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_9Mw9IQPSEeaXyaQL1WyV3A" x="20" y="-20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_9Mw9IgPSEeaXyaQL1WyV3A" type="13002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_9Mw9IwPSEeaXyaQL1WyV3A" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="simple-history-default.uml#_9MnMIAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9MwWEQPSEeaXyaQL1WyV3A" x="100" y="117"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_RrF3AAf7EeayEI1yTJhWhg" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_RrF3Agf7EeayEI1yTJhWhg" type="6001"/>
<children xmi:type="notation:DecorationNode" xmi:id="_RrGeEAf7EeayEI1yTJhWhg" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_RrGeEQf7EeayEI1yTJhWhg" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_RrGeEgf7EeayEI1yTJhWhg" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_RrGeEwf7EeayEI1yTJhWhg"/>
</children>
<element xmi:type="uml:State" href="simple-history-default.uml#_Rq5CsAf7EeayEI1yTJhWhg"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_RrF3AQf7EeayEI1yTJhWhg" x="240" y="117"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kZ9x4wPSEeaXyaQL1WyV3A"/>
</children>
<element xmi:type="uml:Region" href="simple-history-default.uml#_kZ9K0gPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_kZ9x4QPSEeaXyaQL1WyV3A" width="341" height="178"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_hkXdwwPSEeaXyaQL1WyV3A" y="23" width="341" height="178"/>
</children>
<element xmi:type="uml:State" href="simple-history-default.uml#_hkTzYAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_hkW2sQPSEeaXyaQL1WyV3A" x="230" y="47" width="341" height="201"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_phuV4APSEeaXyaQL1WyV3A" type="8000">
<children xmi:type="notation:DecorationNode" xmi:id="_phuV4gPSEeaXyaQL1WyV3A" type="8001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_phuV4wPSEeaXyaQL1WyV3A" x="25" y="3"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_phuV5APSEeaXyaQL1WyV3A" type="8002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_phuV5QPSEeaXyaQL1WyV3A" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="simple-history-default.uml#_phoPQAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_phuV4QPSEeaXyaQL1WyV3A" x="30" y="87"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T1ONOQPSEeaXyaQL1WyV3A"/>
</children>
<element xmi:type="uml:Region" href="simple-history-default.uml#_T1NmIAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T1ONOgPSEeaXyaQL1WyV3A" width="700" height="287"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T1ONOwPSEeaXyaQL1WyV3A" y="23" width="700" height="287"/>
</children>
<element xmi:type="uml:StateMachine" href="simple-history-default.uml#_T1M_EAPSEeaXyaQL1WyV3A"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T1ONPAPSEeaXyaQL1WyV3A" x="30" y="30" width="700" height="310"/>
</children>
<styles xmi:type="notation:StringValueStyle" xmi:id="_T1ONPQPSEeaXyaQL1WyV3A" name="diagram_compatibility_version" stringValue="1.1.0"/>
<styles xmi:type="notation:DiagramStyle" xmi:id="_T1ONPgPSEeaXyaQL1WyV3A"/>
<styles xmi:type="style:PapyrusViewStyle" xmi:id="_T1ONPwPSEeaXyaQL1WyV3A">
<owner xmi:type="uml:Model" href="simple-history-default.uml#_T1LJ4APSEeaXyaQL1WyV3A"/>
</styles>
<element xmi:type="uml:StateMachine" href="simple-history-default.uml#_T1M_EAPSEeaXyaQL1WyV3A"/>
<edges xmi:type="notation:Connector" xmi:id="_tS0o8APSEeaXyaQL1WyV3A" type="7000" source="_phuV4APSEeaXyaQL1WyV3A" target="_e88IgAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_tS0o8wPSEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_tS0o9APSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_tS0o9QPSEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_tS0o9gPSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_tS0o9wPSEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_tS1QAAPSEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_tS0o8QPSEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_tSsGEAPSEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_tS0o8gPSEeaXyaQL1WyV3A" points="[7, -6, -81, 0]$[87, -10, -1, -4]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_tTIyAAPSEeaXyaQL1WyV3A" id="(1.0,0.45)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_tTIyAQPSEeaXyaQL1WyV3A" id="(0.0,0.10638297872340426)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_vV8-UAPSEeaXyaQL1WyV3A" type="7000" source="_e88IgAPSEeaXyaQL1WyV3A" target="_hkW2sAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_vV8-UwPSEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_vV8-VAPSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_vV8-VQPSEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_vV8-VgPSEeaXyaQL1WyV3A" x="-10" y="-13"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_vV8-VwPSEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_vV8-WAPSEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_vV8-UQPSEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_vV1CgAPSEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vV8-UgPSEeaXyaQL1WyV3A" points="[38, 4, -100, -11]$[120, 11, -18, -4]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vWTjoAPSEeaXyaQL1WyV3A" id="(1.0,0.14184397163120568)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vWTjoQPSEeaXyaQL1WyV3A" id="(0.0,0.29850746268656714)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_xIpD8APSEeaXyaQL1WyV3A" type="7000" source="_kZ_AAAPSEeaXyaQL1WyV3A" target="_kxSswAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_xIpD8wPSEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_xIpD9APSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_xIpD9QPSEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_xIpD9gPSEeaXyaQL1WyV3A" x="-1" y="-11"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_xIpD9wPSEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_xIpD-APSEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_xIpD8QPSEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_xIeE0APSEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_xIpD8gPSEeaXyaQL1WyV3A" points="[38, 5, -62, 5]$[102, 3, 2, 3]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_xJCFgAPSEeaXyaQL1WyV3A" id="(1.0,0.44680851063829785)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_xJCFgQPSEeaXyaQL1WyV3A" id="(0.0,0.425531914893617)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_2lE3cAPSEeaXyaQL1WyV3A" type="7000" source="_yTtv8APSEeaXyaQL1WyV3A" target="_kZ_AAAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_2lE3cwPSEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_2lE3dAPSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_2lE3dQPSEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_2lE3dgPSEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_2lE3dwPSEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_2lE3eAPSEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_2lE3cQPSEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_2k8UkAPSEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_2lE3cgPSEeaXyaQL1WyV3A" points="[8, 1, -75, 0]$[77, -10, -6, -11]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_2lcD0APSEeaXyaQL1WyV3A" id="(1.0,0.45)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_2lcD0QPSEeaXyaQL1WyV3A" id="(0.0,0.2127659574468085)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_DE-GoAPTEeaXyaQL1WyV3A" type="7000" source="_e88IgAPSEeaXyaQL1WyV3A" target="_9MwWEAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_DE-tsAPTEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_DE-tsQPTEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_DE-tsgPTEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_DE-tswPTEeaXyaQL1WyV3A" x="-48" y="-21"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_DE-ttAPTEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_DE-ttQPTEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_DE-GoQPTEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_DEzHgAPTEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_DE-GogPTEeaXyaQL1WyV3A" points="[40, 18, -150, -72]$[193, 98, 3, 8]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_DFec8APTEeaXyaQL1WyV3A" id="(1.0,0.37267080745341613)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_DFec8QPTEeaXyaQL1WyV3A" id="(0.0,0.3)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_JsAsEAPTEeaXyaQL1WyV3A" type="7000" source="_hkW2sAPSEeaXyaQL1WyV3A" target="_e88IgAPSEeaXyaQL1WyV3A">
<children xmi:type="notation:DecorationNode" xmi:id="_JsAsEwPTEeaXyaQL1WyV3A" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_JsAsFAPTEeaXyaQL1WyV3A"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_JsAsFQPTEeaXyaQL1WyV3A" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_JsAsFgPTEeaXyaQL1WyV3A" x="-12" y="14"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_JsAsFwPTEeaXyaQL1WyV3A" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_JsAsGAPTEeaXyaQL1WyV3A" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_JsAsEQPTEeaXyaQL1WyV3A"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_Jr4JMAPTEeaXyaQL1WyV3A"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_JsAsEgPTEeaXyaQL1WyV3A" points="[-16, -10, 111, 65]$[-136, -68, -9, 7]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_JscJ4APTEeaXyaQL1WyV3A" id="(0.0,0.7960199004975125)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_JscJ4QPTEeaXyaQL1WyV3A" id="(1.0,0.8226950354609929)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_VhKJoAf7EeayEI1yTJhWhg" type="7000" source="_9MwWEAPSEeaXyaQL1WyV3A" target="_RrF3AAf7EeayEI1yTJhWhg">
<children xmi:type="notation:DecorationNode" xmi:id="_VhKwsAf7EeayEI1yTJhWhg" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_VhKwsQf7EeayEI1yTJhWhg"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_VhKwsgf7EeayEI1yTJhWhg" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_VhKwswf7EeayEI1yTJhWhg"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_VhKwtAf7EeayEI1yTJhWhg" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_VhKwtQf7EeayEI1yTJhWhg" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_VhKJoQf7EeayEI1yTJhWhg"/>
<element xmi:type="uml:Transition" href="simple-history-default.uml#_Vg8HMAf7EeayEI1yTJhWhg"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_VhKJogf7EeayEI1yTJhWhg" points="[9, 0, -130, -13]$[130, 12, -9, -1]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_VhwmkAf7EeayEI1yTJhWhg" id="(1.0,0.5)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_VhwmkQf7EeayEI1yTJhWhg" id="(0.0,0.46808510638297873)"/>
</edges>
</notation:Diagram>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_T1LJ4APSEeaXyaQL1WyV3A" name="RootElement">
<packagedElement xmi:type="uml:StateMachine" xmi:id="_T1M_EAPSEeaXyaQL1WyV3A" name="StateMachine">
<region xmi:type="uml:Region" xmi:id="_T1NmIAPSEeaXyaQL1WyV3A" name="Region1">
<transition xmi:type="uml:Transition" xmi:id="_tSsGEAPSEeaXyaQL1WyV3A" source="_phoPQAPSEeaXyaQL1WyV3A" target="_e85FMAPSEeaXyaQL1WyV3A"/>
<transition xmi:type="uml:Transition" xmi:id="_vV1CgAPSEeaXyaQL1WyV3A" source="_e85FMAPSEeaXyaQL1WyV3A" target="_hkTzYAPSEeaXyaQL1WyV3A">
<trigger xmi:type="uml:Trigger" xmi:id="_fO2icAPTEeaXyaQL1WyV3A" event="_TnRbUAPTEeaXyaQL1WyV3A"/>
</transition>
<transition xmi:type="uml:Transition" xmi:id="_DEzHgAPTEeaXyaQL1WyV3A" source="_e85FMAPSEeaXyaQL1WyV3A" target="_9MnMIAPSEeaXyaQL1WyV3A">
<trigger xmi:type="uml:Trigger" xmi:id="_x7luYAPTEeaXyaQL1WyV3A" event="_aUT7EAPTEeaXyaQL1WyV3A"/>
</transition>
<transition xmi:type="uml:Transition" xmi:id="_Jr4JMAPTEeaXyaQL1WyV3A" source="_hkTzYAPSEeaXyaQL1WyV3A" target="_e85FMAPSEeaXyaQL1WyV3A">
<trigger xmi:type="uml:Trigger" xmi:id="_uX4XEAPTEeaXyaQL1WyV3A" event="_YBCQ4APTEeaXyaQL1WyV3A"/>
</transition>
<subvertex xmi:type="uml:State" xmi:id="_e85FMAPSEeaXyaQL1WyV3A" name="S1"/>
<subvertex xmi:type="uml:State" xmi:id="_hkTzYAPSEeaXyaQL1WyV3A" name="S2">
<region xmi:type="uml:Region" xmi:id="_kZ9K0gPSEeaXyaQL1WyV3A" name="Region1">
<transition xmi:type="uml:Transition" xmi:id="_xIeE0APSEeaXyaQL1WyV3A" source="_kZ-Y8APSEeaXyaQL1WyV3A" target="_kxNNMAPSEeaXyaQL1WyV3A">
<trigger xmi:type="uml:Trigger" xmi:id="_rl-QIAPTEeaXyaQL1WyV3A" event="_VwZD8APTEeaXyaQL1WyV3A"/>
</transition>
<transition xmi:type="uml:Transition" xmi:id="_2k8UkAPSEeaXyaQL1WyV3A" source="_yTlNEAPSEeaXyaQL1WyV3A" target="_kZ-Y8APSEeaXyaQL1WyV3A"/>
<transition xmi:type="uml:Transition" xmi:id="_Vg8HMAf7EeayEI1yTJhWhg" source="_9MnMIAPSEeaXyaQL1WyV3A" target="_Rq5CsAf7EeayEI1yTJhWhg"/>
<subvertex xmi:type="uml:State" xmi:id="_kZ-Y8APSEeaXyaQL1WyV3A" name="S20"/>
<subvertex xmi:type="uml:State" xmi:id="_kxNNMAPSEeaXyaQL1WyV3A" name="S21"/>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_yTlNEAPSEeaXyaQL1WyV3A"/>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_9MnMIAPSEeaXyaQL1WyV3A" name="SH" kind="shallowHistory"/>
<subvertex xmi:type="uml:State" xmi:id="_Rq5CsAf7EeayEI1yTJhWhg" name="S22"/>
</region>
</subvertex>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_phoPQAPSEeaXyaQL1WyV3A"/>
</region>
</packagedElement>
<packagedElement xmi:type="uml:Signal" xmi:id="_Ly45AAPTEeaXyaQL1WyV3A" name="E1"/>
<packagedElement xmi:type="uml:Signal" xmi:id="_NAAzcAPTEeaXyaQL1WyV3A" name="E2"/>
<packagedElement xmi:type="uml:Signal" xmi:id="_OI_0wAPTEeaXyaQL1WyV3A" name="E3"/>
<packagedElement xmi:type="uml:Signal" xmi:id="_SaNyUAPTEeaXyaQL1WyV3A" name="E4"/>
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_TnRbUAPTEeaXyaQL1WyV3A" name="SignalEventE1" signal="_Ly45AAPTEeaXyaQL1WyV3A"/>
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_VwZD8APTEeaXyaQL1WyV3A" name="SignalEventE2" signal="_NAAzcAPTEeaXyaQL1WyV3A"/>
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_YBCQ4APTEeaXyaQL1WyV3A" name="SignalEventE3" signal="_OI_0wAPTEeaXyaQL1WyV3A"/>
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_aUT7EAPTEeaXyaQL1WyV3A" name="SignalEventE4" signal="_SaNyUAPTEeaXyaQL1WyV3A"/>
</uml:Model>