From 7938086c4635391148b8ddb2c1759d876535486a Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sat, 16 Apr 2016 14:50:31 +0100 Subject: [PATCH] Add fork and join state support for uml model - Also add creation of an anonymous transition - Relates to #193 --- .../statemachine/uml/UmlModelParser.java | 62 ++- .../uml/UmlStateMachineModelFactoryTests.java | 34 ++ .../statemachine/uml/simple-forkjoin.di | 2 + .../statemachine/uml/simple-forkjoin.notation | 356 ++++++++++++++++++ .../statemachine/uml/simple-forkjoin.uml | 47 +++ 5 files changed, 499 insertions(+), 2 deletions(-) create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.di create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.notation create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.uml 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 8097e686..a5b63aea 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 @@ -53,6 +53,7 @@ import org.springframework.statemachine.config.model.TransitionsData; import org.springframework.statemachine.guard.Guard; import org.springframework.statemachine.state.PseudoStateKind; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Model parser which constructs states and transitions data out from @@ -69,6 +70,8 @@ public class UmlModelParser { private final Collection> entrys = new ArrayList>(); private final Collection> exits = new ArrayList>(); private final Map>> choices = new HashMap>>(); + private final Map> forks = new HashMap>(); + private final Map> joins = new HashMap>(); /** * Instantiates a new uml model parser. @@ -100,9 +103,10 @@ public class UmlModelParser { handleRegion(region); } // LinkedList can be passed due to generics, need to copy - HashMap>> choicesCopy = new HashMap>>(); + HashMap>> choicesCopy = new HashMap>>(); choicesCopy.putAll(choices); - return new DataHolder(new StatesData<>(stateDatas), new TransitionsData(transitionDatas, choicesCopy, null, null, entrys, exits)); + return new DataHolder(new StatesData<>(stateDatas), + new TransitionsData(transitionDatas, choicesCopy, forks, joins, entrys, exits)); } private void handleRegion(Region region) { @@ -162,6 +166,14 @@ public class UmlModelParser { StateData cpStateData = new StateData<>(parent, regionId, state.getName(), false); cpStateData.setPseudoStateKind(PseudoStateKind.CHOICE); stateDatas.add(cpStateData); + } else if (state.getKind() == PseudostateKind.FORK_LITERAL) { + StateData cpStateData = new StateData<>(parent, regionId, state.getName(), false); + cpStateData.setPseudoStateKind(PseudoStateKind.FORK); + stateDatas.add(cpStateData); + } else if (state.getKind() == PseudostateKind.JOIN_LITERAL) { + StateData cpStateData = new StateData<>(parent, regionId, state.getName(), false); + cpStateData.setPseudoStateKind(PseudoStateKind.JOIN); + stateDatas.add(cpStateData); } } } @@ -200,6 +212,23 @@ public class UmlModelParser { } else { list.addFirst(new ChoiceData(transition.getSource().getName(), transition.getTarget().getName(), guard)); } + } else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.FORK_LITERAL) { + List list = forks.get(transition.getSource().getName()); + if (list == null) { + list = new ArrayList(); + forks.put(transition.getSource().getName(), list); + } + list.add(transition.getTarget().getName()); + } + } + if (transition.getTarget() instanceof Pseudostate) { + if (((Pseudostate)transition.getTarget()).getKind() == PseudostateKind.JOIN_LITERAL) { + List list = joins.get(transition.getTarget().getName()); + if (list == null) { + list = new ArrayList(); + joins.put(transition.getTarget().getName(), list); + } + list.add(transition.getSource().getName()); } } @@ -215,9 +244,38 @@ public class UmlModelParser { } } } + + // create anonymous transition if needed + if (shouldCreateAnonymousTransition(transition)) { + transitionDatas.add(new TransitionData(transition.getSource().getName(), + transition.getTarget().getName(), null)); + } } } + private boolean shouldCreateAnonymousTransition(Transition transition) { + if (!transition.getTriggers().isEmpty()) { + return false; + } + if (!StringUtils.hasText(transition.getSource().getName())) { + return false; + } + if (!StringUtils.hasText(transition.getTarget().getName())) { + return false; + } + if (transition.getSource() instanceof Pseudostate) { + if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.FORK_LITERAL) { + return false; + } + } + if (transition.getTarget() instanceof Pseudostate) { + if (((Pseudostate)transition.getTarget()).getKind() == PseudostateKind.JOIN_LITERAL) { + return false; + } + } + return true; + } + private StateData handleActions(StateData stateData, State state) { if (state.getEntry() instanceof Activity) { String beanId = ((Activity)state.getEntry()).getName(); 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 dff14afa..4e05d09f 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 @@ -296,6 +296,22 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S4")); } + @Test + @SuppressWarnings("unchecked") + public void testSimpleForkJoin() { + context.register(Config7.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("SI")); + stateMachine.sendEvent("E1"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2", "S20", "S30")); + stateMachine.sendEvent("E2"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2", "S21", "S30")); + stateMachine.sendEvent("E3"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("SF")); + } + @Configuration @EnableStateMachine public static class Config2 extends StateMachineConfigurerAdapter { @@ -401,6 +417,24 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { } } + @Configuration + @EnableStateMachine + public static class Config7 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + Resource model = new ClassPathResource("org/springframework/statemachine/uml/simple-forkjoin.uml"); + return new UmlStateMachineModelFactory(model); + } + } + public static class LatchAction implements Action { CountDownLatch latch = new CountDownLatch(1); @Override diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.di b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.notation b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.notation new file mode 100644 index 00000000..ee263592 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.notation @@ -0,0 +1,356 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.uml b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.uml new file mode 100644 index 00000000..efaed7d2 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-forkjoin.uml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +