|
|
|
|
@@ -0,0 +1,180 @@
|
|
|
|
|
package click.kamil.springstatemachineexporter.springbased;
|
|
|
|
|
|
|
|
|
|
import click.kamil.examples.statemachine.simple.OrderEvents;
|
|
|
|
|
import click.kamil.examples.statemachine.simple.OrderStates;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
|
import org.springframework.boot.ApplicationArguments;
|
|
|
|
|
import org.springframework.boot.ApplicationRunner;
|
|
|
|
|
import org.springframework.statemachine.StateMachine;
|
|
|
|
|
import org.springframework.statemachine.config.StateMachineFactory;
|
|
|
|
|
import org.springframework.statemachine.state.PseudoStateKind;
|
|
|
|
|
import org.springframework.statemachine.state.State;
|
|
|
|
|
import org.springframework.statemachine.transition.Transition;
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
import org.w3c.dom.Document;
|
|
|
|
|
import org.w3c.dom.Element;
|
|
|
|
|
|
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
|
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
|
|
import javax.xml.transform.Transformer;
|
|
|
|
|
import javax.xml.transform.TransformerFactory;
|
|
|
|
|
import javax.xml.transform.dom.DOMSource;
|
|
|
|
|
import javax.xml.transform.stream.StreamResult;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
|
|
|
|
|
|
public class SpringExporter {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
@Slf4j
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
class Runner implements ApplicationRunner {
|
|
|
|
|
|
|
|
|
|
private final DotExporter dotExporter;
|
|
|
|
|
private final PlantUmlExporter plantUmlExporter;
|
|
|
|
|
private final ScxmlExporter scxmlExporter;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run(ApplicationArguments args) throws Exception {
|
|
|
|
|
dotExporter.export("stateMachine.dot");
|
|
|
|
|
plantUmlExporter.export("stateMachine.uml.dot");
|
|
|
|
|
scxmlExporter.export("stateMachine.scxml.xml");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abstract class BaseExporter {
|
|
|
|
|
|
|
|
|
|
protected final StateMachineFactory<OrderStates, OrderEvents> factory;
|
|
|
|
|
|
|
|
|
|
protected BaseExporter(@Qualifier("simpleEnumStateMachineFactory") StateMachineFactory<OrderStates, OrderEvents> factory) {
|
|
|
|
|
this.factory = factory;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abstract void export(String name) throws Exception;
|
|
|
|
|
|
|
|
|
|
protected StateMachine<OrderStates, OrderEvents> createStateMachine() {
|
|
|
|
|
return factory.getStateMachine("order");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
class DotExporter extends BaseExporter {
|
|
|
|
|
|
|
|
|
|
public DotExporter(@Qualifier("simpleEnumStateMachineFactory") StateMachineFactory<OrderStates, OrderEvents> factory) {
|
|
|
|
|
super(factory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void export(String filename) throws Exception {
|
|
|
|
|
try (PrintWriter writer = new PrintWriter(filename)) {
|
|
|
|
|
StateMachine<OrderStates, OrderEvents> machine = createStateMachine();
|
|
|
|
|
writer.println("digraph stateMachine {");
|
|
|
|
|
|
|
|
|
|
for (State<OrderStates, OrderEvents> state : machine.getStates()) {
|
|
|
|
|
writer.printf(" %s;%n", state.getId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Transition<OrderStates, OrderEvents> transition : machine.getTransitions()) {
|
|
|
|
|
if (transition.getSource() != null && transition.getTarget() != null) {
|
|
|
|
|
writer.printf(" %s -> %s [label=\"%s\"];%n",
|
|
|
|
|
transition.getSource().getId(),
|
|
|
|
|
transition.getTarget().getId(),
|
|
|
|
|
transition.getTrigger() != null ? transition.getTrigger().getEvent() : "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.println("}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
class PlantUmlExporter extends BaseExporter {
|
|
|
|
|
|
|
|
|
|
public PlantUmlExporter(@Qualifier("simpleEnumStateMachineFactory") StateMachineFactory<OrderStates, OrderEvents> factory) {
|
|
|
|
|
super(factory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void export(String filename) throws Exception {
|
|
|
|
|
try (PrintWriter writer = new PrintWriter(filename)) {
|
|
|
|
|
StateMachine<OrderStates, OrderEvents> machine = createStateMachine();
|
|
|
|
|
writer.println("@startuml");
|
|
|
|
|
writer.println("[*] --> " + machine.getInitialState().getId());
|
|
|
|
|
|
|
|
|
|
for (Transition<OrderStates, OrderEvents> transition : machine.getTransitions()) {
|
|
|
|
|
if (transition.getSource() != null && transition.getTarget() != null) {
|
|
|
|
|
String source = transition.getSource().getId().toString();
|
|
|
|
|
String target = transition.getTarget().getId().toString();
|
|
|
|
|
String label = "";
|
|
|
|
|
|
|
|
|
|
if (transition.getTrigger() != null && transition.getTrigger().getEvent() != null) {
|
|
|
|
|
label += transition.getTrigger().getEvent().toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (transition.getGuard() != null) {
|
|
|
|
|
if (!label.isEmpty()) label += " ";
|
|
|
|
|
label += "[guard]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.printf("%s --> %s%s%n", source, target, !label.isEmpty() ? " : " + label : "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (State<OrderStates, OrderEvents> state : machine.getStates()) {
|
|
|
|
|
if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.END) {
|
|
|
|
|
writer.printf("%s --> [*]%n", state.getId());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.println("@enduml");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
class ScxmlExporter extends BaseExporter {
|
|
|
|
|
|
|
|
|
|
public ScxmlExporter(@Qualifier("simpleEnumStateMachineFactory") StateMachineFactory<OrderStates, OrderEvents> factory) {
|
|
|
|
|
super(factory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void export(String filename) throws Exception {
|
|
|
|
|
StateMachine<OrderStates, OrderEvents> machine = createStateMachine();
|
|
|
|
|
|
|
|
|
|
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
|
|
|
|
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
|
|
|
|
Document doc = dBuilder.newDocument();
|
|
|
|
|
|
|
|
|
|
Element scxml = doc.createElement("scxml");
|
|
|
|
|
scxml.setAttribute("version", "1.0");
|
|
|
|
|
doc.appendChild(scxml);
|
|
|
|
|
|
|
|
|
|
Element statesElement = doc.createElement("states");
|
|
|
|
|
scxml.appendChild(statesElement);
|
|
|
|
|
|
|
|
|
|
for (State<OrderStates, OrderEvents> state : machine.getStates()) {
|
|
|
|
|
Element stateEl = doc.createElement("state");
|
|
|
|
|
stateEl.setAttribute("id", state.getId().toString());
|
|
|
|
|
statesElement.appendChild(stateEl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Element transitionsElement = doc.createElement("transitions");
|
|
|
|
|
scxml.appendChild(transitionsElement);
|
|
|
|
|
|
|
|
|
|
for (Transition<OrderStates, OrderEvents> transition : machine.getTransitions()) {
|
|
|
|
|
Element t = doc.createElement("transition");
|
|
|
|
|
t.setAttribute("from", transition.getSource().getId().toString());
|
|
|
|
|
t.setAttribute("to", transition.getTarget().getId().toString());
|
|
|
|
|
t.setAttribute("event", String.valueOf(transition.getTrigger()));
|
|
|
|
|
transitionsElement.appendChild(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
|
|
|
|
transformer.transform(new DOMSource(doc), new StreamResult(new File(filename)));
|
|
|
|
|
}
|
|
|
|
|
}
|