Support initial action in uml

- Add action detection from initial state
  vertex which then translates into initial state
  initial action.
- Fixes #213
This commit is contained in:
Janne Valkealahti
2016-04-30 16:16:19 +01:00
parent b0b21a4feb
commit f7419d2c7d
6 changed files with 226 additions and 18 deletions

View File

@@ -67,7 +67,7 @@ import org.springframework.util.StringUtils;
*/
public class UmlModelParser {
private final static String LANGUAGE_BEAN = "bean";
public final static String LANGUAGE_BEAN = "bean";
private final Model model;
private final StateMachineComponentResolver<String, String> resolver;
private final Collection<StateData<String, String>> stateDatas = new ArrayList<StateData<String, String>>();
@@ -133,8 +133,13 @@ public class UmlModelParser {
if (state.getOwner() instanceof Region) {
regionId = ((Region)state.getOwner()).getName();
}
boolean isInitialState = UmlUtils.isInitialState(state);
StateData<String, String> stateData = handleActions(
new StateData<String, String>(parent, regionId, state.getName(), UmlUtils.isInitialState(state)), state);
new StateData<String, String>(parent, regionId, state.getName(), isInitialState), state);
if (isInitialState) {
// set possible initial transition
stateData.setInitialAction(resolveInitialTransitionAction(state));
}
stateData.setDeferred(UmlUtils.resolveDererredEvents(state));
if (UmlUtils.isFinalState(state)) {
stateData.setEnd(true);
@@ -272,8 +277,8 @@ public class UmlModelParser {
Signal signal = ((SignalEvent)event).getSignal();
if (signal != null) {
transitionDatas.add(new TransitionData<String, String>(transition.getSource().getName(),
transition.getTarget().getName(), signal.getName(), resolveTransitionActions(transition), guard,
UmlUtils.mapUmlTransitionType(transition)));
transition.getTarget().getName(), signal.getName(), UmlUtils.resolveTransitionActions(transition, resolver),
guard, UmlUtils.mapUmlTransitionType(transition)));
}
} else if (event instanceof TimeEvent) {
TimeEvent timeEvent = (TimeEvent)event;
@@ -284,8 +289,8 @@ public class UmlModelParser {
count = 1;
}
transitionDatas.add(new TransitionData<String, String>(transition.getSource().getName(),
transition.getTarget().getName(), period, count, resolveTransitionActions(transition), guard,
UmlUtils.mapUmlTransitionType(transition)));
transition.getTarget().getName(), period, count, UmlUtils.resolveTransitionActions(transition, resolver),
guard, UmlUtils.mapUmlTransitionType(transition)));
}
}
}
@@ -293,7 +298,8 @@ public class UmlModelParser {
// create anonymous transition if needed
if (shouldCreateAnonymousTransition(transition)) {
transitionDatas.add(new TransitionData<String, String>(transition.getSource().getName(), transition.getTarget().getName(),
null, resolveTransitionActions(transition), resolveGuard(transition), UmlUtils.mapUmlTransitionType(transition)));
null, UmlUtils.resolveTransitionActions(transition, resolver), resolveGuard(transition),
UmlUtils.mapUmlTransitionType(transition)));
}
}
}
@@ -319,16 +325,13 @@ public class UmlModelParser {
}
}
private Collection<Action<String, String>> resolveTransitionActions(Transition transition) {
ArrayList<Action<String, String>> actions = new ArrayList<Action<String, String>>();
if (transition.getEffect() instanceof OpaqueBehavior) {
String beanId = UmlUtils.resolveBodyByLanguage(LANGUAGE_BEAN, (OpaqueBehavior)transition.getEffect());
Action<String, String> bean = resolver.resolveAction(beanId);
if (bean != null) {
actions.add(bean);
}
private Action<String, String> resolveInitialTransitionAction(State state) {
Transition transition = UmlUtils.resolveInitialTransition(state);
if (transition != null) {
return UmlUtils.resolveTransitionAction(transition, resolver);
} else {
return null;
}
return actions;
}
private boolean shouldCreateAnonymousTransition(Transition transition) {

View File

@@ -27,6 +27,7 @@ import org.eclipse.uml2.uml.BodyOwner;
import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.FinalState;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.OpaqueBehavior;
import org.eclipse.uml2.uml.Pseudostate;
import org.eclipse.uml2.uml.PseudostateKind;
import org.eclipse.uml2.uml.Signal;
@@ -36,6 +37,8 @@ import org.eclipse.uml2.uml.Transition;
import org.eclipse.uml2.uml.Trigger;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.model.StateMachineComponentResolver;
import org.springframework.statemachine.transition.TransitionKind;
/**
@@ -70,14 +73,62 @@ public abstract class UmlUtils {
* @return true, if is initial state
*/
public static boolean isInitialState(State state) {
return resolveInitialTransition(state) != null;
}
/**
* Resolve initial transition from a {@link State} if it
* exists, otherwise null is returned.
*
* @param state the state
* @return the transition
*/
public static Transition resolveInitialTransition(State state) {
for (Transition t : state.getIncomings()) {
if (t.getSource() instanceof Pseudostate) {
if (((Pseudostate)t.getSource()).getKind() == PseudostateKind.INITIAL_LITERAL) {
return true;
return t;
}
}
}
return false;
return null;
}
/**
* Resolve transition actions.
*
* @param transition the transition
* @param resolver the state machine component resolver
* @return the collection of actions
*/
public static Collection<Action<String, String>> resolveTransitionActions(Transition transition,
StateMachineComponentResolver<String, String> resolver) {
ArrayList<Action<String, String>> actions = new ArrayList<Action<String, String>>();
Action<String, String> action = resolveTransitionAction(transition, resolver);
if (action != null) {
actions.add(action);
}
return actions;
}
/**
* Resolve transition action or null if no action was found.
*
* @param transition the transition
* @param resolver the state machine component resolver
* @return the action
*/
public static Action<String, String> resolveTransitionAction(Transition transition,
StateMachineComponentResolver<String, String> resolver) {
Action<String, String> action = null;
if (transition.getEffect() instanceof OpaqueBehavior) {
String beanId = UmlUtils.resolveBodyByLanguage(UmlModelParser.LANGUAGE_BEAN, (OpaqueBehavior)transition.getEffect());
Action<String, String> bean = resolver.resolveAction(beanId);
if (bean != null) {
action = bean;
}
}
return action;
}
/**

View File

@@ -526,6 +526,18 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S3"));
}
@Test
@SuppressWarnings("unchecked")
public void testInitialActions() throws Exception {
context.register(Config15.class);
context.refresh();
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
LatchAction initialAction = context.getBean("initialAction", LatchAction.class);
stateMachine.start();
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1"));
assertThat(initialAction.latch.await(1, TimeUnit.SECONDS), is(true));
}
@Configuration
@EnableStateMachine
public static class Config2 extends StateMachineConfigurerAdapter<String, String> {
@@ -811,6 +823,28 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
}
}
@Configuration
@EnableStateMachine
public static class Config15 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/initial-actions.uml");
}
@Bean
public LatchAction initialAction() {
return new LatchAction();
}
}
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,97 @@
<?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="_OwPTEQ7YEeaxyZlCCSfciw" type="PapyrusUMLStateMachineDiagram" name="StateMachine Diagram" measurementUnit="Pixel">
<children xmi:type="notation:Shape" xmi:id="_OwPTEg7YEeaxyZlCCSfciw" type="2000">
<children xmi:type="notation:DecorationNode" xmi:id="_OwPTEw7YEeaxyZlCCSfciw" type="2001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_OwPTFA7YEeaxyZlCCSfciw" width="700" height="23"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_OwPTFQ7YEeaxyZlCCSfciw" type="2002">
<children xmi:type="notation:Shape" xmi:id="_OwPTFg7YEeaxyZlCCSfciw" type="3000">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_OwPTFw7YEeaxyZlCCSfciw" source="RegionAnnotationKey">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_OwPTGA7YEeaxyZlCCSfciw" key="RegionZoneKey" value=""/>
</eAnnotations>
<children xmi:type="notation:DecorationNode" xmi:id="_OwPTGQ7YEeaxyZlCCSfciw" type="3002">
<children xmi:type="notation:Shape" xmi:id="_puV4QA7YEeaxyZlCCSfciw" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_puV4Qg7YEeaxyZlCCSfciw" type="6001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_0rUjMA7YEeaxyZlCCSfciw" width="59"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_puV4Qw7YEeaxyZlCCSfciw" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_puV4RA7YEeaxyZlCCSfciw" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_puV4RQ7YEeaxyZlCCSfciw" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_puV4Rg7YEeaxyZlCCSfciw" y="-1" width="59"/>
</children>
<element xmi:type="uml:State" href="initial-actions.uml#_puTcAA7YEeaxyZlCCSfciw"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_puV4QQ7YEeaxyZlCCSfciw" x="252" y="70" width="59"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_q7K3wA7YEeaxyZlCCSfciw" type="6000">
<children xmi:type="notation:DecorationNode" xmi:id="_q7K3wg7YEeaxyZlCCSfciw" type="6001">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_1O6YcA7YEeaxyZlCCSfciw" width="59"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_q7K3ww7YEeaxyZlCCSfciw" type="19003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_q7K3xA7YEeaxyZlCCSfciw" x="40"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_q7K3xQ7YEeaxyZlCCSfciw" type="6002">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_q7K3xg7YEeaxyZlCCSfciw" y="-1" width="59"/>
</children>
<element xmi:type="uml:State" href="initial-actions.uml#_q7IbgA7YEeaxyZlCCSfciw"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_q7K3wQ7YEeaxyZlCCSfciw" x="438" y="72" width="59"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_3jzzQA7YEeaxyZlCCSfciw" type="8000">
<children xmi:type="notation:DecorationNode" xmi:id="_3jzzQg7YEeaxyZlCCSfciw" type="8001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_3jzzQw7YEeaxyZlCCSfciw" x="25" y="3"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_3j0aUA7YEeaxyZlCCSfciw" type="8002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_3j0aUQ7YEeaxyZlCCSfciw" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="initial-actions.uml#_3jvh0A7YEeaxyZlCCSfciw"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_3jzzQQ7YEeaxyZlCCSfciw" x="16" y="80"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_OwPTGg7YEeaxyZlCCSfciw"/>
</children>
<element xmi:type="uml:Region" href="initial-actions.uml#_OwPTEA7YEeaxyZlCCSfciw"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_OwPTGw7YEeaxyZlCCSfciw" width="700" height="287"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_OwPTHA7YEeaxyZlCCSfciw" y="23" width="700" height="287"/>
</children>
<element xmi:type="uml:StateMachine" href="initial-actions.uml#_OwOsAA7YEeaxyZlCCSfciw"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_OwPTHQ7YEeaxyZlCCSfciw" x="30" y="30" width="700" height="310"/>
</children>
<styles xmi:type="notation:StringValueStyle" xmi:id="_OwPTHg7YEeaxyZlCCSfciw" name="diagram_compatibility_version" stringValue="1.1.0"/>
<styles xmi:type="notation:DiagramStyle" xmi:id="_OwPTHw7YEeaxyZlCCSfciw"/>
<styles xmi:type="style:PapyrusViewStyle" xmi:id="_OwPTIA7YEeaxyZlCCSfciw">
<owner xmi:type="uml:Model" href="initial-actions.uml#_OwM20A7YEeaxyZlCCSfciw"/>
</styles>
<element xmi:type="uml:StateMachine" href="initial-actions.uml#_OwOsAA7YEeaxyZlCCSfciw"/>
<edges xmi:type="notation:Connector" xmi:id="_12EfEA7YEeaxyZlCCSfciw" type="7000" source="_puV4QA7YEeaxyZlCCSfciw" target="_q7K3wA7YEeaxyZlCCSfciw">
<children xmi:type="notation:DecorationNode" xmi:id="_12EfEw7YEeaxyZlCCSfciw" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_12EfFA7YEeaxyZlCCSfciw"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_12EfFQ7YEeaxyZlCCSfciw" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_12EfFg7YEeaxyZlCCSfciw" x="-4" y="-12"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_12EfFw7YEeaxyZlCCSfciw" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_12EfGA7YEeaxyZlCCSfciw" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_12EfEQ7YEeaxyZlCCSfciw"/>
<element xmi:type="uml:Transition" href="initial-actions.uml#_12ANoA7YEeaxyZlCCSfciw"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_12EfEg7YEeaxyZlCCSfciw" points="[8, -1, -127, 25]$[137, -24, 2, 2]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_12PeMA7YEeaxyZlCCSfciw" id="(1.0,0.5319148936170213)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_12PeMQ7YEeaxyZlCCSfciw" id="(0.0,0.48936170212765956)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_8oM6MA7YEeaxyZlCCSfciw" type="7000" source="_3jzzQA7YEeaxyZlCCSfciw" target="_puV4QA7YEeaxyZlCCSfciw">
<children xmi:type="notation:DecorationNode" xmi:id="_8oM6Mw7YEeaxyZlCCSfciw" type="7001">
<layoutConstraint xmi:type="notation:Location" xmi:id="_8oM6NA7YEeaxyZlCCSfciw"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_8oM6NQ7YEeaxyZlCCSfciw" type="7002">
<layoutConstraint xmi:type="notation:Location" xmi:id="_8oNhQA7YEeaxyZlCCSfciw" x="-10" y="-15"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_8oNhQQ7YEeaxyZlCCSfciw" type="7003">
<layoutConstraint xmi:type="notation:Location" xmi:id="_8oNhQg7YEeaxyZlCCSfciw" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_8oM6MQ7YEeaxyZlCCSfciw"/>
<element xmi:type="uml:Transition" href="initial-actions.uml#_8oGzkA7YEeaxyZlCCSfciw"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_8oM6Mg7YEeaxyZlCCSfciw" points="[8, -1, -99, 0]$[98, -3, -9, -2]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8odY4A7YEeaxyZlCCSfciw" id="(1.0,0.4)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8odY4Q7YEeaxyZlCCSfciw" id="(0.0,0.3829787234042553)"/>
</edges>
</notation:Diagram>

View File

@@ -0,0 +1,21 @@
<?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="_OwM20A7YEeaxyZlCCSfciw" name="RootElement">
<packagedElement xmi:type="uml:StateMachine" xmi:id="_OwOsAA7YEeaxyZlCCSfciw" name="StateMachine">
<region xmi:type="uml:Region" xmi:id="_OwPTEA7YEeaxyZlCCSfciw" name="Region1">
<transition xmi:type="uml:Transition" xmi:id="_12ANoA7YEeaxyZlCCSfciw" source="_puTcAA7YEeaxyZlCCSfciw" target="_q7IbgA7YEeaxyZlCCSfciw">
<trigger xmi:type="uml:Trigger" xmi:id="_KWX6YA7ZEeaxyZlCCSfciw" event="_v2ErgA7YEeaxyZlCCSfciw"/>
</transition>
<transition xmi:type="uml:Transition" xmi:id="_8oGzkA7YEeaxyZlCCSfciw" source="_3jvh0A7YEeaxyZlCCSfciw" target="_puTcAA7YEeaxyZlCCSfciw">
<effect xmi:type="uml:OpaqueBehavior" xmi:id="_ZWIeQA7ZEeaxyZlCCSfciw" name="initialAction">
<language>bean</language>
<body>initialAction</body>
</effect>
</transition>
<subvertex xmi:type="uml:State" xmi:id="_puTcAA7YEeaxyZlCCSfciw" name="S1"/>
<subvertex xmi:type="uml:State" xmi:id="_q7IbgA7YEeaxyZlCCSfciw" name="S2"/>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_3jvh0A7YEeaxyZlCCSfciw"/>
</region>
</packagedElement>
<packagedElement xmi:type="uml:Signal" xmi:id="_uJ9H8A7YEeaxyZlCCSfciw" name="E1"/>
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_v2ErgA7YEeaxyZlCCSfciw" name="SignalEventE1" signal="_uJ9H8A7YEeaxyZlCCSfciw"/>
</uml:Model>