diff --git a/build.gradle b/build.gradle index 5a0ab8a..dfe6e5b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'io.spring.dependency-management' version '1.1.7' } -group = 'com.example' +group = 'click.kamil' version = '0.0.1-SNAPSHOT' java { diff --git a/src/main/java/com/example/statemachinedemo/StatemachinedemoApplication.java b/src/main/java/click/kamil/springstatemachineexporter/StatemachinedemoApplication.java similarity index 86% rename from src/main/java/com/example/statemachinedemo/StatemachinedemoApplication.java rename to src/main/java/click/kamil/springstatemachineexporter/StatemachinedemoApplication.java index 1690bd1..d578580 100644 --- a/src/main/java/com/example/statemachinedemo/StatemachinedemoApplication.java +++ b/src/main/java/click/kamil/springstatemachineexporter/StatemachinedemoApplication.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo; +package click.kamil.springstatemachineexporter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/com/example/statemachinedemo/Main.java b/src/main/java/click/kamil/springstatemachineexporter/ast/Main.java similarity index 79% rename from src/main/java/com/example/statemachinedemo/Main.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/Main.java index 0898f1d..16d5a60 100644 --- a/src/main/java/com/example/statemachinedemo/Main.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/Main.java @@ -1,16 +1,16 @@ -package com.example.statemachinedemo; +package click.kamil.springstatemachineexporter.ast; -import com.example.statemachinedemo.app.AstTransitionParser; -import com.example.statemachinedemo.app.StateMachineStateConfigurationMethodFinder; -import com.example.statemachinedemo.app.StateMachineTransitionConfigurationMethodFinder; -import com.example.statemachinedemo.app.TransitionStateUtils; -import com.example.statemachinedemo.app.domain.Transition; -import com.example.statemachinedemo.common.FileUtils; -import com.example.statemachinedemo.in.AstFileFinder; -import com.example.statemachinedemo.out.Dot; -import com.example.statemachinedemo.out.PlantUml; -import com.example.statemachinedemo.out.Scxml; -import com.example.statemachinedemo.out.StateMachineExporter; +import click.kamil.springstatemachineexporter.ast.app.AstTransitionParser; +import click.kamil.springstatemachineexporter.ast.app.StateMachineStateConfigurationMethodFinder; +import click.kamil.springstatemachineexporter.ast.app.StateMachineTransitionConfigurationMethodFinder; +import click.kamil.springstatemachineexporter.ast.app.TransitionStateUtils; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.common.FileUtils; +import click.kamil.springstatemachineexporter.ast.in.AstFileFinder; +import click.kamil.springstatemachineexporter.ast.out.Dot; +import click.kamil.springstatemachineexporter.ast.out.PlantUml; +import click.kamil.springstatemachineexporter.ast.out.Scxml; +import click.kamil.springstatemachineexporter.ast.out.StateMachineExporter; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MethodDeclaration; diff --git a/src/main/java/com/example/statemachinedemo/app/AstTransitionParser.java b/src/main/java/click/kamil/springstatemachineexporter/ast/app/AstTransitionParser.java similarity index 97% rename from src/main/java/com/example/statemachinedemo/app/AstTransitionParser.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/app/AstTransitionParser.java index d4951b2..fea1120 100644 --- a/src/main/java/com/example/statemachinedemo/app/AstTransitionParser.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/app/AstTransitionParser.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.app; +package click.kamil.springstatemachineexporter.ast.app; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import org.eclipse.jdt.core.dom.*; import java.util.ArrayList; diff --git a/src/main/java/com/example/statemachinedemo/app/StateMachineStateConfigurationMethodFinder.java b/src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineStateConfigurationMethodFinder.java similarity index 98% rename from src/main/java/com/example/statemachinedemo/app/StateMachineStateConfigurationMethodFinder.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineStateConfigurationMethodFinder.java index 1f36a61..7ffcbb5 100644 --- a/src/main/java/com/example/statemachinedemo/app/StateMachineStateConfigurationMethodFinder.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineStateConfigurationMethodFinder.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.app; +package click.kamil.springstatemachineexporter.ast.app; import lombok.Getter; import org.eclipse.jdt.core.dom.*; diff --git a/src/main/java/com/example/statemachinedemo/app/StateMachineTransitionConfigurationMethodFinder.java b/src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineTransitionConfigurationMethodFinder.java similarity index 98% rename from src/main/java/com/example/statemachinedemo/app/StateMachineTransitionConfigurationMethodFinder.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineTransitionConfigurationMethodFinder.java index 5424a3d..eb53942 100644 --- a/src/main/java/com/example/statemachinedemo/app/StateMachineTransitionConfigurationMethodFinder.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/app/StateMachineTransitionConfigurationMethodFinder.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.app; +package click.kamil.springstatemachineexporter.ast.app; import lombok.Getter; import org.eclipse.jdt.core.dom.*; diff --git a/src/main/java/com/example/statemachinedemo/app/TransitionStateUtils.java b/src/main/java/click/kamil/springstatemachineexporter/ast/app/TransitionStateUtils.java similarity index 92% rename from src/main/java/com/example/statemachinedemo/app/TransitionStateUtils.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/app/TransitionStateUtils.java index a8a6294..aad8ff8 100644 --- a/src/main/java/com/example/statemachinedemo/app/TransitionStateUtils.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/app/TransitionStateUtils.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.app; +package click.kamil.springstatemachineexporter.ast.app; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import java.util.Collection; import java.util.Objects; diff --git a/src/main/java/com/example/statemachinedemo/app/domain/Transition.java b/src/main/java/click/kamil/springstatemachineexporter/ast/app/domain/Transition.java similarity index 91% rename from src/main/java/com/example/statemachinedemo/app/domain/Transition.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/app/domain/Transition.java index 317c7bf..c2eacb1 100644 --- a/src/main/java/com/example/statemachinedemo/app/domain/Transition.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/app/domain/Transition.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.app.domain; +package click.kamil.springstatemachineexporter.ast.app.domain; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/example/statemachinedemo/common/FileUtils.java b/src/main/java/click/kamil/springstatemachineexporter/ast/common/FileUtils.java similarity index 90% rename from src/main/java/com/example/statemachinedemo/common/FileUtils.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/common/FileUtils.java index 397e2d9..b62e6a8 100644 --- a/src/main/java/com/example/statemachinedemo/common/FileUtils.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/common/FileUtils.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.common; +package click.kamil.springstatemachineexporter.ast.common; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/com/example/statemachinedemo/in/AstFileFinder.java b/src/main/java/click/kamil/springstatemachineexporter/ast/in/AstFileFinder.java similarity index 95% rename from src/main/java/com/example/statemachinedemo/in/AstFileFinder.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/in/AstFileFinder.java index 0f442fe..acb627e 100644 --- a/src/main/java/com/example/statemachinedemo/in/AstFileFinder.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/in/AstFileFinder.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.in; +package click.kamil.springstatemachineexporter.ast.in; import org.eclipse.jdt.core.dom.*; diff --git a/src/main/java/com/example/statemachinedemo/out/Dot.java b/src/main/java/click/kamil/springstatemachineexporter/ast/out/Dot.java similarity index 98% rename from src/main/java/com/example/statemachinedemo/out/Dot.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/out/Dot.java index 494b5a8..ee32c12 100644 --- a/src/main/java/com/example/statemachinedemo/out/Dot.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/out/Dot.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.out; +package click.kamil.springstatemachineexporter.ast.out; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/com/example/statemachinedemo/out/PlantUml.java b/src/main/java/click/kamil/springstatemachineexporter/ast/out/PlantUml.java similarity index 95% rename from src/main/java/com/example/statemachinedemo/out/PlantUml.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/out/PlantUml.java index 066938f..c9fd857 100644 --- a/src/main/java/com/example/statemachinedemo/out/PlantUml.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/out/PlantUml.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.out; +package click.kamil.springstatemachineexporter.ast.out; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import io.micrometer.common.util.StringUtils; import java.util.*; @@ -8,6 +8,20 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; public class PlantUml implements StateMachineExporter { + // Color palette for choices + private final List choiceColors = List.of("FF6347", "4682B4", "32CD32", "FFD700", "6A5ACD", "FF69B4"); + private String getColor(Transition t, String source, Map choiceStateColorMap) { + return switch (t.getType()) { + case "withChoice" -> choiceStateColorMap.getOrDefault(source, "000000"); + case "withExternal" -> "1E90FF"; + case "withInternal" -> "32CD32"; + case "withLocal" -> "FFA500"; + case "withJunction" -> "FF69B4"; + case "withJoin" -> "8A2BE2"; + case "withFork" -> "20B2AA"; + default -> "000000"; + }; + } @Override public String export(List transitions, @@ -44,9 +58,6 @@ public class PlantUml implements StateMachineExporter { } sb.append("\n"); - // Color palette for choices - List choiceColors = List.of("FF6347", "4682B4", "32CD32", "FFD700", "6A5ACD", "FF69B4"); - // Assign color to each choice state Map choiceStateColorMap = new HashMap<>(); int colorIndex = 0; @@ -168,17 +179,4 @@ public class PlantUml implements StateMachineExporter { return String.join(" ", parts); } - - private String getColor(Transition t, String source, Map choiceStateColorMap) { - return switch (t.getType()) { - case "withChoice" -> choiceStateColorMap.getOrDefault(source, "000000"); - case "withExternal" -> "1E90FF"; - case "withInternal" -> "32CD32"; - case "withLocal" -> "FFA500"; - case "withJunction" -> "FF69B4"; - case "withJoin" -> "8A2BE2"; - case "withFork" -> "20B2AA"; - default -> "000000"; - }; - } } diff --git a/src/main/java/com/example/statemachinedemo/out/Scxml.java b/src/main/java/click/kamil/springstatemachineexporter/ast/out/Scxml.java similarity index 96% rename from src/main/java/com/example/statemachinedemo/out/Scxml.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/out/Scxml.java index 3a4b10f..3920878 100644 --- a/src/main/java/com/example/statemachinedemo/out/Scxml.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/out/Scxml.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.out; +package click.kamil.springstatemachineexporter.ast.out; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/com/example/statemachinedemo/out/StateMachineExporter.java b/src/main/java/click/kamil/springstatemachineexporter/ast/out/StateMachineExporter.java similarity index 81% rename from src/main/java/com/example/statemachinedemo/out/StateMachineExporter.java rename to src/main/java/click/kamil/springstatemachineexporter/ast/out/StateMachineExporter.java index 3ee15d1..615fbf9 100644 --- a/src/main/java/com/example/statemachinedemo/out/StateMachineExporter.java +++ b/src/main/java/click/kamil/springstatemachineexporter/ast/out/StateMachineExporter.java @@ -1,6 +1,6 @@ -package com.example.statemachinedemo.out; +package click.kamil.springstatemachineexporter.ast.out; -import com.example.statemachinedemo.app.domain.Transition; +import click.kamil.springstatemachineexporter.ast.app.domain.Transition; import java.util.List; import java.util.Set; diff --git a/src/main/java/click/kamil/springstatemachineexporter/springbased/SpringExporter.java b/src/main/java/click/kamil/springstatemachineexporter/springbased/SpringExporter.java new file mode 100644 index 0000000..72663a6 --- /dev/null +++ b/src/main/java/click/kamil/springstatemachineexporter/springbased/SpringExporter.java @@ -0,0 +1,180 @@ +package click.kamil.springstatemachineexporter.springbased; + +import click.kamil.springstatemachineexporter.statemachine.OrderEvents; +import click.kamil.springstatemachineexporter.statemachine.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 { + @Qualifier("simpleEnumStateMachineFactory") + protected final StateMachineFactory factory; + + protected BaseExporter(StateMachineFactory factory) { + this.factory = factory; + } + + abstract void export(String name) throws Exception; + + protected StateMachine createStateMachine() { + return factory.getStateMachine("order"); + } +} + +@Component +class DotExporter extends BaseExporter { + + public DotExporter(@Qualifier("simpleEnumStateMachineFactory") StateMachineFactory factory) { + super(factory); + } + + @Override + public void export(String filename) throws Exception { + try (PrintWriter writer = new PrintWriter(filename)) { + StateMachine machine = createStateMachine(); + writer.println("digraph stateMachine {"); + + for (State state : machine.getStates()) { + writer.printf(" %s;%n", state.getId()); + } + + for (Transition 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 factory) { + super(factory); + } + + @Override + public void export(String filename) throws Exception { + try (PrintWriter writer = new PrintWriter(filename)) { + StateMachine machine = createStateMachine(); + writer.println("@startuml"); + writer.println("[*] --> " + machine.getInitialState().getId()); + + for (Transition 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 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 factory) { + super(factory); + } + + @Override + public void export(String filename) throws Exception { + StateMachine 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 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 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))); + } +} diff --git a/src/main/java/com/example/statemachinedemo/z/statemachine/ComplexStateMachineConfig.java b/src/main/java/click/kamil/springstatemachineexporter/statemachine/ComplexStateMachineConfig.java similarity index 99% rename from src/main/java/com/example/statemachinedemo/z/statemachine/ComplexStateMachineConfig.java rename to src/main/java/click/kamil/springstatemachineexporter/statemachine/ComplexStateMachineConfig.java index 8515819..1f17caf 100644 --- a/src/main/java/com/example/statemachinedemo/z/statemachine/ComplexStateMachineConfig.java +++ b/src/main/java/click/kamil/springstatemachineexporter/statemachine/ComplexStateMachineConfig.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.z.statemachine; +package click.kamil.springstatemachineexporter.statemachine; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.action.Action; diff --git a/src/main/java/com/example/statemachinedemo/z/statemachine/ForkJoinStateMachineConfig.java b/src/main/java/click/kamil/springstatemachineexporter/statemachine/ForkJoinStateMachineConfig.java similarity index 98% rename from src/main/java/com/example/statemachinedemo/z/statemachine/ForkJoinStateMachineConfig.java rename to src/main/java/click/kamil/springstatemachineexporter/statemachine/ForkJoinStateMachineConfig.java index bd027f8..374390b 100644 --- a/src/main/java/com/example/statemachinedemo/z/statemachine/ForkJoinStateMachineConfig.java +++ b/src/main/java/click/kamil/springstatemachineexporter/statemachine/ForkJoinStateMachineConfig.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.z.statemachine; +package click.kamil.springstatemachineexporter.statemachine; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.action.Action; diff --git a/src/main/java/com/example/statemachinedemo/z/statemachine/OrderEvents.java b/src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderEvents.java similarity index 56% rename from src/main/java/com/example/statemachinedemo/z/statemachine/OrderEvents.java rename to src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderEvents.java index 60f7402..c2e7f89 100644 --- a/src/main/java/com/example/statemachinedemo/z/statemachine/OrderEvents.java +++ b/src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderEvents.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.z.statemachine; +package click.kamil.springstatemachineexporter.statemachine; public enum OrderEvents { FULFILL, PAY, CANCEL, diff --git a/src/main/java/com/example/statemachinedemo/z/statemachine/OrderStates.java b/src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderStates.java similarity index 67% rename from src/main/java/com/example/statemachinedemo/z/statemachine/OrderStates.java rename to src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderStates.java index fc78ddf..845a2b6 100644 --- a/src/main/java/com/example/statemachinedemo/z/statemachine/OrderStates.java +++ b/src/main/java/click/kamil/springstatemachineexporter/statemachine/OrderStates.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.z.statemachine; +package click.kamil.springstatemachineexporter.statemachine; public enum OrderStates { SUBMITTED, PAID, FULFILLED, CANCELED diff --git a/src/main/java/com/example/statemachinedemo/z/statemachine/SimpleEnumStateMachineConfiguration.java b/src/main/java/click/kamil/springstatemachineexporter/statemachine/SimpleEnumStateMachineConfiguration.java similarity index 98% rename from src/main/java/com/example/statemachinedemo/z/statemachine/SimpleEnumStateMachineConfiguration.java rename to src/main/java/click/kamil/springstatemachineexporter/statemachine/SimpleEnumStateMachineConfiguration.java index f90df8e..3f827cd 100644 --- a/src/main/java/com/example/statemachinedemo/z/statemachine/SimpleEnumStateMachineConfiguration.java +++ b/src/main/java/click/kamil/springstatemachineexporter/statemachine/SimpleEnumStateMachineConfiguration.java @@ -1,4 +1,4 @@ -package com.example.statemachinedemo.z.statemachine; +package click.kamil.springstatemachineexporter.statemachine; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/example/statemachinedemo/z/notgoodspringbased/SpringExporter.java b/src/main/java/com/example/statemachinedemo/z/notgoodspringbased/SpringExporter.java deleted file mode 100644 index 978dc9c..0000000 --- a/src/main/java/com/example/statemachinedemo/z/notgoodspringbased/SpringExporter.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.example.statemachinedemo.z.notgoodspringbased; - -import com.example.statemachinedemo.z.statemachine.OrderEvents; -import com.example.statemachinedemo.z.statemachine.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.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; - -public class SpringExporter { -} - - -@Component -@Slf4j -@RequiredArgsConstructor -class Runner implements ApplicationRunner { -// private final StateMachineFactory simpleEnumStateMachineConfiguration; -// @Override -// public void run(ApplicationArguments args) throws Exception { -// StateMachine stateMachine = factory.getStateMachine("order"); -// stateMachine.start(); -// log.info("Current state is {}", stateMachine.getState().getId()); -// -// } - private final Config1 c; - private final Config2 c2; - private final Config3 c3; - - @Override - public void run(ApplicationArguments args) throws Exception { - c.a(); - c2.a(); - c3.exportToSCXML(); - } -} - - -class StateMachineDotExporter { - public static void export(StateMachine stateMachine, PrintWriter writer) { - writer.println("digraph stateMachine {"); - - for (State state : stateMachine.getStates()) { - writer.printf(" %s;%n", state.getId()); - } - - for (Transition transition : stateMachine.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("}"); - } -} - - -class StateMachinePlantUMLExporter { - - public static void export(StateMachine stateMachine, PrintWriter writer) { - writer.println("@startuml"); - - // Initial state - writer.println("[*] --> " + stateMachine.getInitialState().getId()); - - for (Transition transition : stateMachine.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 event is empty but there’s a guard, still label it meaningfully - if (!label.isEmpty()) { - label += " "; - } - label += "[guard]"; - } - - // If there's no label at all, don't include ":" - if (!label.isEmpty()) { - writer.printf("%s --> %s : %s%n", source, target, label); - } else { - writer.printf("%s --> %s%n", source, target); - } - } - } - - // End states - for (State state : stateMachine.getStates()) { - if (state.getPseudoState() != null && - state.getPseudoState().getKind() == PseudoStateKind.END) { - writer.printf("%s --> [*]%n", state.getId()); - } - } - - writer.println("@enduml"); - } -} - -@Component -@RequiredArgsConstructor -class Config1 { - @Qualifier("simpleEnumStateMachineFactory") - private final StateMachineFactory simpleEnumStateMachineConfiguration; - public void a() throws FileNotFoundException { - StateMachine stateMachine = simpleEnumStateMachineConfiguration.getStateMachine("order"); - - StateMachineDotExporter exporter = new StateMachineDotExporter(); - try (PrintWriter out = new PrintWriter("statemachine.dot")) { - StateMachineDotExporter.export(stateMachine, out); - } - } -} - -@Component -@RequiredArgsConstructor -class Config2 { - @Qualifier("simpleEnumStateMachineFactory") - private final StateMachineFactory simpleEnumStateMachineConfiguration; - public void a() throws FileNotFoundException { - StateMachine stateMachine = simpleEnumStateMachineConfiguration.getStateMachine("order"); - - try (PrintWriter out = new PrintWriter("statemachine.uml.dot")) { - StateMachinePlantUMLExporter.export(stateMachine, out); - } - } -} -@Component -@RequiredArgsConstructor -class Config3 { - @Qualifier("simpleEnumStateMachineFactory") - private final StateMachineFactory simpleEnumStateMachineConfiguration; - public void exportToSCXML() throws Exception { - StateMachine stateMachine = simpleEnumStateMachineConfiguration.getStateMachine("order"); - - SCXMLExporter exporter = new SCXMLExporter(); - File file = new File("statemachine.scxml.xml"); - exporter.exportToSCXML(stateMachine); - } -} - - -@Component -class SCXMLExporter { - - // This method assumes you already have a state machine - public void exportToSCXML(StateMachine stateMachine) throws Exception { - // Create a document to represent the SCXML structure - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.newDocument(); - - // Root SCXML element - Element scxmlElement = doc.createElement("scxml"); - scxmlElement.setAttribute("version", "1.0"); - doc.appendChild(scxmlElement); - - // Define the states - Element statesElement = doc.createElement("states"); - scxmlElement.appendChild(statesElement); - - // Iterate over the states of the state machine - Collection> states = stateMachine.getStates(); - for (State state : states) { - Element stateElement = doc.createElement("state"); - stateElement.setAttribute("id", state.getId().toString()); - statesElement.appendChild(stateElement); - } - - // Define the transitions - Element transitionsElement = doc.createElement("transitions"); - scxmlElement.appendChild(transitionsElement); - - Collection> transitions = stateMachine.getTransitions(); - for (Transition transition : transitions) { - Element transitionElement = doc.createElement("transition"); - transitionElement.setAttribute("from", transition.getSource().getId().toString()); - transitionElement.setAttribute("to", transition.getTarget().getId().toString()); - transitionElement.setAttribute("event", String.valueOf(transition.getTrigger())); - transitionsElement.appendChild(transitionElement); - } - - // Write to file - File file = new File("stateMachine.scxml"); - saveDocumentToFile(doc, file); - } - - private void saveDocumentToFile(Document doc, File file) throws Exception { - // Serialize the document to a file (you can use your preferred XML writer) - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - StreamResult result = new StreamResult(file); - DOMSource source = new DOMSource(doc); - transformer.transform(source, result); - } -} \ No newline at end of file diff --git a/src/test/java/click/kamil/springstatemachineexporter/StatemachinedemoApplicationTests.java b/src/test/java/click/kamil/springstatemachineexporter/StatemachinedemoApplicationTests.java new file mode 100644 index 0000000..1ade884 --- /dev/null +++ b/src/test/java/click/kamil/springstatemachineexporter/StatemachinedemoApplicationTests.java @@ -0,0 +1,18 @@ +package click.kamil.springstatemachineexporter; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +@SpringBootTest +class StatemachinedemoApplicationTests { + + @MockitoBean + CommandLineRunner commandLineRunner; + + @Test + void contextLoads() { + } + +} diff --git a/src/test/java/com/example/statemachinedemo/StatemachinedemoApplicationTests.java b/src/test/java/com/example/statemachinedemo/StatemachinedemoApplicationTests.java deleted file mode 100644 index 570c043..0000000 --- a/src/test/java/com/example/statemachinedemo/StatemachinedemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.statemachinedemo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class StatemachinedemoApplicationTests { - - @Test - void contextLoads() { - } - -}