From a4a4a28faf98a56af6d9eaeb05adf9a8a0493012 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sun, 17 Apr 2016 15:24:40 +0100 Subject: [PATCH] Support FunctionBehavior for actions - Fixes #199 --- .../config/model/TransitionData.java | 15 +++ .../statemachine/uml/UmlModelParser.java | 48 ++++++- .../uml/UmlStateMachineModelFactoryTests.java | 51 ++++++++ .../statemachine/uml/simple-actions.di | 2 + .../statemachine/uml/simple-actions.notation | 117 ++++++++++++++++++ .../statemachine/uml/simple-actions.uml | 30 +++++ 6 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.di create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.notation create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.uml diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java index d181ac0e..e0dcef9c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java @@ -51,6 +51,21 @@ public class TransitionData { this(source, target, null, event, null, null, null, null, TransitionKind.EXTERNAL, null); } + /** + * Instantiates a new transition data. + * + * @param source the source + * @param target the target + * @param event the event + * @param actions the actions + * @param guard the guard + * @param kind the kind + */ + public TransitionData(S source, S target, E event, Collection> actions, + Guard guard, TransitionKind kind) { + this(source, target, null, event, null, null, actions, guard, TransitionKind.EXTERNAL, null); + } + /** * Instantiates a new transition data. * 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 ffab7a98..824dedab 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 @@ -25,9 +25,11 @@ import java.util.Map; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.uml2.uml.Activity; +import org.eclipse.uml2.uml.BodyOwner; import org.eclipse.uml2.uml.Constraint; import org.eclipse.uml2.uml.Event; import org.eclipse.uml2.uml.Model; +import org.eclipse.uml2.uml.OpaqueBehavior; import org.eclipse.uml2.uml.OpaqueExpression; import org.eclipse.uml2.uml.PackageableElement; import org.eclipse.uml2.uml.Pseudostate; @@ -277,7 +279,7 @@ public class UmlModelParser { Signal signal = ((SignalEvent)event).getSignal(); if (signal != null) { transitionDatas.add(new TransitionData(transition.getSource().getName(), - transition.getTarget().getName(), signal.getName())); + transition.getTarget().getName(), signal.getName(), resolveTransitionActions(transition), null, null)); } } } @@ -285,11 +287,23 @@ public class UmlModelParser { // create anonymous transition if needed if (shouldCreateAnonymousTransition(transition)) { transitionDatas.add(new TransitionData(transition.getSource().getName(), - transition.getTarget().getName(), null)); + transition.getTarget().getName(), null, resolveTransitionActions(transition), null, null)); } } } + private Collection> resolveTransitionActions(Transition transition) { + ArrayList> actions = new ArrayList>(); + if (transition.getEffect() instanceof OpaqueBehavior) { + String beanId = resolveBodyLanguage("bean", (OpaqueBehavior)transition.getEffect()); + Action bean = resolver.resolveAction(beanId); + if (bean != null) { + actions.add(bean); + } + } + return actions; + } + private boolean shouldCreateAnonymousTransition(Transition transition) { if (!transition.getTriggers().isEmpty()) { return false; @@ -314,6 +328,28 @@ public class UmlModelParser { } private StateData handleActions(StateData stateData, State state) { + if (state.getEntry() instanceof OpaqueBehavior) { + String beanId = resolveBodyLanguage("bean", (OpaqueBehavior)state.getEntry()); + if (StringUtils.hasText(beanId)) { + Action bean = resolver.resolveAction(beanId); + if (bean != null) { + ArrayList> entrys = new ArrayList>(); + entrys.add(bean); + stateData.setEntryActions(entrys); + } + } + } + if (state.getExit() instanceof OpaqueBehavior) { + String beanId = resolveBodyLanguage("bean", (OpaqueBehavior)state.getExit()); + if (StringUtils.hasText(beanId)) { + Action bean = resolver.resolveAction(beanId); + if (bean != null) { + ArrayList> entrys = new ArrayList>(); + entrys.add(bean); + stateData.setExitActions(entrys); + } + } + } if (state.getEntry() instanceof Activity) { String beanId = ((Activity)state.getEntry()).getName(); Action bean = resolver.resolveAction(beanId); @@ -335,6 +371,14 @@ public class UmlModelParser { return stateData; } + private static String resolveBodyLanguage(String language, BodyOwner owner) { + try { + return owner.getBodies().get(owner.getLanguages().indexOf(language)); + } catch (Exception e) { + return null; + } + } + /** * Holder object for results returned from uml parser. */ 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 5c694a0f..00e49b5f 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 @@ -390,6 +390,24 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S7")); } + @Test + @SuppressWarnings("unchecked") + public void testSimpleActions() throws Exception { + context.register(Config11.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + LatchAction e1Action = context.getBean("e1Action", LatchAction.class); + LatchAction s1Exit = context.getBean("s1Exit", LatchAction.class); + LatchAction s2Entry = context.getBean("s2Entry", LatchAction.class); + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1")); + stateMachine.sendEvent("E1"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2")); + assertThat(e1Action.latch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(s1Exit.latch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(s2Entry.latch.await(1, TimeUnit.SECONDS), is(true)); + } + @Configuration @EnableStateMachine public static class Config2 extends StateMachineConfigurerAdapter { @@ -577,6 +595,39 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { } } + @Configuration + @EnableStateMachine + public static class Config11 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-actions.uml"); + return new UmlStateMachineModelFactory(model); + } + + @Bean + public LatchAction s1Exit() { + return new LatchAction(); + } + + @Bean + public LatchAction s2Entry() { + return new LatchAction(); + } + + @Bean + public LatchAction e1Action() { + return new LatchAction(); + } + } + 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-actions.di b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.notation b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.notation new file mode 100644 index 00000000..cb1fedae --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.notation @@ -0,0 +1,117 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.uml b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.uml new file mode 100644 index 00000000..24c35e40 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-actions.uml @@ -0,0 +1,30 @@ + + + + + + + bean + e1Action + + + + + + + bean + s1Exit + + + + + bean + s2Entry + + + + + + + +