export and import json
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -1,6 +1,7 @@
|
|||||||
1.extract from all possible formats (ask chatgpt)
|
1.extract from all possible formats (ask chatgpt)
|
||||||
|
|
||||||
json, xml, maybe yaml
|
[DONE] json
|
||||||
|
[TODO] xml, maybe yaml
|
||||||
|
|
||||||
2. [PLAN] Support external dependencies (JARs/compiled classes):
|
2. [PLAN] Support external dependencies (JARs/compiled classes):
|
||||||
- Enable binding resolution in ASTParser (`setResolveBindings(true)`).
|
- Enable binding resolution in ASTParser (`setResolveBindings(true)`).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'com.gradleup.shadow' version '8.3.5'
|
id 'com.gradleup.shadow' version '9.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'click.kamil.file_combine'
|
group = 'click.kamil.file_combine'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'com.gradleup.shadow' version '8.3.5'
|
id 'com.gradleup.shadow' version '9.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'click.kamil.springstatemachineexporter'
|
group = 'click.kamil.springstatemachineexporter'
|
||||||
@@ -32,6 +32,11 @@ dependencies {
|
|||||||
// deps for parsing java AST
|
// deps for parsing java AST
|
||||||
implementation 'org.eclipse.jdt:org.eclipse.jdt.core:3.36.0'
|
implementation 'org.eclipse.jdt:org.eclipse.jdt.core:3.36.0'
|
||||||
|
|
||||||
|
// Jackson for JSON support
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.1'
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1'
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.46'
|
compileOnly 'org.projectlombok:lombok:1.18.46'
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.46'
|
annotationProcessor 'org.projectlombok:lombok:1.18.46'
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package click.kamil.springstatemachineexporter;
|
package click.kamil.springstatemachineexporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.out.Dot;
|
import click.kamil.springstatemachineexporter.exporter.Dot;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.PlantUml;
|
import click.kamil.springstatemachineexporter.exporter.JsonExporter;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.Scxml;
|
import click.kamil.springstatemachineexporter.exporter.PlantUml;
|
||||||
|
import click.kamil.springstatemachineexporter.exporter.Scxml;
|
||||||
import click.kamil.springstatemachineexporter.command.ExporterCommand;
|
import click.kamil.springstatemachineexporter.command.ExporterCommand;
|
||||||
import click.kamil.springstatemachineexporter.service.ExportService;
|
import click.kamil.springstatemachineexporter.service.ExportService;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
@@ -12,7 +13,7 @@ import java.util.List;
|
|||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// Manual DI / Wiring
|
// Manual DI / Wiring
|
||||||
var exporters = List.of(new PlantUml(), new Dot(), new Scxml());
|
var exporters = List.of(new PlantUml(), new Dot(), new Scxml(), new JsonExporter());
|
||||||
var exportService = new ExportService(exporters);
|
var exportService = new ExportService(exporters);
|
||||||
var command = new ExporterCommand(exportService);
|
var command = new ExporterCommand(exportService);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Action;
|
import click.kamil.springstatemachineexporter.model.Action;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Guard;
|
import click.kamil.springstatemachineexporter.model.Guard;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||||
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
|
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.eclipse.jdt.core.dom.ArrayInitializer;
|
import org.eclipse.jdt.core.dom.ArrayInitializer;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.common;
|
package click.kamil.springstatemachineexporter.ast.common;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import org.eclipse.jdt.core.dom.AST;
|
import org.eclipse.jdt.core.dom.AST;
|
||||||
import org.eclipse.jdt.core.dom.ASTParser;
|
import org.eclipse.jdt.core.dom.ASTParser;
|
||||||
import org.eclipse.jdt.core.dom.Annotation;
|
import org.eclipse.jdt.core.dom.Annotation;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import picocli.CommandLine.Command;
|
|||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
@@ -29,27 +30,49 @@ public class ExporterCommand implements Callable<Integer> {
|
|||||||
@CommandLine.Spec
|
@CommandLine.Spec
|
||||||
CommandLine.Model.CommandSpec spec;
|
CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
@Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code.", defaultValue = ".")
|
@Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code.")
|
||||||
private Path inputDir;
|
private Path inputDir;
|
||||||
|
|
||||||
|
@Option(names = {"-j", "--json"}, description = "Input JSON file containing the state machine definition.")
|
||||||
|
private Path jsonFile;
|
||||||
|
|
||||||
@Option(names = {"-o", "--output"}, description = "Output directory for the generated diagrams.", defaultValue = "./out")
|
@Option(names = {"-o", "--output"}, description = "Output directory for the generated diagrams.", defaultValue = "./out")
|
||||||
private Path outputDir;
|
private Path outputDir;
|
||||||
|
|
||||||
|
@Option(names = {"-f", "--format"}, description = "Comma-separated list of formats to export (dot, puml, scxml, json). If omitted, all formats are exported.", split = ",")
|
||||||
|
private List<String> selectedFormats;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer call() throws Exception {
|
public Integer call() throws Exception {
|
||||||
var out = spec.commandLine().getOut();
|
var out = spec.commandLine().getOut();
|
||||||
var err = spec.commandLine().getErr();
|
var err = spec.commandLine().getErr();
|
||||||
|
|
||||||
|
if (inputDir == null && jsonFile == null) {
|
||||||
|
inputDir = Path.of(".");
|
||||||
|
}
|
||||||
|
|
||||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|bold,green Starting export process...|@"));
|
out.println(CommandLine.Help.Ansi.AUTO.string("@|bold,green Starting export process...|@"));
|
||||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input Directory:|@ " + inputDir.toAbsolutePath()));
|
if (jsonFile != null) {
|
||||||
|
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input JSON File:|@ " + jsonFile.toAbsolutePath()));
|
||||||
|
} else {
|
||||||
|
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input Directory:|@ " + inputDir.toAbsolutePath()));
|
||||||
|
}
|
||||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Output Directory:|@ " + outputDir.toAbsolutePath()));
|
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Output Directory:|@ " + outputDir.toAbsolutePath()));
|
||||||
|
if (selectedFormats != null && !selectedFormats.isEmpty()) {
|
||||||
|
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Selected Formats:|@ " + String.join(", ", selectedFormats)));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exportService.runExporter(inputDir, outputDir);
|
if (jsonFile != null) {
|
||||||
|
exportService.runJsonExporter(jsonFile, outputDir, selectedFormats);
|
||||||
|
} else {
|
||||||
|
exportService.runExporter(inputDir, outputDir, selectedFormats);
|
||||||
|
}
|
||||||
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
|
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
|
||||||
return 0;
|
return 0;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
err.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,red ERROR:|@ An error occurred during the export process: " + e.getMessage()));
|
err.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,red ERROR:|@ An error occurred during the export process: " + e.getMessage()));
|
||||||
|
e.printStackTrace(err);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.out;
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -18,7 +18,8 @@ public class Dot implements StateMachineExporter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String export(List<Transition> transitions,
|
public String export(String name,
|
||||||
|
List<Transition> transitions,
|
||||||
Set<String> startStates,
|
Set<String> startStates,
|
||||||
Set<String> endStates,
|
Set<String> endStates,
|
||||||
boolean useLambdaGuards) {
|
boolean useLambdaGuards) {
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.model.StateMachineModel;
|
||||||
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class JsonExporter implements StateMachineExporter {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public JsonExporter() {
|
||||||
|
this.objectMapper = new ObjectMapper()
|
||||||
|
.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String export(String name, List<Transition> transitions, Set<String> startStates, Set<String> endStates, boolean useLambdaGuards) {
|
||||||
|
try {
|
||||||
|
StateMachineModel model = StateMachineModel.builder()
|
||||||
|
.name(name)
|
||||||
|
.transitions(transitions)
|
||||||
|
.startStates(startStates)
|
||||||
|
.endStates(endStates)
|
||||||
|
.build();
|
||||||
|
return objectMapper.writeValueAsString(model);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to export to JSON", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFileExtension() {
|
||||||
|
return ".json";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.out;
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.StringUtils;
|
import click.kamil.springstatemachineexporter.ast.common.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -34,10 +34,12 @@ public class PlantUml implements StateMachineExporter {
|
|||||||
private final String LAMBDA = "λ";
|
private final String LAMBDA = "λ";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String export(List<Transition> transitions,
|
public String export(String name,
|
||||||
|
List<Transition> transitions,
|
||||||
Set<String> startStates,
|
Set<String> startStates,
|
||||||
Set<String> endStates,
|
Set<String> endStates,
|
||||||
boolean useLambdaGuards) {
|
boolean useLambdaGuards) {
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("@startuml\n");
|
sb.append("@startuml\n");
|
||||||
sb.append("skinparam state {\n");
|
sb.append("skinparam state {\n");
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.out;
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,7 +13,8 @@ public class Scxml implements StateMachineExporter {
|
|||||||
private static final String LAMBDA = "λ";
|
private static final String LAMBDA = "λ";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String export(List<Transition> transitions,
|
public String export(String name,
|
||||||
|
List<Transition> transitions,
|
||||||
Set<String> startStates,
|
Set<String> startStates,
|
||||||
Set<String> endStates,
|
Set<String> endStates,
|
||||||
boolean useLambdaGuards) {
|
boolean useLambdaGuards) {
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.out;
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface StateMachineExporter {
|
public interface StateMachineExporter {
|
||||||
String export(List<Transition> transitions,
|
String export(String name,
|
||||||
|
List<Transition> transitions,
|
||||||
Set<String> startStates,
|
Set<String> startStates,
|
||||||
Set<String> endStates,
|
Set<String> endStates,
|
||||||
boolean useLambdaGuards);
|
boolean useLambdaGuards);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app.domain;
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
public record Action(String expression, boolean isLambda) {
|
public record Action(String expression, boolean isLambda) {
|
||||||
public static Action of(String expression, boolean isLambda) {
|
public static Action of(String expression, boolean isLambda) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app.domain;
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
public record Guard(String expression, boolean isLambda) {
|
public record Guard(String expression, boolean isLambda) {
|
||||||
public static Guard of(String expression, boolean isLambda) {
|
public static Guard of(String expression, boolean isLambda) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app.domain;
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
public record State(String rawName, String fullIdentifier) {
|
public record State(String rawName, String fullIdentifier) {
|
||||||
public static State of(String name) {
|
public static State of(String name) {
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class StateMachineModel {
|
||||||
|
private String name;
|
||||||
|
private List<Transition> transitions;
|
||||||
|
private Set<String> startStates;
|
||||||
|
private Set<String> endStates;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app.domain;
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app.domain;
|
package click.kamil.springstatemachineexporter.model;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -3,9 +3,10 @@ package click.kamil.springstatemachineexporter.service;
|
|||||||
import click.kamil.springstatemachineexporter.ast.app.AstTransitionParser;
|
import click.kamil.springstatemachineexporter.ast.app.AstTransitionParser;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.StateMachineAggregator;
|
import click.kamil.springstatemachineexporter.ast.app.StateMachineAggregator;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.TransitionStateUtils;
|
import click.kamil.springstatemachineexporter.ast.app.TransitionStateUtils;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.StateMachineModel;
|
||||||
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.StateMachineExporter;
|
import click.kamil.springstatemachineexporter.exporter.StateMachineExporter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||||
@@ -27,13 +28,19 @@ public class ExportService {
|
|||||||
private static final List<String> TARGET_ANNOTATIONS = List.of("EnableStateMachineFactory", "EnableStateMachine");
|
private static final List<String> TARGET_ANNOTATIONS = List.of("EnableStateMachineFactory", "EnableStateMachine");
|
||||||
public static final Set<String> STATE_MACHINE_RETURN_TYPES = Set.of("StateMachine", "StateMachineFactory", "StateMachineModelFactory");
|
public static final Set<String> STATE_MACHINE_RETURN_TYPES = Set.of("StateMachine", "StateMachineFactory", "StateMachineModelFactory");
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats) throws IOException {
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(inputDir);
|
context.scan(inputDir);
|
||||||
exportAll(context, outputDir);
|
exportAll(context, outputDir, selectedFormats);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportAll(CodebaseContext context, Path outputDir) throws IOException {
|
public void runJsonExporter(Path jsonFile, Path outputDir, List<String> selectedFormats) throws IOException {
|
||||||
|
JsonImportService jsonImportService = new JsonImportService();
|
||||||
|
StateMachineModel model = jsonImportService.importJson(jsonFile);
|
||||||
|
generateOutputs(outputDir, model.getName(), model.getTransitions(), model.getStartStates(), model.getEndStates(), selectedFormats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exportAll(CodebaseContext context, Path outputDir, List<String> selectedFormats) throws IOException {
|
||||||
Set<String> processedLocations = new HashSet<>();
|
Set<String> processedLocations = new HashSet<>();
|
||||||
|
|
||||||
// 1. Find entry point classes (annotated or extending adapter)
|
// 1. Find entry point classes (annotated or extending adapter)
|
||||||
@@ -41,7 +48,7 @@ public class ExportService {
|
|||||||
for (TypeDeclaration td : entryPoints) {
|
for (TypeDeclaration td : entryPoints) {
|
||||||
String fqn = context.getFqn(td);
|
String fqn = context.getFqn(td);
|
||||||
if (processedLocations.add(fqn)) {
|
if (processedLocations.add(fqn)) {
|
||||||
processEntryPoint(td, outputDir, context);
|
processEntryPoint(td, outputDir, context, selectedFormats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,12 +61,12 @@ public class ExportService {
|
|||||||
String uniqueId = parentFqn + "#" + methodName;
|
String uniqueId = parentFqn + "#" + methodName;
|
||||||
|
|
||||||
if (processedLocations.add(uniqueId)) {
|
if (processedLocations.add(uniqueId)) {
|
||||||
processBeanMethod(m, outputDir, context);
|
processBeanMethod(m, outputDir, context, selectedFormats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processEntryPoint(TypeDeclaration td, Path outputDir, CodebaseContext context) throws IOException {
|
private void processEntryPoint(TypeDeclaration td, Path outputDir, CodebaseContext context, List<String> selectedFormats) throws IOException {
|
||||||
String className = context.getFqn(td);
|
String className = context.getFqn(td);
|
||||||
log.info("Processing state machine config: {}", className);
|
log.info("Processing state machine config: {}", className);
|
||||||
|
|
||||||
@@ -76,10 +83,10 @@ public class ExportService {
|
|||||||
Set<String> startStates = TransitionStateUtils.findStartStates(transitions, initialStatesAst);
|
Set<String> startStates = TransitionStateUtils.findStartStates(transitions, initialStatesAst);
|
||||||
Set<String> endStates = TransitionStateUtils.findEndStates(transitions, endStatesAst);
|
Set<String> endStates = TransitionStateUtils.findEndStates(transitions, endStatesAst);
|
||||||
|
|
||||||
generateOutputs(outputDir, className, transitions, startStates, endStates);
|
generateOutputs(outputDir, className, transitions, startStates, endStates, selectedFormats);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processBeanMethod(MethodDeclaration m, Path outputDir, CodebaseContext context) throws IOException {
|
private void processBeanMethod(MethodDeclaration m, Path outputDir, CodebaseContext context, List<String> selectedFormats) throws IOException {
|
||||||
TypeDeclaration parentClass = (TypeDeclaration) m.getParent();
|
TypeDeclaration parentClass = (TypeDeclaration) m.getParent();
|
||||||
String parentFqn = context.getFqn(parentClass);
|
String parentFqn = context.getFqn(parentClass);
|
||||||
String methodName = m.getName().getIdentifier();
|
String methodName = m.getName().getIdentifier();
|
||||||
@@ -92,17 +99,22 @@ public class ExportService {
|
|||||||
Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
|
Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
|
||||||
Set<String> endStates = TransitionStateUtils.findEndStates(transitions);
|
Set<String> endStates = TransitionStateUtils.findEndStates(transitions);
|
||||||
|
|
||||||
generateOutputs(outputDir, uniqueName, transitions, startStates, endStates);
|
generateOutputs(outputDir, uniqueName, transitions, startStates, endStates, selectedFormats);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateOutputs(Path outputDir, String baseName, List<Transition> transitions,
|
private void generateOutputs(Path outputDir, String baseName, List<Transition> transitions,
|
||||||
Set<String> startStates, Set<String> endStates) throws IOException {
|
Set<String> startStates, Set<String> endStates, List<String> selectedFormats) throws IOException {
|
||||||
|
|
||||||
Path targetDir = outputDir.resolve(baseName);
|
Path targetDir = outputDir.resolve(baseName);
|
||||||
Files.createDirectories(targetDir);
|
Files.createDirectories(targetDir);
|
||||||
|
|
||||||
for (StateMachineExporter output : exporters) {
|
for (StateMachineExporter output : exporters) {
|
||||||
String content = output.export(transitions, startStates, endStates, true);
|
if (selectedFormats != null && !selectedFormats.isEmpty()) {
|
||||||
|
boolean match = selectedFormats.stream().anyMatch(f -> output.getFileExtension().toLowerCase().contains(f.toLowerCase()));
|
||||||
|
if (!match) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String content = output.export(baseName, transitions, startStates, endStates, true);
|
||||||
String fileName = baseName + output.getFileExtension();
|
String fileName = baseName + output.getFileExtension();
|
||||||
|
|
||||||
try (PrintWriter out = new PrintWriter(targetDir.resolve(fileName).toFile())) {
|
try (PrintWriter out = new PrintWriter(targetDir.resolve(fileName).toFile())) {
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.model.StateMachineModel;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class JsonImportService {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public JsonImportService() {
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateMachineModel importJson(Path jsonFile) throws IOException {
|
||||||
|
StateMachineModel model = objectMapper.readValue(jsonFile.toFile(), StateMachineModel.class);
|
||||||
|
if (model.getName() == null) {
|
||||||
|
String fileName = jsonFile.getFileName().toString();
|
||||||
|
int dot = fileName.lastIndexOf('.');
|
||||||
|
model.setName(dot > 0 ? fileName.substring(0, dot) : fileName);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package click.kamil.springstatemachineexporter;
|
package click.kamil.springstatemachineexporter;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.out.Dot;
|
import click.kamil.springstatemachineexporter.exporter.Dot;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.PlantUml;
|
import click.kamil.springstatemachineexporter.exporter.PlantUml;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.Scxml;
|
import click.kamil.springstatemachineexporter.exporter.Scxml;
|
||||||
import click.kamil.springstatemachineexporter.ast.out.StateMachineExporter;
|
import click.kamil.springstatemachineexporter.exporter.StateMachineExporter;
|
||||||
import click.kamil.springstatemachineexporter.service.ExportService;
|
import click.kamil.springstatemachineexporter.service.ExportService;
|
||||||
import click.kamil.springstatemachineexporter.utils.TestScenario;
|
import click.kamil.springstatemachineexporter.utils.TestScenario;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
@@ -95,7 +95,7 @@ public class RegressionTest {
|
|||||||
@ParameterizedTest(name = "{0}")
|
@ParameterizedTest(name = "{0}")
|
||||||
@MethodSource("provideTestScenarios")
|
@MethodSource("provideTestScenarios")
|
||||||
void testOutputMatchesGolden(TestScenario scenario, @TempDir Path tempDir) throws Exception {
|
void testOutputMatchesGolden(TestScenario scenario, @TempDir Path tempDir) throws Exception {
|
||||||
exportService.runExporter(scenario.inputPath(), tempDir);
|
exportService.runExporter(scenario.inputPath(), tempDir, null);
|
||||||
|
|
||||||
// Find the generated directory (it might be named with FQN)
|
// Find the generated directory (it might be named with FQN)
|
||||||
List<Path> generatedDirs;
|
List<Path> generatedDirs;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Action;
|
import click.kamil.springstatemachineexporter.model.Action;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Guard;
|
import click.kamil.springstatemachineexporter.model.Guard;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import org.eclipse.jdt.core.dom.AST;
|
import org.eclipse.jdt.core.dom.AST;
|
||||||
import org.eclipse.jdt.core.dom.ASTParser;
|
import org.eclipse.jdt.core.dom.ASTParser;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.ast.app;
|
package click.kamil.springstatemachineexporter.ast.app;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.State;
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.exporter;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.model.State;
|
||||||
|
import click.kamil.springstatemachineexporter.model.Transition;
|
||||||
|
import click.kamil.springstatemachineexporter.model.TransitionType;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class JsonExporterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExportToJson() {
|
||||||
|
JsonExporter exporter = new JsonExporter();
|
||||||
|
Transition transition = new Transition();
|
||||||
|
transition.setType(TransitionType.EXTERNAL);
|
||||||
|
transition.setSourceStates(List.of(State.of("S1")));
|
||||||
|
transition.setTargetStates(List.of(State.of("S2")));
|
||||||
|
transition.setEvent("E1");
|
||||||
|
|
||||||
|
String json = exporter.export("TestSM", List.of(transition), Set.of("S1"), Set.of("S2"), true);
|
||||||
|
|
||||||
|
assertThat(json)
|
||||||
|
.contains("\"name\" : \"TestSM\"")
|
||||||
|
.contains("\"event\" : \"E1\"")
|
||||||
|
.contains("\"rawName\" : \"S1\"")
|
||||||
|
.contains("\"rawName\" : \"S2\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.model.StateMachineModel;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class JsonImportServiceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldImportFromJson(@TempDir Path tempDir) throws IOException {
|
||||||
|
Path jsonFile = tempDir.resolve("test.json");
|
||||||
|
String json = """
|
||||||
|
{
|
||||||
|
"name" : "ImportedSM",
|
||||||
|
"transitions" : [ {
|
||||||
|
"type" : "EXTERNAL",
|
||||||
|
"sourceStates" : [ { "rawName" : "S1", "fullIdentifier" : "S1" } ],
|
||||||
|
"targetStates" : [ { "rawName" : "S2", "fullIdentifier" : "S2" } ],
|
||||||
|
"event" : "E1"
|
||||||
|
} ],
|
||||||
|
"startStates" : [ "S1" ],
|
||||||
|
"endStates" : [ "S2" ]
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
Files.writeString(jsonFile, json);
|
||||||
|
|
||||||
|
JsonImportService importService = new JsonImportService();
|
||||||
|
StateMachineModel model = importService.importJson(jsonFile);
|
||||||
|
|
||||||
|
assertThat(model.getName()).isEqualTo("ImportedSM");
|
||||||
|
assertThat(model.getTransitions()).hasSize(1);
|
||||||
|
assertThat(model.getTransitions().get(0).getEvent()).isEqualTo("E1");
|
||||||
|
assertThat(model.getStartStates()).containsExactly("S1");
|
||||||
|
assertThat(model.getEndStates()).containsExactly("S2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user