Replace eager enum widen with symbolic placeholders and deferred narrowing, add switch-based predicate evaluation and entry-point synthesis for JSON round-trips. Co-authored-by: Cursor <cursoragent@cursor.com>
Spring State Machine Explorer
Static analysis tool to extract and visualize Spring State Machines with deep code traceability.
Core Modules
state_machine_exporter: Java AST analyzer. Scans source code, resolves local Maven/Gradle dependencies, and extracts logic (Lambdas, Actions, Guards) into JSON/PUML.state_machine_exporter_html: Generates an interactive HTML portal with SVG diagrams and rich tooltips.
Usage
Analysis is source-first: the exporter reads project source and build metadata (pom.xml, settings.gradle) statically. It does not run Maven or Gradle against the project being analyzed.
1. Extract Metadata (JSON)
./gradlew :state_machine_exporter:run --args="-i ./my-project -o ./out -f json"
Common options:
| Flag | Description |
|---|---|
-i, --input |
Project directory to scan (defaults to .) |
-j, --json |
Re-render from an existing JSON export instead of scanning source |
-o, --output |
Output directory (default ./out) |
-f, --format |
Comma-separated formats: json, dot, puml, scxml |
-p, --profiles |
Spring profiles for property placeholder resolution |
--event, --state |
Enum formatting: fn (default), fqn, sn |
--debug |
Verbose analysis logging and unresolved chain diagnostics |
--no-diamonds |
Render choice/junction states as regular nodes (default: diamonds) |
--include-generated-sources |
Also scan target/ / build/ generated .java trees (default: on) |
--no-inline-accessors |
Disable accessor indexing/inlining (enabled by default) |
2. Generate Interactive Portal (HTML)
./gradlew :state_machine_exporter_html:run --args="-i ./my-project -o ./out_html"
HTML-specific options:
| Flag | Description |
|---|---|
-f, --flows |
Custom flows.json path |
-m, --machine |
Export only machines whose name contains this filter |
--open |
Open the generated portal in the default browser |
--no-metadata-pane |
Hide the left metadata pane (entry points, flows) |
--no-diamonds |
Render choice pseudostates as normal nodes |
--debug |
Same diagnostic mode as the core exporter |
--include-generated-sources |
Same generated-source scanning as the core exporter |
--no-inline-accessors |
Disable accessor indexing/inlining (enabled by default) |
-p, --profiles |
Spring profiles for property resolution |
Business Flows
Define sequence of events in src/main/resources/flows.json:
[
{
"name": "Order Success",
"description": "Happy path for order placement",
"steps": ["PAY", "CHECK_AVAILABILITY->PENDING", "SHIP"]
}
]
JSON Structure
metadata.entryPoints: REST, WebFlux, and JMS entry points.metadata.callChains: Trace from API call to machine trigger (sendEvent).transitions.actions[].internalLogic: Extracted source code/lambdas.metadata.properties: Dictionary of all detected Spring profiles.
Static analysis: accessor inlining
During scan, the exporter builds an accessor index of trivial getters, setters, record components, and constant-returning methods. At analysis time, AccessorResolver uses this index first (O(1) lookup), then falls back to constructor/field tracing and method-body evaluation.
This improves resolution of DTO/event chains such as request.getPayload().getType() without running Maven or Gradle on the target project.
# Enabled by default; disable for debugging or A/B comparison
./gradlew :state_machine_exporter:run --args="-i ./my-project -o ./out --no-inline-accessors"
Pipeline components live under analysis/pipeline/ (AccessorResolver, ResolutionBudget, MethodInvocationUnwrapper, FieldInitializerFinder, LibraryUnwrapRegistry).