diff --git a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlModelParser.java b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlModelParser.java index cd511149..dbf0c091 100644 --- a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlModelParser.java +++ b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlModelParser.java @@ -24,6 +24,8 @@ import org.eclipse.uml2.uml.Activity; import org.eclipse.uml2.uml.Event; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.PackageableElement; +import org.eclipse.uml2.uml.Pseudostate; +import org.eclipse.uml2.uml.PseudostateKind; import org.eclipse.uml2.uml.Region; import org.eclipse.uml2.uml.Signal; import org.eclipse.uml2.uml.SignalEvent; @@ -34,11 +36,14 @@ import org.eclipse.uml2.uml.Trigger; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.Vertex; import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.model.EntryData; +import org.springframework.statemachine.config.model.ExitData; import org.springframework.statemachine.config.model.StateData; import org.springframework.statemachine.config.model.StateMachineComponentResolver; import org.springframework.statemachine.config.model.StatesData; import org.springframework.statemachine.config.model.TransitionData; import org.springframework.statemachine.config.model.TransitionsData; +import org.springframework.statemachine.state.PseudoStateKind; import org.springframework.util.Assert; /** @@ -53,6 +58,8 @@ public class UmlModelParser { private final StateMachineComponentResolver resolver; private final Collection> stateDatas = new ArrayList>(); private final Collection> transitionDatas = new ArrayList>(); + private final Collection> entrys = new ArrayList>(); + private final Collection> exits = new ArrayList>(); /** * Instantiates a new uml model parser. @@ -83,7 +90,7 @@ public class UmlModelParser { for (Region region : stateMachine.getRegions()) { handleRegion(region); } - return new DataHolder(new StatesData<>(stateDatas), new TransitionsData(transitionDatas)); + return new DataHolder(new StatesData<>(stateDatas), new TransitionsData(transitionDatas, null, null, null, entrys, exits)); } private void handleRegion(Region region) { @@ -106,6 +113,22 @@ public class UmlModelParser { stateData.setEnd(true); } stateDatas.add(stateData); + + // add states via entry/exit points + for (Pseudostate cp : state.getConnectionPoints()) { + PseudoStateKind kind = null; + if (cp.getKind() == PseudostateKind.ENTRY_POINT_LITERAL) { + kind = PseudoStateKind.ENTRY; + } else if (cp.getKind() == PseudostateKind.EXIT_POINT_LITERAL) { + kind = PseudoStateKind.EXIT; + } + if (kind != null) { + StateData cpStateData = new StateData<>(parent, regionId, cp.getName(), false); + cpStateData.setPseudoStateKind(kind); + stateDatas.add(cpStateData); + } + } + // do recursive handling of regions for (Region sub : state.getRegions()) { handleRegion(sub); @@ -115,6 +138,22 @@ public class UmlModelParser { // build transitions for (Transition transition : region.getTransitions()) { + // for entry/exit points we need to create these outside + // of triggers as link from point to a state is most likely + // just a link and don't have any triggers. + // little unclear for now if link from points to a state should + // have trigger? + // anyway, we need to add entrys and exits to a model + if (transition.getSource() instanceof Pseudostate) { + if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.ENTRY_POINT_LITERAL) { + entrys.add(new EntryData(transition.getSource().getName(), transition.getTarget().getName())); + } else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.EXIT_POINT_LITERAL) { + exits.add(new ExitData(transition.getSource().getName(), transition.getTarget().getName())); + } + } + + // go through all triggers and create transition + // from signals for (Trigger trigger : transition.getTriggers()) { Event event = trigger.getEvent(); if (event instanceof SignalEvent) { @@ -126,7 +165,6 @@ public class UmlModelParser { } } } - } private StateData handleActions(StateData stateData, State state) { diff --git a/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java b/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java index f12d376e..90462f6f 100644 --- a/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java +++ b/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java @@ -41,6 +41,7 @@ import org.springframework.statemachine.config.builders.StateMachineModelConfigu import org.springframework.statemachine.config.model.StateData; import org.springframework.statemachine.config.model.StateMachineModel; import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.state.PseudoStateKind; public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { @@ -158,6 +159,44 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { } } + @Test + public void testSimpleEntryExit() { + context.refresh(); + Resource model1 = new ClassPathResource("org/springframework/statemachine/uml/simple-entryexit.uml"); + UmlStateMachineModelFactory builder = new UmlStateMachineModelFactory(model1); + builder.setBeanFactory(context); + assertThat(model1.exists(), is(true)); + StateMachineModel stateMachineModel = builder.build(); + assertThat(stateMachineModel, notNullValue()); + Collection> stateDatas = stateMachineModel.getStatesData().getStateData(); + assertThat(stateDatas.size(), is(8)); + for (StateData stateData : stateDatas) { + if (stateData.getState().equals("S1")) { + assertThat(stateData.isInitial(), is(true)); + } else if (stateData.getState().equals("S2")) { + assertThat(stateData.isInitial(), is(false)); + } else if (stateData.getState().equals("S21")) { + assertThat(stateData.isInitial(), is(true)); + } else if (stateData.getState().equals("S22")) { + assertThat(stateData.isInitial(), is(false)); + } else if (stateData.getState().equals("S3")) { + assertThat(stateData.isInitial(), is(false)); + } else if (stateData.getState().equals("S4")) { + assertThat(stateData.isInitial(), is(false)); + } else if (stateData.getState().equals("ENTRY")) { + assertThat(stateData.isInitial(), is(false)); + assertThat(stateData.getPseudoStateKind(), is(PseudoStateKind.ENTRY)); + } else if (stateData.getState().equals("EXIT")) { + assertThat(stateData.getPseudoStateKind(), is(PseudoStateKind.EXIT)); + assertThat(stateData.isInitial(), is(false)); + } else { + throw new IllegalArgumentException(); + } + } + assertThat(stateMachineModel.getTransitionsData().getEntrys().size(), is(1)); + assertThat(stateMachineModel.getTransitionsData().getExits().size(), is(1)); + } + @Test @SuppressWarnings("unchecked") public void testSimpleFlatMachine() throws Exception { @@ -203,6 +242,21 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2", "S4")); } + @Test + @SuppressWarnings("unchecked") + public void testSimpleEntryExitMachine() throws Exception { + context.register(Config5.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1")); + stateMachine.sendEvent("E3"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2", "S22")); + stateMachine.sendEvent("E4"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S4")); + } + @Configuration @EnableStateMachine public static class Config2 extends StateMachineConfigurerAdapter { @@ -242,11 +296,6 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { Resource model = new ClassPathResource("org/springframework/statemachine/uml/simple-submachine.uml"); return new UmlStateMachineModelFactory(model); } - - @Bean - public Action action1() { - return new LatchAction(); - } } @Configuration @@ -265,10 +314,23 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { Resource model = new ClassPathResource("org/springframework/statemachine/uml/simple-root-regions.uml"); return new UmlStateMachineModelFactory(model); } + } + + @Configuration + @EnableStateMachine + public static class Config5 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } @Bean - public Action action1() { - return new LatchAction(); + public StateMachineModelFactory modelFactory() { + Resource model = new ClassPathResource("org/springframework/statemachine/uml/simple-entryexit.uml"); + return new UmlStateMachineModelFactory(model); } } diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.di b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.notation b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.notation new file mode 100644 index 00000000..2a373bda --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.notation @@ -0,0 +1,296 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.uml b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.uml new file mode 100644 index 00000000..938e7cfe --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-entryexit.uml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +