42 Commits

Author SHA1 Message Date
bb97285906 kotlin 2026-07-07 17:39:09 +02:00
9a5f122bd2 ai idea 2026-07-07 04:50:18 +02:00
a7bf07e8b2 another ai fix 2026-07-06 22:09:23 +02:00
4f8c35adef resolve path 2026-07-06 22:01:41 +02:00
31adae0a4b fix! from ai multi project 2026-07-06 21:57:54 +02:00
2caaec5419 from ai two 2026-07-06 21:41:26 +02:00
56588a835d from ai 2026-07-06 21:21:35 +02:00
0a8f9720c9 10 2026-07-06 19:13:12 +02:00
6472f01026 9 2026-07-06 18:54:58 +02:00
e041cb9a80 8 2026-07-06 18:29:09 +02:00
9bea5c4687 7 2026-07-06 18:25:48 +02:00
475084aed4 7 2026-07-06 18:17:37 +02:00
28df9fc99f 6 2026-07-06 18:07:02 +02:00
e3dd26c0d4 5 2026-07-06 18:05:41 +02:00
89736a4aa1 4 2026-07-06 17:59:58 +02:00
092fbe49fa 4 2026-07-06 17:56:19 +02:00
61f5549589 3 2026-07-06 17:53:42 +02:00
f5067490a3 2 updated 2026-07-06 17:50:41 +02:00
3a442c8890 2 tests 2026-07-06 17:49:11 +02:00
7807df57ca 2 2026-07-06 17:48:21 +02:00
82719489aa 1 2026-07-06 17:43:34 +02:00
c390f02389 heuristic code 2026-07-05 22:00:26 +02:00
180a283383 siblings 2026-07-05 21:17:25 +02:00
1c852005c8 remove tons of logs 2026-07-05 21:13:07 +02:00
34c53b1097 update to fix that 2026-07-05 20:37:21 +02:00
23af434504 update 2026-07-05 20:02:02 +02:00
cc352432a4 fix on fix 2026-07-05 19:28:01 +02:00
78c0f3e705 apparently perfomance fix that doesn't bring regreesions, NOT SURE 2026-07-05 16:29:42 +02:00
27005da678 update number 6 2026-07-05 16:15:52 +02:00
0cb78e5dfd update number 5 2026-07-05 15:57:53 +02:00
bcdbbffa13 update number 4 2026-07-05 15:11:09 +02:00
3988864494 update number 3 2026-07-05 14:46:38 +02:00
b07b7855a1 update number 2 2026-07-05 14:34:52 +02:00
baa33a887c up 2026-07-05 09:46:32 +02:00
0165da5f51 up 2026-07-04 07:25:11 +02:00
4d7cda56f5 up 2026-07-04 06:30:30 +02:00
2717a01f13 up 2026-06-28 15:24:44 +02:00
f84043f26e up 2026-06-28 15:23:22 +02:00
4d9fee716b updates 2026-06-28 15:17:16 +02:00
f3d030b19a another test state machine 2026-06-28 11:57:01 +02:00
16dccbf81b another test state machine 2026-06-28 11:42:18 +02:00
20b4ed780b update tranistions 2026-06-28 11:23:24 +02:00
149 changed files with 11242 additions and 1688 deletions

BIN
TestHeuristic.class Normal file

Binary file not shown.

17
TestHeuristic.java Normal file
View File

@@ -0,0 +1,17 @@
import java.util.*;
public class TestHeuristic {
public static void main(String[] args) {
System.out.println(isFullyQualifiedMethod("TransformerImpl.transform"));
System.out.println(isFullyQualifiedMethod("Transformer.transform"));
}
private static boolean isFullyQualifiedMethod(String methodFqn) {
if (methodFqn == null || !methodFqn.contains(".")) return false;
int lastDot = methodFqn.lastIndexOf('.');
String classPart = methodFqn.substring(0, lastDot);
if (classPart.contains(".")) return true;
if (!classPart.isEmpty() && Character.isUpperCase(classPart.charAt(0))) return true;
return false;
}
}

View File

@@ -1,26 +0,0 @@
## Phase 11: Formal Compiler & Static Analysis Alignment (Limitations & Missing Features)
While the custom AST-based `JdtDataFlowModel` supports advanced OOP data flow (like flow-sensitive local mutations, fluent builders, and polymorphic union fallbacks), the following compiler/static analysis behaviors are missing or simplified:
1. **Full Pointer & Alias Analysis**:
- If two variables alias the same object (e.g., `Command cmd1 = cmd2;`), a mutation on `cmd1` (e.g., `cmd1.setEvent("A")`) is not reflected when reading the fields of `cmd2` unless they resolve to the exact same AST node reference. A formal pointer/alias analysis (like Anderson's or Steensgaard's algorithm) would map abstract heap locations rather than AST nodes.
2. **Advanced Loop Fixpoints for Fields**:
- If a loop mutates a field based on its previous values (e.g., `while(cond) { builder.count(builder.getCount() + 1); }`), a simple sequential pass will miss the cyclic dependency. A formal data flow framework uses a mathematical lattice and runs a fixpoint iteration algorithm (using meet/join operators) to compute the stable approximation of values.
3. **Exception Path Analysis (Exception CFG)**:
- Edges for exception flows (`throw` statements, `try-catch-finally` blocks, and implicit unchecked runtime exceptions like `NullPointerException`) are not modeled. Consequently, mutations or return paths within catch/finally blocks may be missed or evaluated out of order.
4. **Collection & Array Tracking**:
- Arrays and Collections (such as `List`, `Map`, `Set`) are treated as opaque objects, and their operations (like `list.add(val)` or `map.put(key, val)`) are evaluated as general method calls. Modifying a collection does not propagate the values to its getter calls (e.g. `list.get(0)`).
5. **Field Shadowing and Inheritance Semantics**:
- If a subclass shadows a superclass field (declaring a field with the same name), our resolver might resolve the wrong field binding. Also, implicit superclass constructors and field initializers from superclass hierarchies are not fully simulated.
6. **Static Initialization Blocks and Static Mutable State**:
- Class loading execution order (i.e. static block initializations `static { ... }`) is not modeled. A mutation on a static field in one method is not propagated to reads in another method.
7. **Type Erasure & Generics Precision**:
- Generics type bounds (e.g., wildcards `? extends T`, type parameter replacements) are not fully tracked, which can result in incorrect polymorphic resolution when method signatures differ slightly from erased bindings.

View File

@@ -1,42 +0,0 @@
# Advanced Context Extraction: Payloads and Interceptors
## 1. Incoming Payload Analysis
To understand *what* data drives the state machine, we need to capture metadata about the incoming parameters at entry points.
### REST Endpoints
For methods annotated with `@PostMapping`, `@PutMapping`, etc.:
1. **Extract Parameters**: Scan method parameters for `@RequestBody`, `@PathVariable`, or `@RequestParam`.
2. **Type Extraction**: Capture the FQN of the parameter type (e.g., `com.example.OrderRequest`).
3. **Data Linkage**: If a field from this payload is used in a `sendEvent(payload.getEvent())` call, mark the transition as "Dynamic based on Payload".
### Message Listeners
For `@RabbitListener` or `@JmsListener`:
1. Identify the parameter receiving the message body.
2. Extract its type and any `@Header` or `@Payload` annotations.
## 2. Interceptor and Filter Analysis
Interceptors often perform cross-cutting logic (authentication, context injection) that affects how state machines are driven.
### Discovery Strategy
1. **Find Interceptors**: Scan for classes implementing `HandlerInterceptor` or `WebRequestInterceptor`.
2. **Registration Mapping**: Look for `WebMvcConfigurer.addInterceptors()` calls to identify which URL patterns each interceptor applies to.
3. **Context Injection**: Analyze if the interceptor modifies the request (e.g., `request.setAttribute("userContext", ...)`) or populates a `ThreadLocal` (like `SecurityContextHolder`).
### Linking to State Machines
If a controller uses a value from a known Interceptor-injected context to decide which event to send or which state machine to restore, we can link the Interceptor's logic to the State Machine flow.
## 3. Implementation in `EnrichmentService`
We will introduce a `ContextEnricher` that:
1. Builds a map of **Active Interceptors** per path.
2. Augments `EntryPoint` metadata with **Payload Type** information.
## 4. Modeling Updates
Update `EntryPoint` and `TriggerPoint` to include `payloadType` and `interceptors`.
```java
public record EntryPoint(
// ... existing fields ...
String payloadType,
List<String> activeInterceptors
) {}
```

View File

@@ -1,63 +0,0 @@
# Decoupled Analysis Architecture: The "Modular Provider" Approach
## Core Philosophy
We decouple the **State Machine Definition** (what the machine IS) from the **Codebase Intelligence** (how the machine IS USED). This allows for swappable analysis tools and prevents the state machine parser from becoming a monolithic "all-knowing" beast.
## 1. The Intelligence Interface (The Bridge)
We introduce an abstraction layer that provides information about the code without tying us to a specific implementation (like JDT).
```java
public interface CodebaseIntelligenceProvider {
// Event Triggers
List<TriggerPoint> findTriggerPoints();
// Entry Points
List<EntryPoint> findEntryPoints();
// Call Graphs
List<CallChain> traceCallChain(MethodReference target);
// Resolution
String resolveValue(ExpressionReference ref);
}
```
## 2. Swappable Implementations
This allows us to offer different "tiers" of analysis or even different tools:
- **JdtProvider (Current)**: High-precision, understands hierarchy, but heavy.
- **RegexProvider**: Lightweight, fast, good for simple projects or quick scans.
- **ExternalLspProvider (Future)**: Could query a running Language Server (like Eclipse JDT.LS) for ultra-accurate cross-project resolution.
## 3. The "State Machine Model" is the Sink
The State Machine Exporter now becomes a "Consumer" of intelligence.
1. **SM Parser**: Extracts the core states and transitions.
2. **Intelligence Service**: Discovers where events are triggered.
3. **Correlation Engine**: Merges the two models based on Event IDs.
## 4. Benefits
- **Testability**: We can mock the `CodebaseIntelligenceProvider` to test state machine rendering without needing a full Java project on disk.
- **Maintainability**: Adding support for a new framework (e.g., Micronaut) only requires updating the `IntelligenceProvider`, not touching the core state machine logic.
- **Performance**: We can run the SM Parser and the Intelligence Scanner in parallel since they are now independent.
## 5. Monorepo and Multi-Module Support
Applications are often split into multiple modules (e.g., `core`, `api`, `workers`).
- **Workspace Scanning**: The analyzer should treat the entire monorepo as a single codebase context.
- **Source Tracking**: Each metadata item (`TriggerPoint`, `EntryPoint`) includes a `sourceModule` identifier to show exactly where it was found.
- **Cross-Module Resolution**: Properties defined in one module's `application.yml` and used in another should be resolved globally.
- **Internal Dependency Following**: If the analyzer finds a call to a method in another module, it should continue the call-chain analysis into that module's source.
## Challenges
- **Multiple State Machines**: How to know which `StateMachine` instance is being used?
- Initial heuristic: If there's only one, assume it's that one.
- If multiple, check generic types `StateMachine<S, E>` or variable names.
- **Indirect Calls**: Method `A` (Controller) calls `B` (Service), and `B` calls `sendEvent`.
- Static analysis might need to follow call graphs.
- Use JDT's cross-reference capabilities if possible, or build a simple call graph.
- **Inheritance**:
- Controllers might extend base classes with common mappings.
- State machine configurations already handle inheritance; Trigger detection should too.
## Next Steps
1. Create a PoC `TriggerDetector` for Spring MVC.
2. Integrate `TriggerAggregator` into the main analysis pipeline.
3. Update the `Exporter` to visualize these links.

View File

@@ -1,40 +0,0 @@
# The Analysis Contract: Decoupling Metadata from Implementation
## 1. Unified Model
Instead of each parser having its own internal model, we move to a "Context-Rich Model".
```java
public record CodebaseMetadata(
List<TriggerPoint> triggers,
List<EntryPoint> entryPoints,
Map<String, String> properties,
CallGraph callGraph
) {}
```
## 2. Intelligence Provider Lifecycle
1. **Initialize**: Provide root paths and configuration.
2. **Scan**: The provider builds its internal index (e.g., JDT AST, Regex indices).
3. **Extract**: The provider returns a `CodebaseMetadata` object.
4. **Dispose**: Cleanup resources.
## 3. Tool-Specific Strengths
### JDT Provider (Advanced)
- **Strengths**: True semantic understanding, inheritance resolution, precise constant lookups.
- **Usage**: Deep analysis where we need to know that `BaseService.notify()` is actually what is being called in `ChildService`.
### Regex / Tree-Sitter Provider (Light)
- **Strengths**: Extremely fast, works even on broken code (doesn't need full classpath), very low memory overhead.
- **Usage**: Large monorepos where full JDT analysis would take minutes.
## 4. Aggregation Logic
The `IntelligenceService` can actually combine results from multiple providers!
- Use JDT for the State Machine Config (where we need high precision).
- Use Regex for a quick scan of 1000+ controllers to find mapping patterns.
## 5. Refactoring Strategy
1. **Extract**: Move current JDT-based transition parsing into a `JdtStateMachineProvider`.
2. **Define**: Create the `CodebaseMetadata` model.
3. **Bridge**: Update the `ExportService` to accept both a `StateMachineModel` and a `CodebaseMetadata` object.
4. **Implement**: Create the `CodebaseIntelligenceProvider` interface and its first implementation (wrapping the existing JDT logic).

View File

@@ -1,67 +0,0 @@
# Low-Impact Abstraction: The Enrichment Pipeline
## 1. Goal
Support enhanced codebase analysis (Endpoints, Listeners, etc.) without a massive refactor of the existing JDT state machine parsers.
## 2. The Core Model: `AnalysisResult`
We wrap the existing output into a container that can hold optional metadata.
```java
public class AnalysisResult {
// Current Core Data
private final String name;
private final List<Transition> transitions;
private final Set<String> startStates;
private final Set<String> endStates;
// Optional Enhanced Data
private CodebaseMetadata metadata = CodebaseMetadata.empty();
public AnalysisResult(String name, List<Transition> transitions, Set<String> startStates, Set<String> endStates) {
this.name = name;
this.transitions = transitions;
this.startStates = startStates;
this.endStates = endStates;
}
public void setMetadata(CodebaseMetadata metadata) {
this.metadata = metadata;
}
}
```
## 3. The "Enricher" Abstraction
Instead of modifying the parser, we "enrich" the result after it's produced.
```java
public interface AnalysisEnricher {
/**
* Examines the core analysis result and the codebase context
* to add extra metadata (triggers, endpoints, etc.).
*/
void enrich(AnalysisResult result, CodebaseContext context);
}
```
## 4. Implementation Tiers
### Tier 1: Current State (No-Op)
The `ExportService` calls `enrich()`, but since no enrichers are registered, it just returns the core data. This is zero-impact refactoring.
### Tier 2: Enhanced Analysis
Once we implement `SpringMvcEnricher` or `RabbitMqEnricher`, we register them. They will:
1. Scan the `CodebaseContext`.
2. Find `TriggerPoints`.
3. Link them to the `transitions` already present in the `AnalysisResult`.
4. Update the `metadata` field.
## 5. Why this is "Good":
- **Non-Breaking**: `StateMachineAggregator` remains untouched.
- **Pluggable**: You can add/remove "Intelligence" without changing the core parser.
- **Future-Proof**: If you switch from JDT to another tool for code analysis, you only change the `Enricher` implementation.
## 6. Refactoring Steps
1. **Create `AnalysisResult`** and **`CodebaseMetadata`** classes.
2. **Update `ExportService.generateOutputs`** to take an `AnalysisResult` object instead of 4 separate parameters.
3. **Introduce `EnrichmentService`** that holds a list of `AnalysisEnricher`s.
4. **Modify `ExportService`** to call `enrichmentService.enrich(result, context)` before passing the result to exporters.

View File

@@ -1,92 +0,0 @@
# Generic and Extensible Endpoint Finder
## The Need for Genericity
Applications use various frameworks and patterns to trigger state machine events. While Spring MVC and Kafka are common, others like Micronuat, Jakarta EE, or custom internal event buses might be used.
## Configuration-Driven Detection
We can allow users to define "Entry Point" patterns via a configuration file (e.g., `analysis-config.json`).
```json
{
"entryPoints": [
{
"name": "SpringMVC",
"classAnnotations": ["RestController", "Controller"],
"methodAnnotations": ["PostMapping", "GetMapping", "PutMapping", "DeleteMapping", "RequestMapping"],
"metadataExtractors": {
"path": ["value", "path"]
}
},
{
"name": "Kafka",
"methodAnnotations": ["KafkaListener"],
"metadataExtractors": {
"topic": ["topics", "topicPartitions"]
}
}
]
}
```
## Abstract EntryPoint Detector
A generic detector that uses the above configuration to identify entry points.
```java
public class ConfigurableTriggerDetector implements TriggerDetector {
private final AnalysisConfig config;
@Override
public List<TriggerPoint> detect(CompilationUnit cu, CodebaseContext context) {
// 1. Find all classes
// 2. If class has any of config.classAnnotations
// 3. Scan methods for any of config.methodAnnotations
// 4. If found, look for sendEvent calls inside the method or its call tree
}
}
```
## Indirect Flow Detection (Call Tree Analysis)
Oftentimes, the `sendEvent` call is not directly in the controller method but in a service called by the controller.
### Simple Call Tree Analysis
1. Find all methods that call `sendEvent(E)`. Let's call them "Direct Triggers".
2. Find all methods that call "Direct Triggers".
3. Recursively build this "Trigger Path".
4. If a "Trigger Path" starts at an "Entry Point" (Controller/Listener), we have a complete flow.
Example:
`OrderController.submitOrder()` -> `OrderService.processOrder()` -> `StateMachine.sendEvent(SUBMIT)`
Analysis:
1. `OrderService.processOrder()` is a Direct Trigger for `SUBMIT`.
2. `OrderController.submitOrder()` calls `OrderService.processOrder()`.
3. `OrderController.submitOrder()` is an Entry Point (annotated with `@PostMapping`).
4. Result: `POST /order` -> `SUBMIT` transition.
## Handling Reactive Chains Statically
In WebFlux/Project Reactor, the logic is decoupled into a pipeline. Static analysis must "connect the dots" across operators.
**Strategy**:
1. When analyzing a method, identify if it returns a reactive type (`Mono`, `Flux`).
2. If so, inspect all lambdas within that method.
3. If a lambda calls `sendEvent`, treat the method as a "Trigger Point".
4. If the method is also an "Entry Point" (via annotations or `RouterFunction`), we have a direct reactive flow.
## Supporting Inheritance in Listeners
Often, base classes or interfaces define generic listeners:
```java
public interface BaseConsumer<T> {
@RabbitListener(queues = "${app.queue}")
void handle(T payload);
}
```
The `CodebaseContext` must resolve the `${app.queue}` if possible (via `application.properties` scanning) or at least note that it's a parameterized listener.
## Advanced Call Graph: The "Data Flow"
For a truly detailed analysis, we should track NOT just method calls, but also how the "Event" object is passed around.
- Is the event hardcoded? `sm.sendEvent(Events.CREATE)`
- Is it derived from a payload field? `sm.sendEvent(payload.getEvent())`
- Is it mapped via a `switch` or `Map`?
**Static Mapping Inference**:
If we see a `switch` statement on a payload field that leads to different `sendEvent` calls, we can report multiple possible flows from the same endpoint.

View File

@@ -1,102 +0,0 @@
# Implementation Details: Trigger Detection with JDT
## 1. Finding `sendEvent` Calls
We can use an `ASTVisitor` to find all `MethodInvocation` nodes.
```java
public class SendEventVisitor extends ASTVisitor {
@Override
public boolean visit(MethodInvocation node) {
if ("sendEvent".equals(node.getName().getIdentifier())) {
// Found a call!
// 1. Extract event (first argument)
// 2. Identify the enclosing method and class
}
return super.visit(node);
}
}
```
### Challenges in Event Extraction
Events can be:
- Enum constants: `Events.SUBMIT`
- Strings: `"SUBMIT"`
- Variables: `sm.sendEvent(eventFromPayload)` (Hard to resolve statically)
We should reuse `CodebaseContext.resolveState` logic (which is basically resolving an expression to a value/fqn).
## 2. Identifying Enclosing Context
Once a `sendEvent` is found, we can traverse up the AST to find the `MethodDeclaration` and `TypeDeclaration`.
```java
ASTNode parent = node.getParent();
while (parent != null && !(parent instanceof MethodDeclaration)) {
parent = parent.getParent();
}
// parent is now the MethodDeclaration
```
### 2. Extracting Annotations
From `MethodDeclaration`, we can check for mappings:
- `@PostMapping`, `@GetMapping`, etc.
- `@KafkaListener`: Extract `topics`, `groupId`.
- `@RabbitListener`: Extract `queues`, `bindings` (Exchange/RoutingKey).
From `TypeDeclaration`, we can check for:
- `@RestController`, `@Controller`
- `@RequestMapping` at class level (to get base path)
## 3. Specialized WebFlux Analysis
### Annotation-based WebFlux
This is largely identical to Spring MVC. The challenge is if the `sendEvent` is wrapped in a reactive operator.
```java
public Mono<Void> submit(Order order) {
return service.save(order)
.doOnNext(o -> stateMachine.sendEvent(Events.SUBMIT))
.then();
}
```
**Static Strategy**: We need to look inside `LambdaExpression` nodes passed to reactive operators (`doOnNext`, `flatMap`, `map`, `subscribe`). The `ASTVisitor` should traverse into these lambdas.
### Functional WebFlux (RouterFunctions)
Functional endpoints are often defined as Beans returning `RouterFunction`.
```java
@Bean
public RouterFunction<ServerResponse> route(OrderHandler handler) {
return RouterFunctions.route(POST("/orders"), handler::submitOrder);
}
```
**Static Strategy**:
1. Find methods returning `RouterFunction`.
2. Analyze the `MethodInvocation` chain (`route`, `andRoute`, `nest`).
3. Extract the URI pattern and the `HandlerFunction` reference.
4. If the handler is a method reference (`handler::submitOrder`), link it to the corresponding `MethodDeclaration`.
## 4. Specialized RabbitMQ Analysis
`@RabbitListener` can be complex:
```java
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "orderQueue", durable = "true"),
exchange = @Exchange(value = "orderExchange"),
key = "order.created"
))
public void onOrder(Order order) { ... }
```
**Static Strategy**:
1. Find `@RabbitListener`.
2. If it has `bindings`, drill down into `@QueueBinding`, `@Queue`, `@Exchange` to extract the topology.
3. If it only has `queues`, resolve the queue name (might be a SpEL expression or property placeholder, which we can try to resolve or just keep as-is).
## 5. Indirect Flow Detection (The "Service Link")
If the project has multiple state machines, we need to know which one is being targeted.
Usually, this is done via:
- Autowiring by type: `StateMachine<States, Events> sm;`
- Autowiring by name: `@Qualifier("mySm") StateMachine sm;`
We can look at the fields of the class where `sendEvent` is called.
## 6. Output Enhancement
The `Exporter` should be modified to:
- In DOT: Add nodes for Endpoints/Listeners and link them to the Events/Transitions.
- In SCXML: Add metadata to transitions.

View File

@@ -1,73 +0,0 @@
# State Machine Instance Identification & Persistence Mapping
## 1. The Multi-SM Problem
In large applications, multiple State Machines often coexist. A "Trigger Point" (like a REST Controller) must be linked to the correct State Machine definition.
**Common Identification Patterns**:
- **Unique Types**: `StateMachine<OrderState, OrderEvent>` vs `StateMachine<UserState, UserEvent>`.
- **Bean Qualifiers**: `@Qualifier("orderStateMachine")`.
- **Factory IDs**: `factory.getStateMachine("order-123")`.
- **Persistence Restoration**: `persister.restore(sm, orderId)`.
## 2. Analysis Strategy: "Instance Tracking"
### 1. Definition Discovery (Existing)
Identify all configurations (classes with `@EnableStateMachineFactory` or `@EnableStateMachine`). Store their **Bean Names** and **Generic Types**.
### 2. Dependency Analysis
When a class (e.g., `OrderController`) uses a state machine:
1. **Identify the Field/Parameter**: Look for `StateMachine<S, E>`.
2. **Resolve Generic Types**: Match `S` and `E` against known SM definitions.
3. **Resolve Qualifiers**: Check for `@Qualifier` or variable names that match a SM bean name.
### 3. Loading & Persistence Analysis
A dedicated "Loading Detector" will look for persistence logic.
**Pattern: Persister Restore**
```java
persister.restore(stateMachine, id);
stateMachine.sendEvent(E);
```
**Static Strategy**:
- Find calls to `Persister.restore(sm, ...)` or `PersistStateChangeListener`.
- Link the variable `sm` to the restoration event.
- Mark the `TriggerPoint` as "Restored from Persistence".
**Pattern: Factory Creation**
```java
StateMachine sm = factory.getStateMachine(smId);
```
**Static Strategy**:
- Find `factory.getStateMachine(...)`.
- If the argument is a literal (e.g., `"order"`), map it to the SM definition named "order".
## 3. Implementation: `InstanceIdentifier`
We will introduce an `InstanceIdentifier` that works alongside the `ValueResolver`.
```java
public class InstanceIdentifier {
public StateMachineReference identify(VariableDeclaration var, CodebaseContext context) {
// 1. Check type generics
// 2. Check @Qualifier
// 3. Trace back to factory or persister calls
}
}
```
## 4. Modeling in `AnalysisResult`
The `TriggerPoint` will be enhanced with a `stateMachineId` or `configFqn` field.
```java
public record TriggerPoint(
String className,
String methodName,
String event,
String stateMachineId, // Links back to the specific SM
boolean isRestoredFromPersistence,
Map<String, String> metadata
) {}
```
## 5. Challenges
- **Generic Controllers**: A single base controller that handles multiple SMs via generics. We might need to report "Multiple Potential SMs".
- **Dynamic Factory IDs**: `factory.getStateMachine(payload.getType())`. Hard to resolve statically without data flow analysis.

View File

@@ -1,55 +0,0 @@
# Polyglot Analysis: Supporting Mixed Java and Kotlin
## 1. The Need for Language Abstraction
Professional JVM codebases are increasingly "Mixed Mode" (Java + Kotlin). Tying our analysis logic directly to JDT (Java AST) creates a bottleneck for Kotlin support.
## 2. The "Driver" Architecture
We decouple the **Intelligence Logic** (e.g., "Find all REST controllers") from the **AST Parser** (e.g., "Walk the JDT tree").
### 1. Language Driver Interface
```java
public interface LanguageDriver {
boolean supports(Path filePath);
// Unified Extraction Methods
List<RawClassMetadata> extractClasses(Path filePath);
List<RawMethodMetadata> extractMethods(RawClassMetadata cls);
List<RawInvocationMetadata> findInvocations(RawMethodMetadata method, String targetMethodName);
}
```
### 2. Unified Metadata Model (Intermediate Representation)
We move to a "Generic JVM Model" before performing the final state machine mapping.
```java
public record RawClassMetadata(
String fqn,
String language, // "java", "kotlin"
List<String> annotations,
String superclass,
List<String> interfaces
) {}
public record RawMethodMetadata(
String name,
List<String> annotations,
String returnType,
List<String> parameterTypes
) {}
```
## 3. Polyglot Analysis Flow
1. **File Discovery**: Find all `.java` and `.kt` files.
2. **Driver Assignment**: Delegate each file to the appropriate `LanguageDriver`.
3. **Cross-Language Resolution**: The `CodebaseContext` stores metadata for ALL classes.
- A Java controller calling a Kotlin service is resolved by looking up the Kotlin class's unified metadata.
4. **Enricher Execution**: Enrichers (like `SpringMvcEnricher`) now operate on the **Unified Metadata Model**, making them language-agnostic!
## 4. Why this is "Future Proof":
- **Enrichers are written once**: The logic to find `@PostMapping` only needs to know how to query the `RawClassMetadata` annotations, not how to traverse JDT vs. Kotlin PSI.
- **Easy Expansion**: To support Scala, Groovy, or even a newer Java version, you just add a new `LanguageDriver`.
## 5. Refactoring Plan
1. **Define Intermediate Models**: Create `RawClassMetadata` and `RawMethodMetadata`.
2. **Extract JDT Logic**: Move the current JDT-specific code into `JavaLanguageDriver`.
3. **Kotlin PoC (Phase 12)**: Implement a basic `KotlinLanguageDriver` using Regex or a lightweight parser (like Tree-Sitter) to prove the abstraction works.

View File

@@ -1,80 +0,0 @@
# Spring Property and Value Resolution Strategy
## 1. The Challenge of "Injected" Metadata
In Spring, metadata (like Queue names, Topic names, or even Event names) is often not hardcoded but injected using various mechanisms. To provide an accurate map, the analyzer must resolve these values.
## 2. Supported Injection Patterns
### Field Injection
```java
@Value("${app.order-queue}")
private String orderQueue;
```
**Static Strategy**:
1. Scan all fields for `@Value`.
2. Map `FieldName` -> `Placeholder/Value`.
3. If `${...}` is found, resolve it via the `PropertyResolver`.
### Constructor and Setter Injection
```java
public OrderService(@Value("${app.event.submit}") String submitEvent) {
this.submitEvent = submitEvent;
}
```
**Static Strategy**:
1. Scan constructor/method parameters for `@Value`.
2. Trace the assignment to a class field (`this.submitEvent = ...`).
3. Store the mapping for that field.
### @ConfigurationProperties
```java
@ConfigurationProperties(prefix = "app.messaging")
public class AppProps {
private String queueName;
}
```
**Static Strategy**:
1. Identify `@ConfigurationProperties` classes.
2. Index their fields with the prefix (e.g., `app.messaging.queueName`).
3. When these beans are injected into a service, link the service's usage to the resolved property value.
## 3. Profile-Aware Property Resolver
A component dedicated to building a multi-dimensional map of available properties.
1. **File Scanning**: Parse `application.properties`, `application.yml`, and `bootstrap.yml`.
2. **Profile Identification**:
- From filenames: `application-{profile}.properties`.
- From YAML documents: `spring.config.activate.on-profile` (Spring Boot 2.4+).
3. **The Multi-Profile Map**:
- Instead of one map, we store: `Map<ProfileName, Map<Key, Value>>`.
- The "default" profile is the base.
## 4. Smart Value Propagation
When a trigger uses a property, we want to know it's profile-dependent.
**Example Trace**:
1. `application.yml` -> `app.queue: "orders"`
2. `application-prod.yml` -> `app.queue: "orders-prod"`
3. `TriggerPoint` stores:
- `placeholder: "${app.queue}"`
- `defaultResolvedValue: "orders"`
4. **Rendering Strategy**: The renderer can show "orders" by default, but provide a "Profile Toggle" to switch to "prod" and see the labels update to "orders-prod".
## 5. Metadata Retention
We should never "squash" profiles during analysis. The `CodebaseMetadata` will carry the full profile matrix to the exporter.
## 5. SpEL (Spring Expression Language) Lite
Full SpEL support is hard for static analysis, but we can support "Common Patterns":
- Simple bean property access: `#{myBean.name}`
- System properties: `#{systemProperties['user.dir']}`
- Ternary operators: `${app.enabled ? 'active' : 'inactive'}`
## 6. Constant Resolution
If an annotation uses a reference to a constant:
```java
@RabbitListener(queues = MyConstants.ORDER_QUEUE)
```
**Static Strategy**:
1. Use JDT to resolve the `QualifiedName` (`MyConstants.ORDER_QUEUE`).
2. Fetch the `TypeDeclaration` for `MyConstants`.
3. Find the `VariableDeclarationFragment` for `ORDER_QUEUE` and extract its literal initializer.

View File

@@ -1,53 +0,0 @@
# Advanced String and Variable Resolution Strategy
## 1. The Problem
Annotations often use non-literal values for metadata:
- **Constants**: `@PostMapping(ApiConstants.SUBMIT_PATH)`
- **Concatenation**: `@RequestMapping(BASE + "/orders")`
- **Inherited Variables**: Use of fields defined in base classes.
Simple `StringLiteral` extraction fails in these cases. We need a recursive **Expression Evaluator**.
## 2. Evaluation Logic
### Constant Resolution
If an expression is a `SimpleName` or `QualifiedName`:
1. Use `CodebaseContext.getTypeDeclaration()` to find the class where the variable is defined.
2. Search for the `VariableDeclarationFragment`.
3. If it has an initializer that is a literal or another evaluatable expression, resolve it.
### Concatenation (Infix Expressions)
If an expression is an `InfixExpression` with the `+` operator:
1. Recursively resolve the left operand.
2. Recursively resolve the right operand.
3. Concatenate the results if both are strings.
### Spring Expression Language (SpEL) in Annotations
For values like `@Value("#{systemProperties['path.base'] + '/api'}")`:
1. Identify the SpEL string.
2. Use a "Lite" SpEL parser to extract keys.
3. Match against the `profiles` map in `CodebaseMetadata`.
## 3. Implementation: `ValueResolver`
We will introduce a central `ValueResolver` utility.
```java
public class ValueResolver {
public String resolveString(Expression expr, CodebaseContext context) {
if (expr instanceof StringLiteral sl) return sl.getLiteralValue();
if (expr instanceof InfixExpression ie) return resolveInfix(ie, context);
if (expr instanceof Name name) return resolveVariable(name, context);
// ... fallback to toString() or empty
}
}
```
## 4. Challenges
- **Circular Dependencies**: A + B, where B = A + C. We need a "visited" set to prevent infinite recursion.
- **Runtime-only values**: Some values cannot be resolved statically (e.g., values from a DB). In these cases, we should return the placeholder or a "Runtime Value" marker.
- **Method Calls**: `getPath() + "/orders"`. Resolving method return values is complex; initially, we will only support simple getters returning literals.
## 5. Integration
1. Update `AstUtils` or create `ValueResolver`.
2. Refactor `SpringMvcEnricher`, `RabbitMqEnricher`, and `JmsEnricher` to use the resolver for all path/queue/destination fields.
3. Update `integration_test_state_machine` with complex path examples.

View File

@@ -1,4 +1,5 @@
task runGolden(type: JavaExec) { task runGolden(type: JavaExec) {
mainClass = "click.kamil.springstatemachineexporter.GoldenUpdater" mainClass = "click.kamil.springstatemachineexporter.GoldenUpdater"
classpath = sourceSets.test.runtimeClasspath classpath = sourceSets.test.runtimeClasspath
workingDir = rootProject.projectDir
} }

View File

@@ -19,3 +19,6 @@ include ':state_machines:ultimate_ecosystem_sm'
include ':state_machines:enterprise_order_system' include ':state_machines:enterprise_order_system'
include ':state_machines:multi_module_sample:api-module' include ':state_machines:multi_module_sample:api-module'
include ':state_machines:multi_module_sample:core-module' include ':state_machines:multi_module_sample:core-module'
include ':state_machines:state_machine_enterprise'
include 'state_machine_exporter_kotlin'
include ':state_machines:kotlin_simple_state_machine'

View File

@@ -32,6 +32,9 @@ 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'
// Kotlin plugin integration via ServiceLoader
runtimeOnly project(':state_machine_exporter_kotlin')
// Jackson for JSON support // Jackson for JSON support
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' 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-jdk8:2.17.1'
@@ -58,10 +61,5 @@ tasks.named('test') {
task runGolden(type: JavaExec) { task runGolden(type: JavaExec) {
mainClass = "click.kamil.springstatemachineexporter.GoldenUpdater" mainClass = "click.kamil.springstatemachineexporter.GoldenUpdater"
classpath = sourceSets.test.runtimeClasspath classpath = sourceSets.test.runtimeClasspath
}
task runGoldenFixed(type: JavaExec) {
mainClass = "click.kamil.springstatemachineexporter.GoldenUpdater"
classpath = sourceSets.test.runtimeClasspath
workingDir = rootProject.projectDir workingDir = rootProject.projectDir
} }

View File

@@ -17,12 +17,12 @@ import java.util.stream.Collectors;
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine; import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanResolutionEngine; import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanResolutionEngine;
import click.kamil.springstatemachineexporter.analysis.enricher.matching.EventMatchingEngine; import click.kamil.springstatemachineexporter.analysis.enricher.matching.EventMatchingEngine;
import click.kamil.springstatemachineexporter.analysis.enricher.matching.HeuristicEventMatchingEngine; import click.kamil.springstatemachineexporter.analysis.enricher.matching.StrictFqnMatchingEngine;
public class TransitionLinkerEnricher implements AnalysisEnricher { public class TransitionLinkerEnricher implements AnalysisEnricher {
private final BeanResolutionEngine routingEngine = new HeuristicBeanResolutionEngine(); private final BeanResolutionEngine routingEngine = new HeuristicBeanResolutionEngine();
private final EventMatchingEngine matchingEngine = new HeuristicEventMatchingEngine(); private final EventMatchingEngine matchingEngine = new StrictFqnMatchingEngine();
@Override @Override
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) { public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
@@ -59,7 +59,8 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
.targetState(smSourceRaw) .targetState(smSourceRaw)
.event(smEventRaw) .event(smEventRaw)
.build(); .build();
if (isRoutedToCorrectMachine(chain, result.getName(), context)) { if (isRoutedToCorrectMachine(chain, result.getName(), context)
&& isConstraintCompatible(tp.getConstraint(), result.getName())) {
matched.add(mt); matched.add(mt);
} }
} else { } else {
@@ -70,7 +71,8 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
.targetState(targetRaw) .targetState(targetRaw)
.event(smEventRaw) .event(smEventRaw)
.build(); .build();
if (isRoutedToCorrectMachine(chain, result.getName(), context)) { if (isRoutedToCorrectMachine(chain, result.getName(), context)
&& isConstraintCompatible(tp.getConstraint(), result.getName())) {
matched.add(mt); matched.add(mt);
} }
} }
@@ -104,20 +106,128 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
return routingEngine.isRoutedToCorrectMachine(chain, currentMachineName, context); return routingEngine.isRoutedToCorrectMachine(chain, currentMachineName, context);
} }
private String simplify(String name) { private boolean isConstraintCompatible(String constraint, String machineName) {
if (constraint == null || machineName == null) return true;
String cleanMachine = machineName.substring(machineName.lastIndexOf('.') + 1).toUpperCase();
if (cleanMachine.endsWith("STATEMACHINECONFIGURATION")) {
cleanMachine = cleanMachine.substring(0, cleanMachine.length() - "STATEMACHINECONFIGURATION".length());
}
if (cleanMachine.endsWith("STATEMACHINE")) {
cleanMachine = cleanMachine.substring(0, cleanMachine.length() - "STATEMACHINE".length());
}
if (cleanMachine.endsWith("CONFIG")) {
cleanMachine = cleanMachine.substring(0, cleanMachine.length() - "CONFIG".length());
}
String expr = constraint;
java.util.regex.Pattern p = java.util.regex.Pattern.compile("[\"']([a-zA-Z0-9_-]+)[\"']");
java.util.regex.Matcher matcher = p.matcher(constraint);
java.util.Set<String> literals = new java.util.HashSet<>();
while (matcher.find()) {
literals.add(matcher.group(1));
}
for (String m : literals) {
boolean isCurrent = m.equalsIgnoreCase(cleanMachine);
String replacement = isCurrent ? "true" : "false";
String regex1 = "(?i)[\"']?" + m + "[\"']?\\s*\\.\\s*(equalsIgnoreCase|equals)\\s*\\([^)]*\\)";
String regex2 = "(?i)[a-zA-Z0-9._]+\\s*\\.\\s*(equalsIgnoreCase|equals)\\s*\\(\\s*[\"']?" + m + "[\"']?\\s*\\)";
String regex3 = "(?i)[a-zA-Z0-9._]+\\s*==\\s*[\"']?" + m + "[\"']?";
String regex4 = "(?i)[a-zA-Z0-9._]+\\s*!=\\s*[\"']?" + m + "[\"']?";
expr = expr.replaceAll(regex1, replacement);
expr = expr.replaceAll(regex2, replacement);
expr = expr.replaceAll(regex3, replacement);
expr = expr.replaceAll(regex4, isCurrent ? "false" : "true");
}
try {
return BooleanEvaluator.evaluate(expr);
} catch (Exception e) {
return !expr.contains("false");
}
}
private static class BooleanEvaluator {
public static boolean evaluate(String expr) {
expr = expr.replaceAll("\\s+", "");
return parseExpr(expr);
}
private static boolean parseExpr(String s) {
if (s.isEmpty()) return true;
int parenDepth = 0;
for (int i = s.length() - 1; i >= 0; i--) {
char c = s.charAt(i);
if (c == ')') parenDepth++;
else if (c == '(') parenDepth--;
else if (parenDepth == 0 && i > 0 && s.charAt(i) == '|' && s.charAt(i - 1) == '|') {
return parseExpr(s.substring(0, i - 1)) || parseExpr(s.substring(i + 1));
}
}
parenDepth = 0;
for (int i = s.length() - 1; i >= 0; i--) {
char c = s.charAt(i);
if (c == ')') parenDepth++;
else if (c == '(') parenDepth--;
else if (parenDepth == 0 && i > 0 && s.charAt(i) == '&' && s.charAt(i - 1) == '&') {
return parseExpr(s.substring(0, i - 1)) && parseExpr(s.substring(i + 1));
}
}
if (s.startsWith("!")) {
return !parseExpr(s.substring(1));
}
if (s.startsWith("(") && s.endsWith(")")) {
return parseExpr(s.substring(1, s.length() - 1));
}
if (s.equalsIgnoreCase("true")) return true;
if (s.equalsIgnoreCase("false")) return false;
return true;
}
}
private final java.util.Map<String, String> simplifyCache = new java.util.HashMap<>();
private final java.util.Map<java.util.Map.Entry<String, String>, Boolean> guardedCache = new java.util.HashMap<>();
private static final java.util.regex.Pattern SUFFIX_PATTERN = java.util.regex.Pattern.compile("(?i)(.+)(Event|Action|Transition|Command)(s)?$");
private static final java.util.regex.Pattern FQN_PATTERN = java.util.regex.Pattern.compile("^.*\\.([A-Z0-9_]+)$");
private String simplify(String name) {
if (name == null) return null; if (name == null) return null;
if (simplifyCache.containsKey(name)) return simplifyCache.get(name);
// Strip common suffixes // Strip common suffixes
String simplified = name.replaceAll("(?i)(.*)(Event|Action|Transition|Command)(s)?$", "$1"); String simplified = SUFFIX_PATTERN.matcher(name).replaceAll("$1");
if (simplified.isEmpty()) { if (simplified.isEmpty()) {
simplified = name; simplified = name;
} }
// Simplify full identifiers to just the last part (enum name) // Simplify full identifiers to just the last part (enum name)
simplified = simplified.replaceAll("^.*\\.([A-Z0-9_]+)$", "$1"); simplified = FQN_PATTERN.matcher(simplified).replaceAll("$1");
// For remaining full caps with underscores (like EVENT_X), keep as is or try to simplify
simplifyCache.put(name, simplified);
return simplified; return simplified;
} }
private boolean isGuardedContains(String smEvent, String triggerEvent) { private boolean isGuardedContains(String smEvent, String triggerEvent) {
if (smEvent == null || triggerEvent == null) return false;
// Use a strictly immutable pair to guarantee 100% collision-free caching regardless of string content
java.util.Map.Entry<String, String> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(smEvent, triggerEvent);
if (guardedCache.containsKey(cacheKey)) return guardedCache.get(cacheKey);
boolean result = calculateGuardedContains(smEvent, triggerEvent);
guardedCache.put(cacheKey, result);
return result;
}
private boolean calculateGuardedContains(String smEvent, String triggerEvent) {
// Only fire if it looks like a simple identifier (no spaces, no parens) // Only fire if it looks like a simple identifier (no spaces, no parens)
// Prevents unresolved AST nodes (e.g. "event.getPayload()") from accidentally matching "Event" // Prevents unresolved AST nodes (e.g. "event.getPayload()") from accidentally matching "Event"
if (smEvent.contains(" ") || smEvent.contains("(") || smEvent.contains(")") || if (smEvent.contains(" ") || smEvent.contains("(") || smEvent.contains(")") ||
@@ -135,7 +245,7 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
// Guard: If the matching part is very short (< 4 chars), it must be bounded by word separators // Guard: If the matching part is very short (< 4 chars), it must be bounded by word separators
// to avoid matching completely unrelated words (e.g., "PAY" matching "PAYMENT" or "E" matching "UPDATE") // to avoid matching completely unrelated words (e.g., "PAY" matching "PAYMENT" or "E" matching "UPDATE")
if (shorter.length() < 4) { if (shorter.length() < 4) {
return longer.matches(".*(^|_|-)" + shorter + "(_|-|$).*"); return longer.matches(".*(^|_|-)" + java.util.regex.Pattern.quote(shorter) + "(_|-|$).*");
} }
// For longer strings, a straight contains is usually safe enough // For longer strings, a straight contains is usually safe enough
return true; return true;

View File

@@ -6,11 +6,22 @@ import java.util.List;
public class HeuristicEventMatchingEngine implements EventMatchingEngine { public class HeuristicEventMatchingEngine implements EventMatchingEngine {
private final java.util.Map<java.util.Map.Entry<Event, TriggerPoint>, Boolean> matchesCache = new java.util.HashMap<>();
@Override @Override
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) { public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) { if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
return false; return false;
} }
java.util.Map.Entry<Event, TriggerPoint> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(stateMachineEvent, triggerPoint);
if (matchesCache.containsKey(cacheKey)) return matchesCache.get(cacheKey);
boolean result = calculateMatches(stateMachineEvent, triggerPoint);
matchesCache.put(cacheKey, result);
return result;
}
private boolean calculateMatches(Event stateMachineEvent, TriggerPoint triggerPoint) {
String rawTriggerEvent = triggerPoint.getEvent(); String rawTriggerEvent = triggerPoint.getEvent();
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName(); String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
@@ -58,7 +69,22 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
} }
if (hasPolyMatch) return true; if (hasPolyMatch) return true;
if (polyEvents.isEmpty() && isWildcard) return true; if (polyEvents.isEmpty() && isWildcard) {
String eventTypeFqn = triggerPoint.getEventTypeFqn();
if (eventTypeFqn != null && smEventRaw.contains(".")) {
String smEventClass = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
if (smEventClass.equals(eventTypeFqn)) {
return true;
}
String simpleEventType = eventTypeFqn.contains(".") ? eventTypeFqn.substring(eventTypeFqn.lastIndexOf('.') + 1) : eventTypeFqn;
String simpleSmClass = smEventClass.contains(".") ? smEventClass.substring(smEventClass.lastIndexOf('.') + 1) : smEventClass;
if (simpleEventType.equalsIgnoreCase(simpleSmClass)) {
return true;
}
return false;
}
return true;
}
if (rawTriggerEvent.contains(".") && smEventRaw.contains(".")) { if (rawTriggerEvent.contains(".") && smEventRaw.contains(".")) {
if (smEventRaw.equals(rawTriggerEvent) || smEventRaw.endsWith("." + rawTriggerEvent) || rawTriggerEvent.endsWith("." + smEventRaw)) { if (smEventRaw.equals(rawTriggerEvent) || smEventRaw.endsWith("." + rawTriggerEvent) || rawTriggerEvent.endsWith("." + smEventRaw)) {
@@ -87,8 +113,23 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
return isGuardedContains(smEvent, triggerEvent); return isGuardedContains(smEvent, triggerEvent);
} }
private final java.util.Map<String, String> simplifyCache = new java.util.HashMap<>();
private final java.util.Map<java.util.Map.Entry<String, String>, Boolean> guardedCache = new java.util.HashMap<>();
private static final java.util.regex.Pattern SUFFIX_PATTERN = java.util.regex.Pattern.compile("(?i)(.+)(Event|Action|Transition|Command)(s)?$");
private static final java.util.regex.Pattern FQN_PATTERN = java.util.regex.Pattern.compile("^.*\\.([A-Z0-9_]+)$");
private boolean isGuardedContains(String smEvent, String triggerEvent) { private boolean isGuardedContains(String smEvent, String triggerEvent) {
if (smEvent == null || triggerEvent == null) return false; if (smEvent == null || triggerEvent == null) return false;
java.util.Map.Entry<String, String> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(smEvent, triggerEvent);
if (guardedCache.containsKey(cacheKey)) return guardedCache.get(cacheKey);
boolean result = calculateGuardedContains(smEvent, triggerEvent);
guardedCache.put(cacheKey, result);
return result;
}
private boolean calculateGuardedContains(String smEvent, String triggerEvent) {
// Only fire if it looks like a simple identifier (no spaces, no parens) // Only fire if it looks like a simple identifier (no spaces, no parens)
// Prevents unresolved AST nodes (e.g. "event.getPayload()") from accidentally matching "Event" // Prevents unresolved AST nodes (e.g. "event.getPayload()") from accidentally matching "Event"
if (smEvent.contains(" ") || smEvent.contains("(") || smEvent.contains(")") || if (smEvent.contains(" ") || smEvent.contains("(") || smEvent.contains(")") ||
@@ -116,18 +157,36 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
} }
private boolean isWildcardVariable(String eventStr) { private boolean isWildcardVariable(String eventStr) {
return eventStr.equals("event") || eventStr.equals("e") || if (eventStr == null) return false;
eventStr.equals("msg") || eventStr.equals("message") ||
eventStr.equals("payload"); if (eventStr.equals("event") || eventStr.equals("e") ||
eventStr.equals("msg") || eventStr.equals("message") ||
eventStr.equals("payload")) {
return true;
}
if (eventStr.contains(".valueOf(") || eventStr.contains("valueOf (")) {
return true;
}
if (eventStr.contains(" ? ") && eventStr.contains(" : ")) {
return true;
}
return false;
} }
private String simplify(String name) { private String simplify(String name) {
if (name == null) return null; if (name == null) return null;
String simplified = name.replaceAll("(?i)(.*)(Event|Action|Transition|Command)(s)?$", "$1"); if (simplifyCache.containsKey(name)) return simplifyCache.get(name);
String simplified = SUFFIX_PATTERN.matcher(name).replaceAll("$1");
if (simplified.isEmpty()) { if (simplified.isEmpty()) {
simplified = name; simplified = name;
} }
simplified = simplified.replaceAll("^.*\\.([A-Z0-9_]+)$", "$1"); simplified = FQN_PATTERN.matcher(simplified).replaceAll("$1");
simplifyCache.put(name, simplified);
return simplified; return simplified;
} }
} }

View File

@@ -0,0 +1,150 @@
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.model.Event;
import java.util.List;
public class StrictFqnMatchingEngine implements EventMatchingEngine {
@Override
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
return false;
}
if (triggerPoint.isExternal()) {
return true;
}
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
String rawTriggerEvent = triggerPoint.getEvent();
if (rawTriggerEvent != null && rawTriggerEvent.startsWith("<SYMBOLIC: ") && rawTriggerEvent.endsWith(".*>")) {
String targetType = rawTriggerEvent.substring(11, rawTriggerEvent.length() - 3);
if (smEventRaw.startsWith(targetType + ".")) {
return true;
}
if (smEventRaw.contains(".")) {
String smType = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
if (smType.endsWith("." + targetType) || smType.equals(targetType)) {
return true;
}
}
return false;
}
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
for (String pe : polyEvents) {
if (matchEventNames(pe, smEventRaw, triggerPoint.getEventTypeFqn())) {
return true;
}
if (pe.startsWith("<SYMBOLIC: ") && pe.endsWith(".*>")) {
String targetType = pe.substring(11, pe.length() - 3);
if (smEventRaw.startsWith(targetType + ".")) {
return true;
}
if (smEventRaw.contains(".")) {
String smType = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
if (smType.endsWith("." + targetType) || smType.equals(targetType)) {
return true;
}
}
}
}
if (rawTriggerEvent != null && rawTriggerEvent.contains(".valueOf(")) {
String enumClass = rawTriggerEvent.substring(0, rawTriggerEvent.indexOf(".valueOf("));
if (enumClass.contains(" ")) {
enumClass = enumClass.substring(enumClass.lastIndexOf(" ") + 1);
}
String smClass = smEventRaw.contains(".") ? smEventRaw.substring(0, smEventRaw.lastIndexOf('.')) : smEventRaw;
if (enumClass.equals(smClass) || enumClass.endsWith("." + smClass) || smClass.endsWith("." + enumClass)) {
return true;
}
return false;
}
if (polyEvents.isEmpty() && isDynamicVariable(rawTriggerEvent)) {
if (triggerPoint.getEventTypeFqn() != null) {
if (smEventRaw.startsWith(triggerPoint.getEventTypeFqn() + ".")) {
return true;
}
if (isStringTypeOrPrimitive(triggerPoint.getEventTypeFqn()) && !smEventRaw.contains(".")) {
return true;
}
return false;
}
return true;
}
return matchEventNames(rawTriggerEvent, smEventRaw, triggerPoint.getEventTypeFqn());
}
private boolean matchEventNames(String triggerEvent, String smEvent, String eventTypeFqn) {
if (triggerEvent == null || smEvent == null) return false;
if (triggerEvent.equals(smEvent)) return true;
String tConst = triggerEvent.contains(".") ? triggerEvent.substring(triggerEvent.lastIndexOf('.') + 1) : triggerEvent;
String smConst = smEvent.contains(".") ? smEvent.substring(smEvent.lastIndexOf('.') + 1) : smEvent;
if (!tConst.equals(smConst)) return false;
// Prevent matching string/primitive triggers to enum transitions
if (isStringTypeOrPrimitive(eventTypeFqn) && smEvent.contains(".")) {
return false;
}
// Prevent matching enum triggers to string transitions
if (!smEvent.contains(".") && triggerEvent.contains(".")) {
return false;
}
if (eventTypeFqn != null && eventTypeFqn.contains(".") && smEvent.contains(".")) {
String fullTriggerFqn = eventTypeFqn + "." + tConst;
return fullTriggerFqn.equals(smEvent);
}
if (triggerEvent.contains(".") && smEvent.contains(".")) {
String tClass = triggerEvent.substring(0, triggerEvent.lastIndexOf('.'));
String smClass = smEvent.substring(0, smEvent.lastIndexOf('.'));
String tClassSimple = tClass.contains(".") ? tClass.substring(tClass.lastIndexOf('.') + 1) : tClass;
String smClassSimple = smClass.contains(".") ? smClass.substring(smClass.lastIndexOf('.') + 1) : smClass;
return tClassSimple.equals(smClassSimple);
}
return true;
}
private boolean isStringTypeOrPrimitive(String typeFqn) {
if (typeFqn == null) return false;
return typeFqn.equals("String") || typeFqn.equals("java.lang.String") ||
typeFqn.equals("int") || typeFqn.equals("long") || typeFqn.equals("char");
}
private boolean isDynamicVariable(String eventStr) {
if (eventStr == null || eventStr.isEmpty()) return false;
if (eventStr.contains(" ? ") && eventStr.contains(" : ")) {
return true;
}
if (eventStr.endsWith("()")) {
return true;
}
if (eventStr.contains(".")) {
String firstPart = eventStr.substring(0, eventStr.indexOf('.'));
if (!firstPart.isEmpty() && Character.isLowerCase(firstPart.charAt(0))) {
return true;
}
return false;
}
if (Character.isLowerCase(eventStr.charAt(0))) {
return true;
}
return false;
}
}

View File

@@ -43,9 +43,11 @@ public class SymbolicPathValueEstimator implements PathValueEstimator {
click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver resolver = new click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver(); click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver resolver = new click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver();
String callerVal = resolver.resolve(caller, context); if (caller != null) {
if (callerVal != null && !callerVal.isEmpty()) { String callerVal = resolver.resolve(caller, context);
addValue(collectedConstants, callerVal); if (callerVal != null && !callerVal.isEmpty()) {
addValue(collectedConstants, callerVal);
}
} }
String argVal = resolver.resolve(arg, context); String argVal = resolver.resolve(arg, context);

View File

@@ -364,6 +364,7 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
if (type1 == null || type2 == null) return false; if (type1 == null || type2 == null) return false;
type1 = eraseGenerics(type1); type1 = eraseGenerics(type1);
type2 = eraseGenerics(type2); type2 = eraseGenerics(type2);
if (type1.length() == 1 || type2.length() == 1) return false;
if (type1.equals("java.lang.String") || type1.equals("String") || type1.equals("java.lang.Object") || type1.equals("Object")) return false; if (type1.equals("java.lang.String") || type1.equals("String") || type1.equals("java.lang.Object") || type1.equals("Object")) return false;
if (type2.equals("java.lang.String") || type2.equals("String") || type2.equals("java.lang.Object") || type2.equals("Object")) return false; if (type2.equals("java.lang.String") || type2.equals("String") || type2.equals("java.lang.Object") || type2.equals("Object")) return false;
return !type1.equals(type2); return !type1.equals(type2);

View File

@@ -13,8 +13,13 @@ public class CallEdge {
private String targetMethod; private String targetMethod;
private List<String> arguments; private List<String> arguments;
private String receiver; private String receiver;
private String constraint;
public CallEdge(String targetMethod, List<String> arguments) { public CallEdge(String targetMethod, List<String> arguments) {
this(targetMethod, arguments, null); this(targetMethod, arguments, null, null);
}
public CallEdge(String targetMethod, List<String> arguments, String receiver) {
this(targetMethod, arguments, receiver, null);
} }
} }

View File

@@ -25,4 +25,106 @@ public class TriggerPoint {
@com.fasterxml.jackson.annotation.JsonIgnore @com.fasterxml.jackson.annotation.JsonIgnore
private final String eventTypeFqn; // Type of Event (e.g. OrderEvents FQN) private final String eventTypeFqn; // Type of Event (e.g. OrderEvents FQN)
private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism
private final boolean external;
private final String constraint;
private final boolean ambiguous;
public TriggerPoint(
String event,
String className,
String methodName,
String sourceFile,
String sourceModule,
String stateMachineId,
String sourceState,
int lineNumber,
String stateTypeFqn,
String eventTypeFqn,
java.util.List<String> polymorphicEvents,
boolean external,
String constraint,
boolean ambiguous) {
this.className = className;
this.methodName = methodName;
this.sourceFile = sourceFile;
this.sourceModule = sourceModule;
this.stateMachineId = stateMachineId;
this.sourceState = sourceState;
this.lineNumber = lineNumber;
this.stateTypeFqn = stateTypeFqn;
this.eventTypeFqn = eventTypeFqn;
this.event = qualifyEvent(event, eventTypeFqn);
if (polymorphicEvents != null) {
this.polymorphicEvents = polymorphicEvents.stream()
.map(pe -> qualifyEvent(pe, eventTypeFqn))
.collect(java.util.stream.Collectors.toList());
} else {
this.polymorphicEvents = null;
}
this.external = external;
this.constraint = constraint;
this.ambiguous = ambiguous;
}
private static String qualifyEvent(String e, String eventTypeFqn) {
if (e == null || eventTypeFqn == null || e.isEmpty()) {
return e;
}
if (e.startsWith("<SYMBOLIC: ")) {
return e;
}
if (isWildcardVariable(e)) {
return e;
}
if (Character.isLowerCase(e.charAt(0))) {
return e;
}
if (!isEnumConstantCandidate(e)) {
return e;
}
String simpleEventType = eventTypeFqn.contains(".") ? eventTypeFqn.substring(eventTypeFqn.lastIndexOf('.') + 1) : eventTypeFqn;
if (e.contains(".")) {
String qualifier = e.substring(0, e.lastIndexOf('.'));
String lastSegment = e.substring(e.lastIndexOf('.') + 1);
String simpleQualifier = qualifier.contains(".") ? qualifier.substring(qualifier.lastIndexOf('.') + 1) : qualifier;
if (simpleQualifier.equals(simpleEventType)) {
return simpleEventType + "." + lastSegment;
}
return e;
}
if (eventTypeFqn.startsWith("java.lang.") || eventTypeFqn.equals("String") || eventTypeFqn.equals("int") || eventTypeFqn.equals("long") || eventTypeFqn.equals("char")) {
return e;
}
return simpleEventType + "." + e;
}
private static boolean isEnumConstantCandidate(String eventStr) {
if (eventStr == null) return false;
if (eventStr.startsWith("<SYMBOLIC: ")) {
return false;
}
if (eventStr.contains(" ") || eventStr.contains("(") || eventStr.contains(")") || eventStr.contains("?")) {
return false;
}
return true;
}
private static boolean isWildcardVariable(String eventStr) {
if (eventStr == null) return false;
if (eventStr.equals("event") || eventStr.equals("e") ||
eventStr.equals("msg") || eventStr.equals("message") ||
eventStr.equals("payload")) {
return true;
}
if (eventStr.contains(".valueOf(") || eventStr.contains("valueOf (")) {
return true;
}
if (eventStr.contains(" ? ") && eventStr.contains(" : ")) {
return true;
}
return false;
}
} }

View File

@@ -6,6 +6,9 @@ import org.eclipse.jdt.core.dom.*;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.LinkedHashSet;
import java.util.Arrays;
import java.util.List;
@Slf4j @Slf4j
public class ConstantResolver { public class ConstantResolver {
@@ -93,28 +96,43 @@ public class ConstantResolver {
// Fallback for unresolved AST nodes // Fallback for unresolved AST nodes
if (mi.getExpression() instanceof QualifiedName qn) { if (mi.getExpression() instanceof QualifiedName qn) {
System.out.println("RETURNING qn name: " + qn.getName().getIdentifier()); return qn.getName().getIdentifier(); return qn.getName().getIdentifier();
} else if (mi.getExpression() instanceof SimpleName sn) { } else if (mi.getExpression() instanceof SimpleName sn) {
System.out.println("RETURNING sn name: " + sn.getIdentifier()); return sn.getIdentifier(); return sn.getIdentifier();
} }
} }
if ("valueOf".equals(mi.getName().getIdentifier()) && mi.getExpression() != null) { if ("valueOf".equals(mi.getName().getIdentifier())) {
java.util.List<String> enumValues = context.getEnumValues(mi.getExpression().toString()); String enumFqn = null;
if (enumValues != null && !enumValues.isEmpty()) { Expression arg = null;
return "ENUM_SET:" + String.join(",", enumValues); if (mi.arguments().size() == 2) {
enumFqn = resolveEnumFqn((Expression) mi.arguments().get(0), context);
arg = (Expression) mi.arguments().get(1);
} else if (mi.arguments().size() == 1 && mi.getExpression() != null) {
enumFqn = resolveEnumFqn(mi.getExpression(), context);
arg = (Expression) mi.arguments().get(0);
}
if (enumFqn != null) {
if (arg != null) {
String resolvedArg = resolveInternal(arg, context, visited);
if (resolvedArg != null && resolvedArg.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
return enumFqn + "." + resolvedArg;
}
}
return "<SYMBOLIC: " + enumFqn + ".*>";
} }
} }
IMethodBinding mb = mi.resolveMethodBinding(); IMethodBinding mb = mi.resolveMethodBinding();
if (mb != null) { if (mb != null) {
ITypeBinding returnType = mb.getReturnType(); if (!java.lang.reflect.Modifier.isStatic(mb.getModifiers())) {
if (returnType != null) { Expression receiver = mi.getExpression();
java.util.List<String> values = context.getEnumValues(returnType.getQualifiedName()); if (!(receiver instanceof ClassInstanceCreation)) {
if (values != null && !values.isEmpty()) { return null;
return "ENUM_SET:" + String.join(",", values);
} }
} }
} else { } else {
TypeDeclaration td = null; TypeDeclaration td = null;
if (mi.getExpression() != null) { if (mi.getExpression() != null) {
@@ -147,8 +165,7 @@ public class ConstantResolver {
if (md != null) { if (md != null) {
String evaluated = evaluateMethodOutput(mi, md, context, visited); String evaluated = evaluateMethodOutput(mi, md, context, visited);
if (evaluated != null) return evaluated; if (evaluated != null) return evaluated;
if (mi.getName().getIdentifier().startsWith("get") && md.getReturnType2() != null) {
if (md.getReturnType2() != null) {
java.util.List<String> values = context.getEnumValues(md.getReturnType2().toString()); java.util.List<String> values = context.getEnumValues(md.getReturnType2().toString());
if (values != null && !values.isEmpty()) { if (values != null && !values.isEmpty()) {
return "ENUM_SET:" + String.join(",", values); return "ENUM_SET:" + String.join(",", values);
@@ -158,7 +175,6 @@ public class ConstantResolver {
} }
} }
} }
return null; return null;
} }
@@ -223,9 +239,8 @@ public class ConstantResolver {
} }
try { try {
java.util.Map<String, String> localVars = new java.util.HashMap<>(prebuiltLocals);
java.util.Set<String> declaredLocals = new java.util.HashSet<>(prebuiltLocals.keySet()); java.util.Set<String> declaredLocals = new java.util.HashSet<>(prebuiltLocals.keySet());
return evaluateBody(md.getBody(), localVars, declaredLocals, context, visited); return evaluateBody(md.getBody(), prebuiltLocals, declaredLocals, context, visited);
} finally { } finally {
visited.remove(methodFqn); visited.remove(methodFqn);
} }
@@ -251,15 +266,61 @@ public class ConstantResolver {
String varName = fragment.getName().getIdentifier(); String varName = fragment.getName().getIdentifier();
declaredLocals.add(varName); declaredLocals.add(varName);
if (fragment.getInitializer() != null) { if (fragment.getInitializer() != null) {
String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars); String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars, context);
if (rhsVal == null) rhsVal = resolveInternal(fragment.getInitializer(), context, visited); if (rhsVal == null) rhsVal = resolveInternal(fragment.getInitializer(), context, visited);
if (rhsVal != null) localVars.put(varName, rhsVal); if (rhsVal != null) {
localVars.put(varName, rhsVal);
} else {
java.util.Map<String, String> bFields = extractBuilderFields(fragment.getInitializer(), localVars, context, visited);
if (bFields != null) {
for (java.util.Map.Entry<String, String> e : bFields.entrySet()) {
localVars.put(varName + "." + e.getKey(), e.getValue());
}
}
}
} }
} }
} }
return super.visit(vds); return super.visit(vds);
} }
@Override
public boolean visit(org.eclipse.jdt.core.dom.MethodInvocation node) {
MethodDeclaration sideEffectMd = findInvokedMethod(node, context);
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
if (entry.getKey().startsWith("this.")) {
inlineLocals.put(entry.getKey(), entry.getValue());
} else if (context.hasField(currentTd, entry.getKey())) {
inlineLocals.put(entry.getKey(), entry.getValue());
inlineLocals.put("this." + entry.getKey(), entry.getValue());
}
}
for (int i = 0; i < node.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
Expression arg = (Expression) node.arguments().get(i);
String val = resolveExpressionWithParams(arg, localVars, context);
if (val == null) val = resolveInternal(arg, context, visited);
if (val != null) {
org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
inlineLocals.put(param.getName().getIdentifier(), val);
}
}
evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, visited);
for (java.util.Map.Entry<String, String> entry : inlineLocals.entrySet()) {
if (entry.getKey().startsWith("this.")) {
localVars.put(entry.getKey(), entry.getValue());
String bareName = entry.getKey().substring(5);
if (!declaredLocals.contains(bareName)) localVars.put(bareName, entry.getValue());
}
}
}
}
return super.visit(node);
}
@Override @Override
public boolean visit(Assignment assignment) { public boolean visit(Assignment assignment) {
Expression lhs = assignment.getLeftHandSide(); Expression lhs = assignment.getLeftHandSide();
@@ -269,9 +330,19 @@ public class ConstantResolver {
} else if (lhs instanceof FieldAccess fa) { } else if (lhs instanceof FieldAccess fa) {
varName = "this." + fa.getName().getIdentifier(); varName = "this." + fa.getName().getIdentifier();
} }
String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars); if (varName != null) {
if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited); String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars, context);
if (varName != null && rhsVal != null) { if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited);
if (rhsVal != null) {
localVars.put(varName, rhsVal);
} else {
java.util.Map<String, String> bFields = extractBuilderFields(assignment.getRightHandSide(), localVars, context, visited);
if (bFields != null) {
for (java.util.Map.Entry<String, String> e : bFields.entrySet()) {
localVars.put(varName + "." + e.getKey(), e.getValue());
}
}
}
if (varName.startsWith("this.")) { if (varName.startsWith("this.")) {
localVars.put(varName, rhsVal); localVars.put(varName, rhsVal);
String bareName = varName.substring(5); String bareName = varName.substring(5);
@@ -300,7 +371,7 @@ public class ConstantResolver {
String result = evaluateSwitchExpression(se, localVars, context, visited); String result = evaluateSwitchExpression(se, localVars, context, visited);
if (result != null) finalResult[0] = result; if (result != null) finalResult[0] = result;
} else if (rs.getExpression() != null) { } else if (rs.getExpression() != null) {
String result = resolveExpressionWithParams(rs.getExpression(), localVars); String result = resolveExpressionWithParams(rs.getExpression(), localVars, context);
if (result == null) result = resolveInternal(rs.getExpression(), context, visited); if (result == null) result = resolveInternal(rs.getExpression(), context, visited);
if (result != null) finalResult[0] = result; if (result != null) finalResult[0] = result;
} }
@@ -313,8 +384,44 @@ public class ConstantResolver {
} }
private String evaluateSwitchStatement(org.eclipse.jdt.core.dom.SwitchStatement ss, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) { private String evaluateSwitchStatement(org.eclipse.jdt.core.dom.SwitchStatement ss, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
String switchVar = resolveExpressionWithParams(ss.getExpression(), paramValues); String switchVar = resolveExpressionWithParams(ss.getExpression(), paramValues, context);
if (switchVar == null) return null; if (switchVar == null) {
Set<String> values = new LinkedHashSet<>();
for (Object stmtObj : ss.statements()) {
if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs && rs.getExpression() != null) {
String val = resolveInternal(rs.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
} else if (stmtObj instanceof org.eclipse.jdt.core.dom.YieldStatement ys && ys.getExpression() != null) {
String val = resolveInternal(ys.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
} else if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es && es.getExpression() != null) {
String val = resolveInternal(es.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
}
}
if (!values.isEmpty()) {
return "ENUM_SET:" + String.join(",", values);
}
return null;
}
String switchType = null; String switchType = null;
if (switchVar.contains(".")) { if (switchVar.contains(".")) {
@@ -329,12 +436,12 @@ public class ConstantResolver {
matchingCase = true; matchingCase = true;
} else { } else {
for (Object exprObj : sc.expressions()) { for (Object exprObj : sc.expressions()) {
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues); String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context);
// Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback // Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback
if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) { if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
caseVal = caseSn.getIdentifier(); caseVal = caseSn.getIdentifier();
if (switchType != null) { if (switchType != null) {
caseVal = switchType + "." + caseVal; caseVal = switchType + "." + caseVal;
} }
} }
if (caseVal != null) { if (caseVal != null) {
@@ -364,8 +471,44 @@ public class ConstantResolver {
} }
private String evaluateSwitchExpression(org.eclipse.jdt.core.dom.SwitchExpression se, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) { private String evaluateSwitchExpression(org.eclipse.jdt.core.dom.SwitchExpression se, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
String switchVar = resolveExpressionWithParams(se.getExpression(), paramValues); String switchVar = resolveExpressionWithParams(se.getExpression(), paramValues, context);
if (switchVar == null) return null; if (switchVar == null) {
Set<String> values = new LinkedHashSet<>();
for (Object stmtObj : se.statements()) {
if (stmtObj instanceof org.eclipse.jdt.core.dom.YieldStatement ys && ys.getExpression() != null) {
String val = resolveInternal(ys.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
} else if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es && es.getExpression() != null) {
String val = resolveInternal(es.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
} else if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs && rs.getExpression() != null) {
String val = resolveInternal(rs.getExpression(), context, visited);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
values.addAll(Arrays.asList(val.substring(9).split(",")));
} else {
values.add(val);
}
}
}
}
if (!values.isEmpty()) {
return "ENUM_SET:" + String.join(",", values);
}
return null;
}
String switchType = null; String switchType = null;
if (switchVar.contains(".")) { if (switchVar.contains(".")) {
@@ -380,7 +523,7 @@ public class ConstantResolver {
matchingCase = true; matchingCase = true;
} else { } else {
for (Object exprObj : sc.expressions()) { for (Object exprObj : sc.expressions()) {
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues); String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context);
// Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback // Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback
if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) { if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
caseVal = caseSn.getIdentifier(); caseVal = caseSn.getIdentifier();
@@ -414,7 +557,7 @@ public class ConstantResolver {
return null; return null;
} }
private String resolveExpressionWithParams(Expression expr, java.util.Map<String, String> paramValues) { private String resolveExpressionWithParams(Expression expr, java.util.Map<String, String> paramValues, CodebaseContext context) {
if (expr instanceof SimpleName sn) { if (expr instanceof SimpleName sn) {
String name = sn.getIdentifier(); String name = sn.getIdentifier();
if (paramValues.containsKey(name)) return paramValues.get(name); if (paramValues.containsKey(name)) return paramValues.get(name);
@@ -427,10 +570,107 @@ public class ConstantResolver {
return null; return null;
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) { } else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
return sl.getLiteralValue(); return sl.getLiteralValue();
} } else if (expr instanceof MethodInvocation mi) {
if ("valueOf".equals(mi.getName().getIdentifier())) {
Expression arg = null;
if (mi.arguments().size() == 1) {
arg = (Expression) mi.arguments().get(0);
} else if (mi.arguments().size() == 2) {
arg = (Expression) mi.arguments().get(1);
}
if (arg != null) {
String resolvedArg = resolveExpressionWithParams(arg, paramValues, context);
if (resolvedArg != null && resolvedArg.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
String enumFqn = null;
if (mi.arguments().size() == 2) {
enumFqn = resolveEnumFqn((Expression) mi.arguments().get(0), context);
} else if (mi.arguments().size() == 1 && mi.getExpression() != null) {
enumFqn = resolveEnumFqn(mi.getExpression(), context);
}
if (enumFqn != null) {
return enumFqn + "." + resolvedArg;
} else {
// If we can't resolve the exact enum type here, we can just return the bare constant
return resolvedArg;
}
}
}
} else {
if (mi.arguments().isEmpty() && mi.getExpression() instanceof SimpleName sn) {
String objName = sn.getIdentifier();
String methodName = mi.getName().getIdentifier();
String propName = null;
if (methodName.startsWith("get") && methodName.length() > 3) {
propName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
} else {
propName = methodName;
}
String val = paramValues.get(objName + "." + propName);
if (val == null && !propName.equals(methodName)) {
val = paramValues.get(objName + "." + methodName);
}
if (val != null) return val;
}
MethodDeclaration sideEffectMd = findInvokedMethod(mi, context);
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
for (java.util.Map.Entry<String, String> entry : paramValues.entrySet()) {
if (entry.getKey().startsWith("this.")) {
inlineLocals.put(entry.getKey(), entry.getValue());
} else if (context.hasField(currentTd, entry.getKey())) {
inlineLocals.put(entry.getKey(), entry.getValue());
inlineLocals.put("this." + entry.getKey(), entry.getValue());
}
}
for (int i = 0; i < mi.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
Expression arg = (Expression) mi.arguments().get(i);
String val = resolveExpressionWithParams(arg, paramValues, context);
if (val == null) val = resolveInternal(arg, context, new java.util.HashSet<>());
if (val != null) {
org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
inlineLocals.put(param.getName().getIdentifier(), val);
}
}
String ret = evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, new java.util.HashSet<>());
for (java.util.Map.Entry<String, String> entry : inlineLocals.entrySet()) {
if (entry.getKey().startsWith("this.")) {
paramValues.put(entry.getKey(), entry.getValue());
String bareName = entry.getKey().substring(5);
paramValues.put(bareName, entry.getValue());
}
}
return ret;
}
}
}
return null; return null;
} }
private java.util.Map<String, String> extractBuilderFields(Expression expr, java.util.Map<String, String> localVars, CodebaseContext context, Set<String> visited) {
if (!(expr instanceof MethodInvocation mi)) return null;
java.util.Map<String, String> fields = new java.util.HashMap<>();
MethodInvocation current = mi;
while (current != null) {
String methodName = current.getName().getIdentifier();
if (!"build".equals(methodName) && !"builder".equals(methodName) && !"toBuilder".equals(methodName)) {
if (current.arguments().size() == 1) {
Expression arg = (Expression) current.arguments().get(0);
String val = resolveExpressionWithParams(arg, localVars, context);
if (val == null) val = resolveInternal(arg, context, visited);
if (val != null) fields.put(methodName, val);
}
}
if (current.getExpression() instanceof MethodInvocation next) {
current = next;
} else {
break;
}
}
return fields.isEmpty() ? null : fields;
}
private String resolveInfix(InfixExpression expr, CodebaseContext context, Set<String> visited) { private String resolveInfix(InfixExpression expr, CodebaseContext context, Set<String> visited) {
if (expr.getOperator() != InfixExpression.Operator.PLUS) return null; if (expr.getOperator() != InfixExpression.Operator.PLUS) return null;
@@ -558,11 +798,50 @@ public class ConstantResolver {
} }
} }
} }
// Check superclass
if (td.getSuperclassType() != null) {
String superclassName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(td.getSuperclassType());
String resolvedSuperclass = resolveTypeFqn(superclassName, context, td);
if (resolvedSuperclass != null) {
TypeDeclaration superTd = context.getTypeDeclaration(resolvedSuperclass);
if (superTd != null) {
String result = resolveFieldInType(superTd, fieldName, resolvedSuperclass, context, visited);
if (result != null) return result;
}
}
}
// Check superinterfaces
for (Object intfObj : td.superInterfaceTypes()) {
Type intfType = (Type) intfObj;
String intfName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(intfType);
String resolvedIntf = resolveTypeFqn(intfName, context, td);
if (resolvedIntf != null) {
TypeDeclaration intfTd = context.getTypeDeclaration(resolvedIntf);
if (intfTd != null) {
String result = resolveFieldInType(intfTd, fieldName, resolvedIntf, context, visited);
if (result != null) return result;
}
}
}
} finally { } finally {
visited.remove(fieldId); visited.remove(fieldId);
} }
return null; return null;
} }
private String resolveTypeFqn(String simpleName, CodebaseContext context, TypeDeclaration td) {
CompilationUnit cu = (CompilationUnit) td.getRoot();
for (Object impObj : cu.imports()) {
ImportDeclaration imp = (ImportDeclaration) impObj;
String name = imp.getName().getFullyQualifiedName();
if (name.endsWith("." + simpleName)) {
return name;
}
}
return cu.getPackage().getName().getFullyQualifiedName() + "." + simpleName;
}
private String findValueFromAnnotation(FieldDeclaration fd) { private String findValueFromAnnotation(FieldDeclaration fd) {
for (Object mod : fd.modifiers()) { for (Object mod : fd.modifiers()) {
@@ -597,11 +876,29 @@ public class ConstantResolver {
} }
private TypeDeclaration findEnclosingType(ASTNode node) { private TypeDeclaration findEnclosingType(ASTNode node) {
ASTNode parent = node.getParent(); ASTNode current = node;
while (parent != null && !(parent instanceof TypeDeclaration)) { while (current != null && !(current instanceof TypeDeclaration)) {
parent = parent.getParent(); current = current.getParent();
} }
return (TypeDeclaration) parent; return (TypeDeclaration) current;
}
private MethodDeclaration findInvokedMethod(org.eclipse.jdt.core.dom.MethodInvocation mi, CodebaseContext context) {
org.eclipse.jdt.core.dom.IMethodBinding methodBinding = mi.resolveMethodBinding();
if (methodBinding != null) {
org.eclipse.jdt.core.dom.ITypeBinding declaringClass = methodBinding.getDeclaringClass();
if (declaringClass != null) {
TypeDeclaration targetTd = context.getTypeDeclaration(declaringClass.getErasure().getQualifiedName());
if (targetTd != null) {
return context.findMethodDeclaration(targetTd, mi.getName().getIdentifier(), false);
}
}
}
TypeDeclaration currentTd = findEnclosingType(mi);
if (currentTd != null && (mi.getExpression() == null || mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression)) {
return context.findMethodDeclaration(currentTd, mi.getName().getIdentifier(), true);
}
return null;
} }
private MethodDeclaration findEnclosingMethod(ASTNode node) { private MethodDeclaration findEnclosingMethod(ASTNode node) {
@@ -648,4 +945,81 @@ public class ConstantResolver {
} }
return null; return null;
} }
private String resolveEnumFqn(Expression expr, CodebaseContext context) {
if (expr == null) return null;
CompilationUnit contextCu = (expr.getRoot() instanceof CompilationUnit) ? (CompilationUnit) expr.getRoot() : null;
if (expr instanceof TypeLiteral tl) {
ITypeBinding binding = tl.getType().resolveBinding();
if (binding != null) {
return binding.getQualifiedName();
}
String typeName = tl.getType().toString();
if (typeName.endsWith(".class")) {
typeName = typeName.substring(0, typeName.length() - 6);
}
String resolved = resolveTypeNameToFqn(typeName, contextCu, context);
if (resolved != null) return resolved;
return typeName;
}
ITypeBinding binding = expr.resolveTypeBinding();
if (binding != null) {
if (binding.isClass() && "java.lang.Class".equals(binding.getErasure().getQualifiedName())) {
ITypeBinding[] typeArgs = binding.getTypeArguments();
if (typeArgs.length > 0) {
return typeArgs[0].getQualifiedName();
}
}
return binding.getQualifiedName();
}
String exprStr = expr.toString();
if (exprStr.endsWith(".class")) {
exprStr = exprStr.substring(0, exprStr.length() - 6);
}
String resolved = resolveTypeNameToFqn(exprStr, contextCu, context);
if (resolved != null) return resolved;
return exprStr;
}
private String resolveTypeNameToFqn(String name, CompilationUnit contextCu, CodebaseContext context) {
if (name == null || name.isEmpty()) return null;
if (context.getTypeDeclaration(name) != null || context.getEnumValuesMap().containsKey(name)) {
return name;
}
if (contextCu != null) {
for (Object typeObj : contextCu.types()) {
if (typeObj instanceof AbstractTypeDeclaration atd) {
if (atd.getName().getIdentifier().equals(name)) {
String pkg = contextCu.getPackage() != null ? contextCu.getPackage().getName().getFullyQualifiedName() : "";
return pkg.isEmpty() ? name : pkg + "." + name;
}
}
}
for (Object impObj : contextCu.imports()) {
ImportDeclaration imp = (ImportDeclaration) impObj;
String impName = imp.getName().getFullyQualifiedName();
if (!imp.isStatic() && !imp.isOnDemand()) {
if (impName.endsWith("." + name)) {
return impName;
}
}
}
if (contextCu.getPackage() != null) {
String pkg = contextCu.getPackage().getName().getFullyQualifiedName();
String pkgFqn = pkg + "." + name;
if (context.getEnumValuesMap().containsKey(pkgFqn) || context.getTypeDeclaration(pkgFqn) != null) {
return pkgFqn;
}
}
}
return null;
}
} }

View File

@@ -18,14 +18,28 @@ public class SiblingDependencyResolver {
private static final Pattern MAVEN_DEPENDENCY = Pattern.compile("<dependency>\\s*<groupId>(.*?)</groupId>\\s*<artifactId>(.*?)</artifactId>\\s*<version>(.*?)</version>", Pattern.DOTALL); private static final Pattern MAVEN_DEPENDENCY = Pattern.compile("<dependency>\\s*<groupId>(.*?)</groupId>\\s*<artifactId>(.*?)</artifactId>\\s*<version>(.*?)</version>", Pattern.DOTALL);
public Set<Path> resolveSiblings(Path projectDir) { public Set<Path> resolveSiblings(Path projectDir) {
Set<Path> siblings = new HashSet<>(); Set<Path> allSiblings = new HashSet<>();
try { Queue<Path> queue = new LinkedList<>();
siblings.addAll(resolveGradleSiblings(projectDir)); queue.add(projectDir);
siblings.addAll(resolveMavenSiblings(projectDir));
} catch (Exception e) { while (!queue.isEmpty()) {
log.warn("Error resolving sibling dependencies for {}", projectDir, e); Path current = queue.poll();
Set<Path> currentSiblings = new HashSet<>();
try {
currentSiblings.addAll(resolveGradleSiblings(current));
currentSiblings.addAll(resolveMavenSiblings(current));
} catch (Exception e) {
log.warn("Error resolving sibling dependencies for {}", current, e);
}
for (Path sibling : currentSiblings) {
if (!allSiblings.contains(sibling) && !sibling.equals(projectDir)) {
allSiblings.add(sibling);
queue.add(sibling);
}
}
} }
return siblings; return allSiblings;
} }
private Set<Path> resolveGradleSiblings(Path projectDir) throws IOException { private Set<Path> resolveGradleSiblings(Path projectDir) throws IOException {
@@ -52,6 +66,23 @@ public class SiblingDependencyResolver {
for (String gradlePath : gradlePaths) { for (String gradlePath : gradlePaths) {
String relativePath = gradlePath.replace(':', '/'); String relativePath = gradlePath.replace(':', '/');
Path resolvedSibling = rootDir.resolve(relativePath).normalize(); Path resolvedSibling = rootDir.resolve(relativePath).normalize();
// Check for explicit projectDir mapping in settings.gradle or settings.gradle.kts
Path settingsFile = rootDir.resolve("settings.gradle");
if (!Files.exists(settingsFile)) {
settingsFile = rootDir.resolve("settings.gradle.kts");
}
if (Files.exists(settingsFile)) {
String settingsContent = Files.readString(settingsFile);
String regex = "project\\(['\"]:" + gradlePath + "['\"]\\)\\.projectDir\\s*=\\s*(?:file\\(['\"](.*?)['\"]\\)|new File\\(.*?,\\s*['\"](.*?)['\"]\\))";
Matcher settingsMatcher = Pattern.compile(regex).matcher(settingsContent);
if (settingsMatcher.find()) {
String customPath = settingsMatcher.group(1) != null ? settingsMatcher.group(1) : settingsMatcher.group(2);
resolvedSibling = rootDir.resolve(customPath).normalize();
log.info("Found custom projectDir in settings for {}: {}", gradlePath, resolvedSibling);
}
}
if (Files.exists(resolvedSibling)) { if (Files.exists(resolvedSibling)) {
log.info("Discovered local Gradle sibling dependency: {}", resolvedSibling); log.info("Discovered local Gradle sibling dependency: {}", resolvedSibling);
siblings.add(resolvedSibling); siblings.add(resolvedSibling);
@@ -117,7 +148,7 @@ public class SiblingDependencyResolver {
if (artifactId != null && targetArtifacts.contains(artifactId)) { if (artifactId != null && targetArtifacts.contains(artifactId)) {
Path siblingDir = p.getParent(); Path siblingDir = p.getParent();
if (!siblingDir.equals(projectDir)) { if (!siblingDir.equals(projectDir)) {
log.info("Discovered local Maven sibling dependency: {}", siblingDir); log.info("Discovered local Maven sibling dependency via artifact match: {}", siblingDir);
siblings.add(siblingDir); siblings.add(siblingDir);
} }
} }
@@ -128,6 +159,37 @@ public class SiblingDependencyResolver {
} }
} }
} }
// Also directly add aggregator <module> directories declared in this pom
Matcher moduleMatcher = Pattern.compile("<module>(.*?)</module>").matcher(content);
while (moduleMatcher.find()) {
String moduleName = moduleMatcher.group(1).trim();
Path moduleDir = projectDir.resolve(moduleName).normalize();
if (Files.exists(moduleDir) && Files.exists(moduleDir.resolve("pom.xml")) && !moduleDir.equals(projectDir)) {
log.info("Discovered local Maven sibling dependency via <module>: {}", moduleDir);
siblings.add(moduleDir);
}
}
// Also add parent dir if it has a pom.xml
Path parentDir = projectDir.getParent();
// Check for explicit <relativePath> in parent block
Matcher parentRelativePathMatcher = Pattern.compile("<parent>.*?<relativePath>(.*?)</relativePath>.*?</parent>", Pattern.DOTALL).matcher(content);
if (parentRelativePathMatcher.find()) {
String relPathStr = parentRelativePathMatcher.group(1).trim();
Path relPath = projectDir.resolve(relPathStr).normalize();
if (Files.isRegularFile(relPath) && relPath.getFileName().toString().equals("pom.xml")) {
parentDir = relPath.getParent();
} else if (Files.isDirectory(relPath)) {
parentDir = relPath;
}
}
if (parentDir != null && Files.exists(parentDir.resolve("pom.xml")) && !parentDir.equals(projectDir)) {
log.info("Discovered local Maven parent sibling: {}", parentDir);
siblings.add(parentDir);
}
} }
return siblings; return siblings;
} }

View File

@@ -8,4 +8,8 @@ import java.util.List;
public interface CallGraphEngine { public interface CallGraphEngine {
List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers); List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers);
default java.util.Map<String, java.util.List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> getCallGraph() {
return java.util.Collections.emptyMap();
}
} }

View File

@@ -2,12 +2,17 @@ package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.CallEdge; import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext; import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.*; import java.util.*;
@Slf4j @Slf4j
public class CallGraphPathFinder { public class CallGraphPathFinder {
private final CodebaseContext context; private final CodebaseContext context;
private final Map<java.util.Map.Entry<String, String>, Boolean> heuristicCache = new HashMap<>();
private final Map<java.util.Map.Entry<String, String>, List<List<String>>> memoCache = new HashMap<>();
private final Map<java.util.Map.Entry<String, String>, Boolean> unreachableCache = new HashMap<>();
public CallGraphPathFinder(CodebaseContext context) { public CallGraphPathFinder(CodebaseContext context) {
this.context = context; this.context = context;
@@ -47,6 +52,129 @@ public class CallGraphPathFinder {
return null; return null;
} }
public List<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited) {
return findAllPaths(start, target, graph, visited, new boolean[]{false});
}
public List<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited, boolean[] parentHitCycle) {
List<List<String>> result = new ArrayList<>();
java.util.Map.Entry<String, String> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(start, target);
if (unreachableCache.containsKey(cacheKey)) {
return result;
}
if (memoCache.containsKey(cacheKey)) {
List<List<String>> cached = memoCache.get(cacheKey);
for (List<String> path : cached) {
boolean hasCycle = false;
for (String node : path) {
if (visited.contains(node)) {
hasCycle = true;
parentHitCycle[0] = true;
break;
}
}
if (!hasCycle) {
result.add(new ArrayList<>(path));
}
}
return result;
}
if (start.equals(target)) {
result.add(new ArrayList<>(List.of(start)));
return result;
}
if (!visited.add(start)) {
parentHitCycle[0] = true;
return result;
}
boolean[] localHitCycle = new boolean[]{false};
List<CallEdge> neighbors = graph.get(start);
if (neighbors != null) {
for (CallEdge edge : neighbors) {
String neighbor = edge.getTargetMethod();
if (neighbor.startsWith("java.") || neighbor.startsWith("javax.") || neighbor.startsWith("jakarta.") || neighbor.startsWith("org.springframework.")) continue;
if (neighbor.equals(target) || isHeuristicMatch(neighbor, target)) {
List<String> path = new ArrayList<>(List.of(start, target));
result.add(path);
} else {
List<List<String>> subPaths = findAllPaths(neighbor, target, graph, visited, localHitCycle);
for (List<String> subPath : subPaths) {
List<String> path = new ArrayList<>();
path.add(start);
path.addAll(subPath);
result.add(path);
}
}
}
}
// Handle inheritance fallback for concrete class methods not in the graph
if (result.isEmpty() && start.contains(".")) {
String className = start.substring(0, start.lastIndexOf('.'));
String methodName = start.substring(start.lastIndexOf('.') + 1);
if (className.contains("<")) {
className = className.substring(0, className.indexOf('<'));
}
TypeDeclaration td = context.getTypeDeclaration(className);
if (td != null) {
String superclass = context.getSuperclassFqn(td);
if (superclass != null) {
String superMethod = superclass + "." + methodName;
if (graph.containsKey(superMethod)) {
List<List<String>> superPaths = findAllPaths(superMethod, target, graph, visited, localHitCycle);
for (List<String> superPath : superPaths) {
List<String> path = new ArrayList<>();
path.add(start);
path.addAll(superPath);
result.add(path);
}
}
}
}
}
// Handle implementation fallback for interfaces/superclasses (interface -> concrete class)
if (result.isEmpty() && start.contains(".")) {
String className = start.substring(0, start.lastIndexOf('.'));
String methodName = start.substring(start.lastIndexOf('.') + 1);
if (className.contains("<")) {
className = className.substring(0, className.indexOf('<'));
}
List<String> impls = context.getImplementations(className);
if (impls != null) {
for (String impl : impls) {
String implMethod = impl + "." + methodName;
if (graph.containsKey(implMethod)) {
List<List<String>> implPaths = findAllPaths(implMethod, target, graph, visited, localHitCycle);
for (List<String> implPath : implPaths) {
List<String> path = new ArrayList<>();
path.add(start);
path.addAll(implPath);
result.add(path);
}
}
}
}
}
visited.remove(start);
if (localHitCycle[0]) {
parentHitCycle[0] = true;
} else if (result.isEmpty()) {
unreachableCache.put(cacheKey, true);
} else {
List<List<String>> toCache = new ArrayList<>();
for (List<String> path : result) {
toCache.add(new ArrayList<>(path));
}
memoCache.put(cacheKey, toCache);
}
return result;
}
public String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) { public String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) {
for (String node : path) { for (String node : path) {
List<CallEdge> edges = callGraph.get(node); List<CallEdge> edges = callGraph.get(node);
@@ -68,6 +196,18 @@ public class CallGraphPathFinder {
if (neighbor == null || target == null) return false; if (neighbor == null || target == null) return false;
if (neighbor.equals(target)) return true; if (neighbor.equals(target)) return true;
java.util.Map.Entry<String, String> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(neighbor, target);
Boolean cached = heuristicCache.get(cacheKey);
if (cached != null) return cached;
boolean result = calculateHeuristicMatch(neighbor, target);
heuristicCache.put(cacheKey, result);
return result;
}
private boolean calculateHeuristicMatch(String neighbor, String target) {
if (neighbor.equals(target)) return true;
if (isFullyQualifiedMethod(neighbor) && isFullyQualifiedMethod(target)) { if (isFullyQualifiedMethod(neighbor) && isFullyQualifiedMethod(target)) {
int lastDotNeighbor = neighbor.lastIndexOf('.'); int lastDotNeighbor = neighbor.lastIndexOf('.');
int lastDotTarget = target.lastIndexOf('.'); int lastDotTarget = target.lastIndexOf('.');
@@ -89,7 +229,6 @@ public class CallGraphPathFinder {
String simpleClassTarget = classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget; String simpleClassTarget = classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget;
if (simpleClassNeighbor.equals(simpleClassTarget)) return true; if (simpleClassNeighbor.equals(simpleClassTarget)) return true;
if (simpleClassNeighbor.contains(simpleClassTarget) || simpleClassTarget.contains(simpleClassNeighbor)) return true;
List<String> impls = context.getImplementations(classTarget); List<String> impls = context.getImplementations(classTarget);
if (impls != null) { if (impls != null) {
@@ -109,7 +248,21 @@ public class CallGraphPathFinder {
String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target; String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target;
String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor; String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor;
return simpleNeighbor.equals(simpleTarget); if (!simpleNeighbor.equals(simpleTarget)) return false;
String classNeighbor = neighbor.contains(".") ? neighbor.substring(0, neighbor.lastIndexOf('.')) : null;
String classTarget = target.contains(".") ? target.substring(0, target.lastIndexOf('.')) : null;
String simpleClassTarget = classTarget != null && classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget;
String simpleClassNeighbor = classNeighbor != null && classNeighbor.contains(".") ? classNeighbor.substring(classNeighbor.lastIndexOf('.') + 1) : classNeighbor;
if (simpleClassNeighbor != null && simpleClassTarget != null) {
if (simpleClassNeighbor.equalsIgnoreCase(simpleClassTarget)) return true;
// e.g. "this" vs "com.example.Machine"
if (simpleClassNeighbor.equals("this") || simpleClassNeighbor.equals("super")) return true;
return false;
}
return true;
} }
private boolean isFullyQualifiedMethod(String methodFqn) { private boolean isFullyQualifiedMethod(String methodFqn) {

View File

@@ -0,0 +1,118 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import java.util.*;
public class CompositeCallGraphEngine implements CallGraphEngine {
private final List<CallGraphEngine> engines;
private final Map<String, List<CallEdge>> mergedGraph = new HashMap<>();
public CompositeCallGraphEngine(List<CallGraphEngine> engines) {
this.engines = engines;
mergeGraphs();
}
private void mergeGraphs() {
for (CallGraphEngine engine : engines) {
Map<String, List<CallEdge>> graph = engine.getCallGraph();
if (graph != null) {
for (Map.Entry<String, List<CallEdge>> entry : graph.entrySet()) {
String caller = normalizeCompanionFqn(entry.getKey());
List<CallEdge> edges = mergedGraph.computeIfAbsent(caller, k -> new ArrayList<>());
for (CallEdge edge : entry.getValue()) {
String target = normalizeCompanionFqn(edge.getTargetMethod());
edges.add(new CallEdge(target, edge.getArguments(), edge.getReceiver()));
}
}
}
}
}
private String normalizeCompanionFqn(String fqn) {
if (fqn == null) return null;
return fqn.replace(".Companion.", ".");
}
@Override
public Map<String, List<CallEdge>> getCallGraph() {
return mergedGraph;
}
@Override
public List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
List<CallChain> chains = new ArrayList<>();
for (EntryPoint ep : entryPoints) {
String startMethod = normalizeCompanionFqn(ep.getClassName() + "." + ep.getMethodName());
for (TriggerPoint tp : triggers) {
String targetMethod = normalizeCompanionFqn(tp.getClassName() + "." + tp.getMethodName());
List<List<String>> paths = findAllPaths(startMethod, targetMethod, new LinkedHashSet<>());
for (List<String> path : paths) {
chains.add(CallChain.builder()
.entryPoint(ep)
.methodChain(path)
.triggerPoint(tp)
.build());
}
}
}
return chains;
}
private List<List<String>> findAllPaths(String start, String target, Set<String> visited) {
List<List<String>> result = new ArrayList<>();
if (start.equals(target)) {
result.add(new ArrayList<>(List.of(start)));
return result;
}
if (!visited.add(start)) {
return result;
}
List<CallEdge> neighbors = mergedGraph.get(start);
if (neighbors != null) {
for (CallEdge edge : neighbors) {
String neighbor = edge.getTargetMethod();
if (neighbor.startsWith("java.") || neighbor.startsWith("javax.") ||
neighbor.startsWith("jakarta.") || neighbor.startsWith("org.springframework.")) {
continue;
}
if (neighbor.equals(target) || isHeuristicMatch(neighbor, target)) {
List<String> path = new ArrayList<>(List.of(start, target));
result.add(path);
} else {
List<List<String>> subPaths = findAllPaths(neighbor, target, visited);
for (List<String> subPath : subPaths) {
List<String> path = new ArrayList<>();
path.add(start);
path.addAll(subPath);
result.add(path);
}
}
}
}
visited.remove(start);
return result;
}
private boolean isHeuristicMatch(String neighbor, String target) {
if (neighbor == null || target == null) return false;
if (neighbor.equals(target)) return true;
if (neighbor.contains(".") && target.contains(".")) {
String methodNeighbor = neighbor.substring(neighbor.lastIndexOf('.') + 1);
String methodTarget = target.substring(target.lastIndexOf('.') + 1);
if (methodNeighbor.equals(methodTarget)) {
String classNeighbor = neighbor.substring(0, neighbor.lastIndexOf('.'));
String classTarget = target.substring(0, target.lastIndexOf('.'));
String simpleClassNeighbor = classNeighbor.contains(".") ? classNeighbor.substring(classNeighbor.lastIndexOf('.') + 1) : classNeighbor;
String simpleClassTarget = classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget;
return simpleClassNeighbor.equalsIgnoreCase(simpleClassTarget);
}
}
return false;
}
}

View File

@@ -0,0 +1,53 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import java.util.*;
public class CompositeIntelligenceProvider implements CodebaseIntelligenceProvider {
private final List<CodebaseIntelligenceProvider> providers;
private final List<CallGraphEngine> engines;
public CompositeIntelligenceProvider(List<CodebaseIntelligenceProvider> providers, List<CallGraphEngine> engines) {
this.providers = providers;
this.engines = engines;
}
@Override
public List<TriggerPoint> findTriggerPoints() {
List<TriggerPoint> result = new ArrayList<>();
for (CodebaseIntelligenceProvider provider : providers) {
result.addAll(provider.findTriggerPoints());
}
return result;
}
@Override
public List<EntryPoint> findEntryPoints() {
List<EntryPoint> result = new ArrayList<>();
for (CodebaseIntelligenceProvider provider : providers) {
result.addAll(provider.findEntryPoints());
}
return result;
}
@Override
public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
CompositeCallGraphEngine compositeEngine = new CompositeCallGraphEngine(engines);
return compositeEngine.findChains(entryPoints, triggers);
}
@Override
public Map<String, Map<String, String>> resolveProperties() {
Map<String, Map<String, String>> result = new HashMap<>();
for (CodebaseIntelligenceProvider provider : providers) {
Map<String, Map<String, String>> props = provider.resolveProperties();
if (props != null) {
result.putAll(props);
}
}
return result;
}
}

View File

@@ -30,6 +30,21 @@ public class ConstantExtractor {
} }
public void extractConstantsFromExpression(Expression expr, List<String> constants) { public void extractConstantsFromExpression(Expression expr, List<String> constants) {
if (constantResolver != null) {
String resolved = constantResolver.resolve(expr, context);
if (resolved != null) {
if (resolved.startsWith("ENUM_SET:")) {
for (String eVal : resolved.substring(9).split(",")) {
String parsed = parseEnumSetElement(eVal);
if (!constants.contains(parsed)) constants.add(parsed);
}
} else {
constants.add(resolved);
}
return;
}
}
if (expr instanceof QualifiedName qn) { if (expr instanceof QualifiedName qn) {
constants.add(qn.toString()); constants.add(qn.toString());
} else if (expr instanceof StringLiteral sl) { } else if (expr instanceof StringLiteral sl) {
@@ -74,7 +89,8 @@ public class ConstantExtractor {
} else if (expr instanceof MethodInvocation mi) { } else if (expr instanceof MethodInvocation mi) {
String methodName = mi.getName().getIdentifier(); String methodName = mi.getName().getIdentifier();
System.out.println("Processing mi: " + mi); if (methodName.equals("get") || methodName.equals("getOrDefault")) { log.trace("Processing mi: {}", mi);
if (methodName.equals("get") || methodName.equals("getOrDefault")) {
if (mi.getExpression() != null) { if (mi.getExpression() != null) {
if (variableTracer != null) { if (variableTracer != null) {
List<Expression> traced = variableTracer.traceVariableAll(mi.getExpression()); List<Expression> traced = variableTracer.traceVariableAll(mi.getExpression());
@@ -266,6 +282,7 @@ public class ConstantExtractor {
} }
public List<String> resolveMethodReturnConstant(String className, String methodName, int depth, Set<String> visited, CompilationUnit contextCu) { public List<String> resolveMethodReturnConstant(String className, String methodName, int depth, Set<String> visited, CompilationUnit contextCu) {
log.debug("ConstantExtractor.resolveMethodReturnConstant CALLED: className={}, methodName={}", className, methodName);
log.debug("Entering resolveMethodReturnConstant with className={}, methodName={}, depth={}", className, methodName, depth); log.debug("Entering resolveMethodReturnConstant with className={}, methodName={}, depth={}", className, methodName, depth);
if (depth > 20) { if (depth > 20) {
log.debug("Exiting resolveMethodReturnConstant early (depth limit) for {}.{}", className, methodName); log.debug("Exiting resolveMethodReturnConstant early (depth limit) for {}.{}", className, methodName);
@@ -333,6 +350,9 @@ public class ConstantExtractor {
} }
if (!handled && variableTracer != null && constructorAnalyzer != null) { if (!handled && variableTracer != null && constructorAnalyzer != null) {
List<Expression> tracedRetExprs = variableTracer.traceVariableAll(retExpr); List<Expression> tracedRetExprs = variableTracer.traceVariableAll(retExpr);
if (tracedRetExprs.isEmpty() && retExpr != null) {
tracedRetExprs = List.of(retExpr);
}
for (Expression tracedRetExpr : tracedRetExprs) { for (Expression tracedRetExpr : tracedRetExprs) {
extractConstantsFromExpression(tracedRetExpr, constants); extractConstantsFromExpression(tracedRetExpr, constants);
String val = constantResolver.resolve(tracedRetExpr, context); String val = constantResolver.resolve(tracedRetExpr, context);
@@ -373,6 +393,7 @@ public class ConstantExtractor {
} }
} }
visited.remove(fqn); visited.remove(fqn);
log.debug("resolveMethodReturnConstant className={} methodName={} returns: {}", className, methodName, constants);
log.debug("resolveMethodReturnConstant for class={}, method={} resolved constants: {}", className, methodName, constants); log.debug("resolveMethodReturnConstant for class={}, method={} resolved constants: {}", className, methodName, constants);
return constants; return constants;
} }

View File

@@ -5,6 +5,9 @@ import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.*; import org.eclipse.jdt.core.dom.*;
import java.util.*; import java.util.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ConstructorAnalyzer { public class ConstructorAnalyzer {
private final CodebaseContext context; private final CodebaseContext context;
private final ConstantResolver constantResolver; private final ConstantResolver constantResolver;
@@ -245,7 +248,8 @@ public class ConstructorAnalyzer {
boolean matches = false; boolean matches = false;
if (resolvedCtor != null && ctor.resolveBinding() != null) { if (resolvedCtor != null && ctor.resolveBinding() != null) {
matches = resolvedCtor.isEqualTo(ctor.resolveBinding()) || resolvedCtor.getKey().equals(ctor.resolveBinding().getKey()); matches = resolvedCtor.isEqualTo(ctor.resolveBinding()) || resolvedCtor.getKey().equals(ctor.resolveBinding().getKey());
} else { }
if (!matches) {
matches = ctor.parameters().size() == cic.arguments().size(); matches = ctor.parameters().size() == cic.arguments().size();
} }
if (!matches) continue; if (!matches) continue;
@@ -271,39 +275,19 @@ public class ConstructorAnalyzer {
if (resolved != null) paramValues.put(pName, resolved); if (resolved != null) paramValues.put(pName, resolved);
} }
} }
if (paramValues.isEmpty()) continue; // Evaluate constructor body regardless of whether parameters could be resolved as constants
ctor.getBody().accept(new ASTVisitor() { java.util.Map<String, String> localVars = new java.util.HashMap<>(paramValues);
@Override constantResolver.evaluateMethodBodyWithLocals(ctor, localVars, context, new java.util.HashSet<>());
public boolean visit(Assignment node) { for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
Expression lhs = node.getLeftHandSide(); if (entry.getKey().startsWith("this.")) {
Expression rhs = node.getRightHandSide(); fieldValues.put(entry.getKey(), entry.getValue());
fieldValues.put(entry.getKey().substring(5), entry.getValue());
String fieldName = null; } else if (context.hasField(td, entry.getKey())) {
if (lhs instanceof FieldAccess fa fieldValues.put(entry.getKey(), entry.getValue());
&& fa.getExpression() instanceof ThisExpression) { fieldValues.put("this." + entry.getKey(), entry.getValue());
fieldName = fa.getName().getIdentifier();
} else if (lhs instanceof SimpleName sn
&& !paramValues.containsKey(sn.getIdentifier())) {
fieldName = sn.getIdentifier();
}
if (fieldName != null) {
String paramVal = null;
if (rhs instanceof SimpleName snRhs) {
paramVal = paramValues.get(snRhs.getIdentifier());
} else if (rhsResolver != null) {
paramVal = rhsResolver.resolve(rhs, paramValues, td);
}
if (paramVal != null) {
fieldValues.put(fieldName, paramVal);
fieldValues.put("this." + fieldName, paramVal);
}
}
return super.visit(node);
} }
}); }
ctor.getBody().accept(new ASTVisitor() { ctor.getBody().accept(new ASTVisitor() {
@Override @Override
@@ -383,43 +367,29 @@ public class ConstructorAnalyzer {
resolved = constantResolver.resolve(argExpr, context); resolved = constantResolver.resolve(argExpr, context);
} }
} }
if (resolved == null && rhsResolver != null) {
TypeDeclaration enclosingTd = findEnclosingType(argExpr);
if (enclosingTd != null) {
resolved = rhsResolver.resolve(argExpr, subClassParamValues, enclosingTd);
}
}
if (resolved != null) { if (resolved != null) {
superParamValues.put(pName, resolved); superParamValues.put(pName, resolved);
} }
} }
if (superParamValues.isEmpty()) continue; if (superParamValues.isEmpty()) continue;
superCtor.getBody().accept(new ASTVisitor() { java.util.Map<String, String> localVars = new java.util.HashMap<>(superParamValues);
@Override constantResolver.evaluateMethodBodyWithLocals(superCtor, localVars, context, new java.util.HashSet<>());
public boolean visit(Assignment node) { for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
Expression lhs = node.getLeftHandSide(); if (entry.getKey().startsWith("this.")) {
Expression rhs = node.getRightHandSide(); fieldValues.put(entry.getKey(), entry.getValue());
fieldValues.put(entry.getKey().substring(5), entry.getValue());
String fieldName = null; } else if (context.hasField(superTd, entry.getKey())) {
if (lhs instanceof FieldAccess fa fieldValues.put(entry.getKey(), entry.getValue());
&& fa.getExpression() instanceof ThisExpression) { fieldValues.put("this." + entry.getKey(), entry.getValue());
fieldName = fa.getName().getIdentifier();
} else if (lhs instanceof SimpleName sn
&& !superParamValues.containsKey(sn.getIdentifier())) {
fieldName = sn.getIdentifier();
}
if (fieldName != null) {
String paramVal = null;
if (rhs instanceof SimpleName snRhs) {
paramVal = superParamValues.get(snRhs.getIdentifier());
} else if (rhsResolver != null) {
paramVal = rhsResolver.resolve(rhs, superParamValues, superTd);
}
if (paramVal != null) {
fieldValues.put(fieldName, paramVal);
fieldValues.put("this." + fieldName, paramVal);
}
}
return super.visit(node);
} }
}); }
superCtor.getBody().accept(new ASTVisitor() { superCtor.getBody().accept(new ASTVisitor() {
@Override @Override
@@ -472,6 +442,13 @@ public class ConstructorAnalyzer {
} }
MethodDeclaration method = context.findMethodDeclaration(td, methodName, true); MethodDeclaration method = context.findMethodDeclaration(td, methodName, true);
if (method == null) {
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
TypeDeclaration targetTd = context.resolveStaticImport(methodName, cu);
if (targetTd != null) {
method = context.findMethodDeclaration(targetTd, methodName, true);
}
}
if (method == null || method.getBody() == null) { if (method == null || method.getBody() == null) {
return null; return null;
} }
@@ -576,10 +553,12 @@ public class ConstructorAnalyzer {
} }
try { try {
Map<String, String> fieldValues = buildFieldValuesFromCIC(td, cic, context); Map<String, String> fieldValues = buildFieldValuesFromCIC(td, cic, context, this::evaluateMethodCallInConstructor);
log.debug("RESOLVE_GETTER: td={}, fieldValues={}", context.getFqn(td), fieldValues);
if (fieldValues.isEmpty()) return Collections.emptyList(); if (fieldValues.isEmpty()) return Collections.emptyList();
String result = constantResolver.evaluateMethodBodyWithLocals(getter, fieldValues, context, visited); String result = constantResolver.evaluateMethodBodyWithLocals(getter, fieldValues, context, visited);
log.debug("RESOLVE_GETTER: result={}", result);
return result != null ? List.of(result) : Collections.emptyList(); return result != null ? List.of(result) : Collections.emptyList();
} finally { } finally {
visited.remove(getterKey); visited.remove(getterKey);
@@ -665,6 +644,12 @@ public class ConstructorAnalyzer {
if (targetIdx >= 0 && targetIdx < arguments.size()) { if (targetIdx >= 0 && targetIdx < arguments.size()) {
Expression arg = (Expression) arguments.get(targetIdx); Expression arg = (Expression) arguments.get(targetIdx);
String val = constantResolver.resolve(arg, context); String val = constantResolver.resolve(arg, context);
if (val == null && arg instanceof MethodInvocation mi) {
TypeDeclaration enclosingTd = findEnclosingType(arg);
if (enclosingTd != null) {
val = evaluateMethodCallInConstructor(arg, new HashMap<>(), enclosingTd);
}
}
if (val != null) { if (val != null) {
if (val.startsWith("ENUM_SET:")) { if (val.startsWith("ENUM_SET:")) {
for (String eVal : val.substring(9).split(",")) { for (String eVal : val.substring(9).split(",")) {

View File

@@ -34,9 +34,7 @@ public class DynamicClasspathResolver {
protected List<String> resolveMaven(Path projectRoot) { protected List<String> resolveMaven(Path projectRoot) {
log.info("Maven project detected. Resolving dependencies..."); log.info("Maven project detected. Resolving dependencies...");
Path cpFile = null;
try { try {
cpFile = Files.createTempFile("state_machine_exporter_cp", ".txt");
String mvnCmd = getCommand(projectRoot, "mvnw", "mvn"); String mvnCmd = getCommand(projectRoot, "mvnw", "mvn");
if (mvnCmd == null) { if (mvnCmd == null) {
log.warn("Maven executable not found."); log.warn("Maven executable not found.");
@@ -48,26 +46,32 @@ public class DynamicClasspathResolver {
"-q", "-q",
"dependency:build-classpath", "dependency:build-classpath",
"-DincludeScope=compile", "-DincludeScope=compile",
"-Dmdep.outputFile=" + cpFile.toAbsolutePath().toString() "-Dmdep.outputFile=sme_cp.txt"
); );
pb.directory(projectRoot.toFile()); pb.directory(projectRoot.toFile());
runProcess(pb); runProcess(pb);
if (Files.exists(cpFile)) { java.util.Set<String> allPaths = new java.util.LinkedHashSet<>();
String cpString = Files.readString(cpFile).trim(); try (java.util.stream.Stream<Path> stream = Files.walk(projectRoot)) {
if (!cpString.isEmpty()) { stream.filter(p -> p.getFileName().toString().equals("sme_cp.txt"))
return Arrays.asList(cpString.split(File.pathSeparator)); .forEach(cpFile -> {
} try {
String cpString = Files.readString(cpFile).trim();
if (!cpString.isEmpty()) {
allPaths.addAll(Arrays.asList(cpString.split(File.pathSeparator)));
}
Files.deleteIfExists(cpFile);
} catch (IOException e) {
log.warn("Failed to read or delete sme_cp.txt at {}", cpFile, e);
}
});
}
if (!allPaths.isEmpty()) {
return new ArrayList<>(allPaths);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to dynamically resolve Maven classpath", e); log.error("Failed to dynamically resolve Maven classpath", e);
} finally {
if (cpFile != null) {
try {
Files.deleteIfExists(cpFile);
} catch (IOException ignored) {}
}
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -107,14 +111,18 @@ public class DynamicClasspathResolver {
pb.directory(projectRoot.toFile()); pb.directory(projectRoot.toFile());
String output = runProcessAndGetOutput(pb); String output = runProcessAndGetOutput(pb);
java.util.Set<String> allPaths = new java.util.LinkedHashSet<>();
for (String line : output.split("\\r?\\n")) { for (String line : output.split("\\r?\\n")) {
if (line.startsWith("SME_CLASSPATH_MARKER:")) { if (line.startsWith("SME_CLASSPATH_MARKER:")) {
String cpString = line.substring("SME_CLASSPATH_MARKER:".length()).trim(); String cpString = line.substring("SME_CLASSPATH_MARKER:".length()).trim();
if (!cpString.isEmpty()) { if (!cpString.isEmpty()) {
return Arrays.asList(cpString.split(File.pathSeparator)); allPaths.addAll(Arrays.asList(cpString.split(File.pathSeparator)));
} }
} }
} }
if (!allPaths.isEmpty()) {
return new ArrayList<>(allPaths);
}
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to dynamically resolve Gradle classpath", e); log.error("Failed to dynamically resolve Gradle classpath", e);
} finally { } finally {
@@ -143,11 +151,14 @@ public class DynamicClasspathResolver {
} }
protected void runProcess(ProcessBuilder pb) throws IOException, InterruptedException { protected void runProcess(ProcessBuilder pb) throws IOException, InterruptedException {
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);
Process p = pb.start(); Process p = pb.start();
p.waitFor(); p.waitFor();
} }
protected String runProcessAndGetOutput(ProcessBuilder pb) throws IOException, InterruptedException { protected String runProcessAndGetOutput(ProcessBuilder pb) throws IOException, InterruptedException {
pb.redirectErrorStream(true);
Process p = pb.start(); Process p = pb.start();
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {

View File

@@ -70,7 +70,14 @@ public class GenericEventDetector {
String sourceState = extractSourceState(node); String sourceState = extractSourceState(node);
String[] smTypes = resolveStateMachineTypeArgumentsForExpression(emr.getExpression(), node); String[] smTypes = resolveStateMachineTypeArgumentsForExpression(emr.getExpression(), node);
boolean external = false;
if (receiver != null) {
AssignmentDag dag = new AssignmentDag(method);
external = dag.isExternal(receiver, new java.util.HashSet<>());
}
if (eventValue != null) { if (eventValue != null) {
String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
triggers.add(TriggerPoint.builder() triggers.add(TriggerPoint.builder()
.event(eventValue) .event(eventValue)
.sourceState(sourceState) .sourceState(sourceState)
@@ -80,6 +87,8 @@ public class GenericEventDetector {
.lineNumber(cu.getLineNumber(node.getStartPosition())) .lineNumber(cu.getLineNumber(node.getStartPosition()))
.stateTypeFqn(smTypes[0]) .stateTypeFqn(smTypes[0])
.eventTypeFqn(smTypes[1]) .eventTypeFqn(smTypes[1])
.external(external)
.constraint(constraint)
.build()); .build());
} }
} }
@@ -227,6 +236,15 @@ public class GenericEventDetector {
String sourceState = extractSourceState(node); String sourceState = extractSourceState(node);
String[] smTypes = resolveStateMachineTypeArguments(node); String[] smTypes = resolveStateMachineTypeArguments(node);
boolean external = false;
if (!node.arguments().isEmpty()) {
Expression eventExpr = (Expression) node.arguments().get(0);
AssignmentDag dag = new AssignmentDag(method);
external = dag.isExternal(eventExpr, new java.util.HashSet<>());
}
String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
List<TriggerPoint> results = new ArrayList<>(); List<TriggerPoint> results = new ArrayList<>();
if (eventValue != null && eventValue.startsWith("ENUM_SET:")) { if (eventValue != null && eventValue.startsWith("ENUM_SET:")) {
@@ -242,6 +260,8 @@ public class GenericEventDetector {
.lineNumber(cu.getLineNumber(node.getStartPosition())) .lineNumber(cu.getLineNumber(node.getStartPosition()))
.stateTypeFqn(smTypes[0]) .stateTypeFqn(smTypes[0])
.eventTypeFqn(smTypes[1]) .eventTypeFqn(smTypes[1])
.external(external)
.constraint(constraint)
.build()); .build());
} }
} }
@@ -255,6 +275,8 @@ public class GenericEventDetector {
.lineNumber(cu.getLineNumber(node.getStartPosition())) .lineNumber(cu.getLineNumber(node.getStartPosition()))
.stateTypeFqn(smTypes[0]) .stateTypeFqn(smTypes[0])
.eventTypeFqn(smTypes[1]) .eventTypeFqn(smTypes[1])
.external(external)
.constraint(constraint)
.build()); .build());
} }
@@ -503,6 +525,21 @@ public class GenericEventDetector {
private String[] resolveStateMachineTypeArguments(MethodInvocation node) { private String[] resolveStateMachineTypeArguments(MethodInvocation node) {
Expression receiver = node.getExpression(); Expression receiver = node.getExpression();
// Trace back the receiver if it's a builder chain
while (receiver instanceof MethodInvocation mi) {
if (mi.getName().getIdentifier().equals("sendEvent")) {
receiver = mi.getExpression();
break;
}
receiver = mi.getExpression();
}
// Also if the node itself is sendEvent, receiver is already correct
if (node.getName().getIdentifier().equals("sendEvent")) {
receiver = node.getExpression();
}
if (receiver == null) return new String[]{null, null}; if (receiver == null) return new String[]{null, null};
ITypeBinding binding = receiver.resolveTypeBinding(); ITypeBinding binding = receiver.resolveTypeBinding();
@@ -524,13 +561,57 @@ public class GenericEventDetector {
CompilationUnit cu = (CompilationUnit) node.getRoot(); CompilationUnit cu = (CompilationUnit) node.getRoot();
String stateType = resolveTypeToFqn((Type) typeArgs.get(0), cu); String stateType = resolveTypeToFqn((Type) typeArgs.get(0), cu);
String eventType = resolveTypeToFqn((Type) typeArgs.get(1), cu); String eventType = resolveTypeToFqn((Type) typeArgs.get(1), cu);
if (stateType != null && stateType.length() == 1) {
stateType = resolveGenericTypeVariable(stateType, node, cu);
}
if (eventType != null && eventType.length() == 1) {
eventType = resolveGenericTypeVariable(eventType, node, cu);
}
return new String[]{stateType, eventType}; return new String[]{stateType, eventType};
} }
} }
return new String[]{null, null}; return new String[]{null, null};
} }
private String resolveGenericTypeVariable(String typeVarName, ASTNode node, CompilationUnit cu) {
TypeDeclaration enclosingClass = findEnclosingType(node);
if (enclosingClass == null) return typeVarName;
String className = context.getFqn(enclosingClass);
if (className == null) return typeVarName;
List<String> impls = context.getImplementations(className);
if (impls == null || impls.isEmpty()) return typeVarName;
for (String implFqn : impls) {
org.eclipse.jdt.core.dom.AbstractTypeDeclaration implNode = context.getAbstractTypeDeclaration(implFqn);
if (implNode instanceof TypeDeclaration td) {
Type superclassType = td.getSuperclassType();
if (superclassType instanceof ParameterizedType pt) {
Type baseType = pt.getType();
String baseName = resolveTypeToFqn(baseType, (CompilationUnit) implNode.getRoot());
if (className.equals(baseName) || className.endsWith("." + baseName)) {
int typeIndex = -1;
List<?> typeParams = enclosingClass.typeParameters();
for (int i = 0; i < typeParams.size(); i++) {
TypeParameter tp = (TypeParameter) typeParams.get(i);
if (tp.getName().getIdentifier().equals(typeVarName)) {
typeIndex = i;
break;
}
}
if (typeIndex >= 0 && typeIndex < pt.typeArguments().size()) {
Type concreteType = (Type) pt.typeArguments().get(typeIndex);
return resolveTypeToFqn(concreteType, (CompilationUnit) implNode.getRoot());
}
}
}
}
}
return typeVarName;
}
private ITypeBinding findStateMachineSupertype(ITypeBinding binding, java.util.Set<String> visited) { private ITypeBinding findStateMachineSupertype(ITypeBinding binding, java.util.Set<String> visited) {
ITypeBinding current = binding; ITypeBinding current = binding;
while (current != null) { while (current != null) {
@@ -635,4 +716,93 @@ public class GenericEventDetector {
return simpleName; return simpleName;
} }
private static class AssignmentDag {
final java.util.Map<String, List<Expression>> assignments = new java.util.HashMap<>();
final java.util.Set<SingleVariableDeclaration> parameters = new java.util.HashSet<>();
AssignmentDag(MethodDeclaration method) {
if (method == null) return;
for (Object paramObj : method.parameters()) {
if (paramObj instanceof SingleVariableDeclaration svd) {
parameters.add(svd);
}
}
if (method.getBody() != null) {
method.getBody().accept(new ASTVisitor() {
@Override
public boolean visit(VariableDeclarationFragment fragment) {
String varName = fragment.getName().getIdentifier();
if (fragment.getInitializer() != null) {
assignments.computeIfAbsent(varName, k -> new ArrayList<>()).add(fragment.getInitializer());
}
return super.visit(fragment);
}
@Override
public boolean visit(Assignment assignment) {
if (assignment.getLeftHandSide() instanceof SimpleName sn) {
String varName = sn.getIdentifier();
assignments.computeIfAbsent(varName, k -> new ArrayList<>()).add(assignment.getRightHandSide());
}
return super.visit(assignment);
}
});
}
}
boolean isExternal(Expression expr, java.util.Set<String> visited) {
if (expr == null) return false;
Expression peeled = expr;
while (peeled instanceof ParenthesizedExpression pe) {
peeled = pe.getExpression();
}
while (peeled instanceof CastExpression ce) {
peeled = ce.getExpression();
}
if (peeled instanceof SimpleName sn) {
String varName = sn.getIdentifier();
for (SingleVariableDeclaration svd : parameters) {
if (svd.getName().getIdentifier().equals(varName)) {
return isAnnotatedAsExternal(svd);
}
}
if (assignments.containsKey(varName)) {
if (!visited.add(varName)) {
return false;
}
List<Expression> defs = assignments.get(varName);
for (Expression def : defs) {
if (isExternal(def, visited)) {
return true;
}
}
}
} else if (peeled instanceof MethodInvocation mi) {
if (mi.getExpression() != null && isExternal(mi.getExpression(), visited)) {
return true;
}
for (Object arg : mi.arguments()) {
if (isExternal((Expression) arg, visited)) {
return true;
}
}
}
return false;
}
private boolean isAnnotatedAsExternal(SingleVariableDeclaration svd) {
for (Object modifier : svd.modifiers()) {
if (modifier instanceof Annotation annotation) {
String typeName = annotation.getTypeName().getFullyQualifiedName();
if (typeName.endsWith("PathVariable") || typeName.endsWith("RequestBody") || typeName.endsWith("RequestParam")
|| typeName.endsWith("PathParam") || typeName.endsWith("QueryParam")) {
return true;
}
}
}
return false;
}
}
} }

View File

@@ -20,7 +20,13 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
super(context); super(context);
} }
@SuppressWarnings("unchecked")
protected Map<String, List<CallEdge>> buildCallGraph() { protected Map<String, List<CallEdge>> buildCallGraph() {
Map<String, List<CallEdge>> cached = (Map<String, List<CallEdge>>) context.getCache().get("heuristicCallGraph");
if (cached != null) {
this.graph = cached;
return cached;
}
graph = new HashMap<>(); graph = new HashMap<>();
for (CompilationUnit cu : context.getCompilationUnits()) { for (CompilationUnit cu : context.getCompilationUnits()) {
cu.accept(new ASTVisitor() { cu.accept(new ASTVisitor() {
@@ -34,9 +40,12 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
String currentMethodFqn = context.getFqn(td) + "." + md.getName().getIdentifier(); String currentMethodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
List<String> calledMethods = resolveCalledMethodsPolymorphic(node); List<String> calledMethods = resolveCalledMethodsPolymorphic(node);
List<String> args = resolveArguments(node.arguments()); List<String> args = resolveArguments(node.arguments());
String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
for (String calledMethod : calledMethods) { for (String calledMethod : calledMethods) {
String receiver = getReceiverString(node.getExpression()); String receiver = getReceiverString(node.getExpression());
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args, receiver)); CallEdge edge = new CallEdge(calledMethod, args, receiver);
edge.setConstraint(resolveConstraint(node, calledMethod, constraint));
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
for (Object argObj : node.arguments()) { for (Object argObj : node.arguments()) {
if (argObj instanceof ExpressionMethodReference emr) { if (argObj instanceof ExpressionMethodReference emr) {
@@ -53,7 +62,9 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
implicitArgs.addAll(args); implicitArgs.addAll(args);
} }
String receiver = getReceiverString(node.getExpression()); String receiver = getReceiverString(node.getExpression());
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, implicitArgs, receiver)); CallEdge edge = new CallEdge(refMethod, implicitArgs, receiver);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
} }
} else { } else {
@@ -73,11 +84,15 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
implicitArgs.addAll(args); implicitArgs.addAll(args);
} }
String receiver = getReceiverString(node.getExpression()); String receiver = getReceiverString(node.getExpression());
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, implicitArgs, receiver)); CallEdge edge = new CallEdge(calledMethod, implicitArgs, receiver);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
List<String> impls = context.getImplementations(fallbackTypeFqn); List<String> impls = context.getImplementations(fallbackTypeFqn);
for (String impl : impls) { for (String impl : impls) {
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(impl + "." + emr.getName().getIdentifier(), args, receiver)); CallEdge implEdge = new CallEdge(impl + "." + emr.getName().getIdentifier(), args, receiver);
implEdge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(implEdge);
} }
} }
} }
@@ -110,7 +125,10 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
} }
List<String> args = resolveArguments(node.arguments()); List<String> args = resolveArguments(node.arguments());
String receiver = "super"; String receiver = "super";
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args, receiver)); String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
CallEdge edge = new CallEdge(calledMethod, args, receiver);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
} }
} }
@@ -119,6 +137,7 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
} }
}); });
} }
context.getCache().put("heuristicCallGraph", graph);
return graph; return graph;
} }
@@ -148,33 +167,6 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
return resolvedValue; return resolvedValue;
} }
protected List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
String baseCalled = resolveCalledMethod(node);
if (baseCalled == null) return Collections.emptyList();
List<String> allResolved = new ArrayList<>();
allResolved.add(baseCalled);
if (baseCalled.contains(".")) {
String className = baseCalled.substring(0, baseCalled.lastIndexOf('.'));
String methodName = baseCalled.substring(baseCalled.lastIndexOf('.') + 1);
String definingClass = findDefiningClass(className, methodName);
if (definingClass != null && !definingClass.equals(className)) {
allResolved.set(0, definingClass + "." + methodName);
className = definingClass;
}
List<String> impls = context.getImplementations(className);
for (String impl : impls) {
allResolved.add(impl + "." + methodName);
}
}
return allResolved;
}
private String findDefiningClass(String className, String methodName) { private String findDefiningClass(String className, String methodName) {
TypeDeclaration td = context.getTypeDeclaration(className); TypeDeclaration td = context.getTypeDeclaration(className);
if (td == null) return null; if (td == null) return null;
@@ -607,6 +599,14 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
} }
if (receiverType != null) { if (receiverType != null) {
String rawType = receiverType;
if (rawType.contains("<")) {
rawType = rawType.substring(0, rawType.indexOf('<'));
}
if (rawType.equals("java.util.Map") || rawType.equals("Map")) {
String valType = resolveMapValueType(mi);
if (valType != null) return valType;
}
if (receiverType.contains("<")) { if (receiverType.contains("<")) {
receiverType = receiverType.substring(0, receiverType.indexOf('<')); receiverType = receiverType.substring(0, receiverType.indexOf('<'));
} }
@@ -623,6 +623,12 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
} }
} }
} }
IMethodBinding methodBinding = mi.resolveMethodBinding();
if (methodBinding != null && methodBinding.getReturnType() != null) {
return methodBinding.getReturnType().getErasure().getQualifiedName();
}
return null; return null;
} }
} }

View File

@@ -24,7 +24,18 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
this.injectionAnalyzer = injectionAnalyzer; this.injectionAnalyzer = injectionAnalyzer;
} }
@Override
public Map<String, List<CallEdge>> getCallGraph() {
return buildCallGraph();
}
@SuppressWarnings("unchecked")
protected Map<String, List<CallEdge>> buildCallGraph() { protected Map<String, List<CallEdge>> buildCallGraph() {
Map<String, List<CallEdge>> cached = (Map<String, List<CallEdge>>) context.getCache().get("jdtCallGraph");
if (cached != null) {
this.graph = cached;
return cached;
}
graph = new HashMap<>(); graph = new HashMap<>();
for (CompilationUnit cu : context.getCompilationUnits()) { for (CompilationUnit cu : context.getCompilationUnits()) {
cu.accept(new ASTVisitor() { cu.accept(new ASTVisitor() {
@@ -38,8 +49,11 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
String currentMethodFqn = context.getFqn(td) + "." + md.getName().getIdentifier(); String currentMethodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
List<String> calledMethods = resolveCalledMethodsPolymorphic(node); List<String> calledMethods = resolveCalledMethodsPolymorphic(node);
List<String> args = resolveArguments(node.arguments()); List<String> args = resolveArguments(node.arguments());
String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
for (String calledMethod : calledMethods) { for (String calledMethod : calledMethods) {
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args)); CallEdge edge = new CallEdge(calledMethod, args);
edge.setConstraint(resolveConstraint(node, calledMethod, constraint));
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
for (Object argObj : node.arguments()) { for (Object argObj : node.arguments()) {
if (argObj instanceof ExpressionMethodReference emr) { if (argObj instanceof ExpressionMethodReference emr) {
@@ -55,7 +69,9 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} else { } else {
implicitArgs.addAll(args); implicitArgs.addAll(args);
} }
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, implicitArgs)); CallEdge edge = new CallEdge(refMethod, implicitArgs);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
} }
} else { } else {
@@ -74,11 +90,15 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} else { } else {
implicitArgs.addAll(args); implicitArgs.addAll(args);
} }
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, implicitArgs)); CallEdge edge = new CallEdge(calledMethod, implicitArgs);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
List<String> impls = context.getImplementations(fallbackTypeFqn); List<String> impls = context.getImplementations(fallbackTypeFqn);
for (String impl : impls) { for (String impl : impls) {
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(impl + "." + emr.getName().getIdentifier(), args)); CallEdge implEdge = new CallEdge(impl + "." + emr.getName().getIdentifier(), args);
implEdge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(implEdge);
} }
} }
} }
@@ -110,7 +130,10 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
calledMethod = superFqn + "." + methodName; calledMethod = superFqn + "." + methodName;
} }
List<String> args = resolveArguments(node.arguments()); List<String> args = resolveArguments(node.arguments());
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args)); String constraint = click.kamil.springstatemachineexporter.ast.common.AstUtils.findConditionConstraint(node);
CallEdge edge = new CallEdge(calledMethod, args);
edge.setConstraint(constraint);
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(edge);
} }
} }
} }
@@ -119,6 +142,7 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} }
}); });
} }
context.getCache().put("jdtCallGraph", graph);
return graph; return graph;
} }
@@ -143,27 +167,11 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} }
// Delegate to base class for lambda, method reference, and constructor unwrapping // Delegate to base class for lambda, method reference, and constructor unwrapping
return super.resolveArgument(expr); String result = super.resolveArgument(expr);
} if (result != null) {
result = result.replaceAll("->\\s*yield\\s+", "-> ");
private List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
String baseCalled = resolveCalledMethod(node);
if (baseCalled == null) return Collections.emptyList();
List<String> allResolved = new ArrayList<>();
allResolved.add(baseCalled);
if (baseCalled.contains(".")) {
String className = baseCalled.substring(0, baseCalled.lastIndexOf('.'));
String methodName = baseCalled.substring(baseCalled.lastIndexOf('.') + 1);
List<String> impls = context.getImplementations(className);
for (String impl : impls) {
allResolved.add(impl + "." + methodName);
}
} }
return result;
return allResolved;
} }
protected String resolveCalledMethod(MethodInvocation node) { protected String resolveCalledMethod(MethodInvocation node) {
@@ -193,16 +201,16 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
nameBinding = fa.resolveFieldBinding(); nameBinding = fa.resolveFieldBinding();
} }
if (nameBinding instanceof IVariableBinding varBinding) { if (nameBinding instanceof IVariableBinding varBinding) {
System.out.println("CALLGRAPH RESOLVING BEAN FOR BINDING: " + varBinding.getName() + " calling " + methodName); log.debug("CALLGRAPH RESOLVING BEAN FOR BINDING: {} calling {}", varBinding.getName(), methodName);
String concreteFqn = injectionAnalyzer.resolveInjectedBeanFqn(varBinding); String concreteFqn = injectionAnalyzer.resolveInjectedBeanFqn(varBinding);
if (concreteFqn != null) { if (concreteFqn != null) {
System.out.println(" -> RESOLVED CONCRETE FQN: " + concreteFqn); log.debug(" -> RESOLVED CONCRETE FQN: {}", concreteFqn);
return concreteFqn + "." + methodName; return concreteFqn + "." + methodName;
} else { } else {
System.out.println(" -> RESOLVE FAILED, RETURNED NULL"); log.debug(" -> RESOLVE FAILED, RETURNED NULL");
} }
} else { } else {
System.out.println("CALLGRAPH NAME BINDING IS NOT VARIABLE: " + nameBinding + " calling " + methodName); log.debug("CALLGRAPH NAME BINDING IS NOT VARIABLE: {} calling {}", nameBinding, methodName);
} }
} }
@@ -231,7 +239,10 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} }
if (typeName != null && !typeName.isEmpty()) { if (typeName != null && !typeName.isEmpty()) {
return typeName + "." + methodName; org.eclipse.jdt.core.dom.TypeDeclaration resolvedTd = context.getTypeDeclaration(typeName);
if (resolvedTd != null && context.findMethodDeclaration(resolvedTd, methodName, true) != null) {
return typeName + "." + methodName;
}
} }
} }
@@ -379,6 +390,14 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
} }
if (receiverType != null) { if (receiverType != null) {
String rawType = receiverType;
if (rawType.contains("<")) {
rawType = rawType.substring(0, rawType.indexOf('<'));
}
if (rawType.equals("java.util.Map") || rawType.equals("Map")) {
String valType = resolveMapValueType(mi);
if (valType != null) return valType;
}
if (receiverType.contains("<")) { if (receiverType.contains("<")) {
receiverType = receiverType.substring(0, receiverType.indexOf('<')); receiverType = receiverType.substring(0, receiverType.indexOf('<'));
} }

View File

@@ -29,6 +29,8 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
private final LifecycleDetector lifecycleDetector; private final LifecycleDetector lifecycleDetector;
private final InterceptorDetector interceptorDetector; private final InterceptorDetector interceptorDetector;
private final SpringComponentDetector componentDetector; private final SpringComponentDetector componentDetector;
private List<TriggerPoint> cachedTriggers;
private List<EntryPoint> cachedEntryPoints;
public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) { public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
this.context = context; this.context = context;
@@ -54,6 +56,9 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
@Override @Override
public List<TriggerPoint> findTriggerPoints() { public List<TriggerPoint> findTriggerPoints() {
if (cachedTriggers != null) {
return cachedTriggers;
}
List<TriggerPoint> allTriggers = new ArrayList<>(); List<TriggerPoint> allTriggers = new ArrayList<>();
var cus = context.getCompilationUnits(); var cus = context.getCompilationUnits();
log.info("JdtIntelligenceProvider scanning {} compilation units for triggers", cus.size()); log.info("JdtIntelligenceProvider scanning {} compilation units for triggers", cus.size());
@@ -62,11 +67,15 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
allTriggers.addAll(lifecycleDetector.detect(cu)); allTriggers.addAll(lifecycleDetector.detect(cu));
} }
log.info("Found {} triggers (including lifecycle) in total", allTriggers.size()); log.info("Found {} triggers (including lifecycle) in total", allTriggers.size());
cachedTriggers = allTriggers;
return allTriggers; return allTriggers;
} }
@Override @Override
public List<EntryPoint> findEntryPoints() { public List<EntryPoint> findEntryPoints() {
if (cachedEntryPoints != null) {
return cachedEntryPoints;
}
List<EntryPoint> allEntryPoints = new ArrayList<>(); List<EntryPoint> allEntryPoints = new ArrayList<>();
var cus = context.getCompilationUnits(); var cus = context.getCompilationUnits();
log.info("JdtIntelligenceProvider scanning {} compilation units for entry points", cus.size()); log.info("JdtIntelligenceProvider scanning {} compilation units for entry points", cus.size());
@@ -77,6 +86,7 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
allEntryPoints.addAll(componentDetector.detect(cu)); allEntryPoints.addAll(componentDetector.detect(cu));
} }
log.info("Found {} entry points (including interceptors and listeners) in total", allEntryPoints.size()); log.info("Found {} entry points (including interceptors and listeners) in total", allEntryPoints.size());
cachedEntryPoints = allEntryPoints;
return allEntryPoints; return allEntryPoints;
} }

View File

@@ -30,6 +30,21 @@ public class TypeResolver {
return -1; return -1;
} }
public String getParameterName(String methodFqn, int index) {
if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
TypeDeclaration td = context.getTypeDeclaration(className);
if (td != null) {
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
if (md != null && index < md.parameters().size()) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(index);
return svd.getName().getIdentifier();
}
}
return null;
}
public String getParameterType(String methodFqn, int index) { public String getParameterType(String methodFqn, int index) {
if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null; if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.')); String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));

View File

@@ -28,6 +28,16 @@ public class VariableTracer {
return all.isEmpty() ? expr : all.get(all.size() - 1); return all.isEmpty() ? expr : all.get(all.size() - 1);
} }
private Expression peelExpression(Expression expr) {
while (expr instanceof ParenthesizedExpression pe) {
expr = pe.getExpression();
}
if (expr instanceof CastExpression ce) {
return peelExpression(ce.getExpression());
}
return expr;
}
public List<Expression> traceVariableAll(Expression expr) { public List<Expression> traceVariableAll(Expression expr) {
return dataFlowModel.getReachingDefinitions(expr); return dataFlowModel.getReachingDefinitions(expr);
} }
@@ -82,6 +92,62 @@ public class VariableTracer {
return null; return null;
} }
private String getFieldType(TypeDeclaration td, String fieldName, CodebaseContext context, java.util.Set<String> visited) {
if (td == null) return null;
String typeFqn = context.getFqn(td);
if (!visited.add(typeFqn)) return null;
for (FieldDeclaration fd : td.getFields()) {
for (Object fragObj : fd.fragments()) {
VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
if (frag.getName().getIdentifier().equals(fieldName)) {
return fd.getType().toString();
}
}
}
String superFqn = context.getSuperclassFqn(td);
if (superFqn != null) {
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
if (superTd != null) {
String result = getFieldType(superTd, fieldName, context, visited);
if (result != null) return result;
}
}
for (Object intfObj : td.superInterfaceTypes()) {
Type intfType = (Type) intfObj;
String intfName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(intfType);
String resolvedIntf = resolveTypeFqn(intfName, context, td);
if (resolvedIntf != null) {
TypeDeclaration intfTd = context.getTypeDeclaration(resolvedIntf);
if (intfTd != null) {
String result = getFieldType(intfTd, fieldName, context, visited);
if (result != null) return result;
}
}
}
return null;
}
private String resolveTypeFqn(String simpleName, CodebaseContext context, TypeDeclaration td) {
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
if (cu != null) {
for (Object impObj : cu.imports()) {
ImportDeclaration imp = (ImportDeclaration) impObj;
String name = imp.getName().getFullyQualifiedName();
if (name.endsWith("." + simpleName)) {
return name;
}
}
if (cu.getPackage() != null) {
return cu.getPackage().getName().getFullyQualifiedName() + "." + simpleName;
}
}
return simpleName;
}
public String getVariableDeclaredType(String methodFqn, String cleanFieldName) { public String getVariableDeclaredType(String methodFqn, String cleanFieldName) {
if (methodFqn == null) return null; if (methodFqn == null) return null;
int lastDot = methodFqn.lastIndexOf('.'); int lastDot = methodFqn.lastIndexOf('.');
@@ -200,15 +266,9 @@ public class VariableTracer {
if (foundType[0] != null) return foundType[0]; if (foundType[0] != null) return foundType[0];
} }
// Fallback to fields // Fallback to fields including superclasses
for (FieldDeclaration fd : td.getFields()) { String fieldType = getFieldType(td, cleanFieldName, context, new java.util.HashSet<>());
for (Object fragObj : fd.fragments()) { if (fieldType != null) return fieldType;
VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
if (frag.getName().getIdentifier().equals(cleanFieldName)) {
return fd.getType().toString();
}
}
}
} }
} }
@@ -240,14 +300,14 @@ public class VariableTracer {
@Override @Override
public boolean visit(VariableDeclarationFragment node) { public boolean visit(VariableDeclarationFragment node) {
if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) { if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) {
initializers.add(node.getInitializer()); initializers.add(peelExpression(node.getInitializer()));
} }
return super.visit(node); return super.visit(node);
} }
@Override @Override
public boolean visit(Assignment node) { public boolean visit(Assignment node) {
if (node.getLeftHandSide() instanceof SimpleName asn && asn.getIdentifier().equals(varName)) { if (node.getLeftHandSide() instanceof SimpleName asn && asn.getIdentifier().equals(varName)) {
initializers.add(node.getRightHandSide()); initializers.add(peelExpression(node.getRightHandSide()));
} }
return super.visit(node); return super.visit(node);
} }
@@ -258,7 +318,7 @@ public class VariableTracer {
for (Expression expr : initializers) { for (Expression expr : initializers) {
Expression traced = traceVariable(expr); Expression traced = traceVariable(expr);
if (traced instanceof MethodInvocation mi) { if (traced instanceof MethodInvocation mi) {
Expression innerMost = unwrapMethodInvocation(mi, 0); Expression innerMost = unwrapMethodInvocation(mi, 0, methodFqn);
stringified.add(innerMost.toString()); stringified.add(innerMost.toString());
} else if (traced instanceof ArrayInitializer) { } else if (traced instanceof ArrayInitializer) {
stringified.add("new Object[]" + traced.toString()); stringified.add("new Object[]" + traced.toString());
@@ -266,14 +326,16 @@ public class VariableTracer {
stringified.add(traced.toString()); stringified.add(traced.toString());
} }
} }
if (stringified.size() == 1) return stringified.get(0); if (stringified.size() == 1) {
return stringified.get(0).replaceAll("->\\s*yield\\s+", "-> ");
}
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < stringified.size() - 1; i++) { for (int i = 0; i < stringified.size() - 1; i++) {
sb.append("true ? ").append(stringified.get(i)).append(" : "); sb.append("true ? ").append(stringified.get(i)).append(" : ");
} }
sb.append(stringified.get(stringified.size() - 1)); sb.append(stringified.get(stringified.size() - 1));
return sb.toString(); return sb.toString().replaceAll("->\\s*yield\\s+", "-> ");
} }
} }
@@ -385,6 +447,33 @@ public class VariableTracer {
public Map<String, String> buildParameterValuesMap(String caller, String target, Map<String, List<CallEdge>> callGraph, List<String> path, int pathIndex) { public Map<String, String> buildParameterValuesMap(String caller, String target, Map<String, List<CallEdge>> callGraph, List<String> path, int pathIndex) {
Map<String, String> paramValues = new HashMap<>(); Map<String, String> paramValues = new HashMap<>();
List<CallEdge> edges = callGraph.get(caller); List<CallEdge> edges = callGraph.get(caller);
boolean hasEdge = false;
if (edges != null) {
for (CallEdge edge : edges) {
if (edge.getTargetMethod().equals(target) || isHeuristicMatch(edge.getTargetMethod(), target)) {
hasEdge = true;
break;
}
}
}
// Bridge polymorphic interfaces to implementations without graph edges
if (!hasEdge) {
String callerSimple = caller.contains(".") ? caller.substring(caller.lastIndexOf('.') + 1) : caller;
String targetSimple = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target;
if (callerSimple.equals(targetSimple)) {
List<String> callerParams = getParameterNames(caller);
List<String> targetParams = getParameterNames(target);
if (callerParams != null && targetParams != null && callerParams.size() == targetParams.size()) {
for (int i = 0; i < callerParams.size(); i++) {
paramValues.put(targetParams.get(i), callerParams.get(i));
}
return paramValues;
}
}
}
if (edges == null) { if (edges == null) {
return paramValues; return paramValues;
} }
@@ -425,12 +514,40 @@ public class VariableTracer {
return paramValues; return paramValues;
} }
private List<String> getParameterNames(String methodFqn) {
int lastDot = methodFqn.lastIndexOf('.');
if (lastDot < 0) return null;
String className = methodFqn.substring(0, lastDot);
String methodName = methodFqn.substring(lastDot + 1);
TypeDeclaration td = context.getTypeDeclaration(className);
if (td == null) return null;
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
if (md == null) return null;
List<String> paramNames = new ArrayList<>();
for (Object paramObj : md.parameters()) {
SingleVariableDeclaration param = (SingleVariableDeclaration) paramObj;
paramNames.add(param.getName().getIdentifier());
}
return paramNames;
}
public String resolveArgumentValue(String argValue, String callerMethod, List<String> path, int pathIndex, Map<String, List<CallEdge>> callGraph) { public String resolveArgumentValue(String argValue, String callerMethod, List<String> path, int pathIndex, Map<String, List<CallEdge>> callGraph) {
if (argValue == null) return null; if (argValue == null) return null;
if (argValue.contains(".") || argValue.startsWith("new ") || argValue.matches(".*[0-9].*")) { if (argValue.contains(".") || argValue.startsWith("new ") || argValue.matches(".*[0-9].*") || (argValue.startsWith("\"") && argValue.endsWith("\""))) {
return argValue; return argValue;
} }
String tracedLocal = traceLocalVariable(callerMethod, argValue);
if (tracedLocal != null && !tracedLocal.equals(argValue)) {
if (tracedLocal.contains(".") || tracedLocal.startsWith("new ") || tracedLocal.matches(".*[0-9].*") || tracedLocal.contains("(") || (tracedLocal.startsWith("\"") && tracedLocal.endsWith("\""))) {
return tracedLocal;
}
argValue = tracedLocal;
}
int callerIndex = path.indexOf(callerMethod); int callerIndex = path.indexOf(callerMethod);
if (callerIndex <= 0) return argValue; if (callerIndex <= 0) return argValue;
@@ -489,12 +606,37 @@ public class VariableTracer {
if (simpleClassNeighbor.equals(simpleClassTarget)) return true; if (simpleClassNeighbor.equals(simpleClassTarget)) return true;
List<String> impls = context.getImplementations(classTarget); List<String> impls = context.getImplementations(classTarget);
if (impls != null && impls.contains(classNeighbor)) return true; if (impls != null) {
for (String impl : impls) {
if (impl.equals(classNeighbor) || impl.endsWith("." + classNeighbor)) return true;
}
}
List<String> implsN = context.getImplementations(classNeighbor); List<String> implsN = context.getImplementations(classNeighbor);
if (implsN != null && implsN.contains(classTarget)) return true; if (implsN != null) {
for (String impl : implsN) {
if (impl.equals(classTarget) || impl.endsWith("." + classTarget)) return true;
}
}
return false;
} }
return false;
String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target;
String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor;
if (!simpleNeighbor.equals(simpleTarget)) return false;
String classNeighbor = neighbor.contains(".") ? neighbor.substring(0, neighbor.lastIndexOf('.')) : null;
String classTarget = target.contains(".") ? target.substring(0, target.lastIndexOf('.')) : null;
String simpleClassTarget = classTarget != null && classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget;
String simpleClassNeighbor = classNeighbor != null && classNeighbor.contains(".") ? classNeighbor.substring(classNeighbor.lastIndexOf('.') + 1) : classNeighbor;
if (simpleClassNeighbor != null && simpleClassTarget != null) {
if (simpleClassNeighbor.equalsIgnoreCase(simpleClassTarget)) return true;
if (simpleClassNeighbor.equals("this") || simpleClassNeighbor.equals("super")) return true;
return false;
}
return true;
} }
private MethodDeclaration findEnclosingMethod(ASTNode node) { private MethodDeclaration findEnclosingMethod(ASTNode node) {
@@ -513,8 +655,33 @@ public class VariableTracer {
return (TypeDeclaration) parent; return (TypeDeclaration) parent;
} }
private Expression unwrapMethodInvocation(MethodInvocation mi, int depth) { private Expression unwrapMethodInvocation(MethodInvocation mi, int depth, String methodFqn) {
if (depth > 5) return mi; if (depth > 5) return mi;
String targetMethodFqn = resolveTargetMethodFqn(mi, methodFqn);
if (targetMethodFqn != null && targetMethodFqn.contains(".")) {
String className = targetMethodFqn.substring(0, targetMethodFqn.lastIndexOf('.'));
String methodName = targetMethodFqn.substring(targetMethodFqn.lastIndexOf('.') + 1);
TypeDeclaration currentTd = methodFqn != null && methodFqn.contains(".") ? context.getTypeDeclaration(methodFqn.substring(0, methodFqn.lastIndexOf('.'))) : null;
CompilationUnit cu = currentTd != null && currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
TypeDeclaration td = context.getTypeDeclaration(className, cu);
if (td != null) {
MethodDeclaration localMd = context.findMethodDeclaration(td, methodName, true);
if (localMd != null) {
int paramIdx = getReturnedParameterIndex(localMd);
if (paramIdx >= 0 && paramIdx < mi.arguments().size()) {
Expression arg = peelExpression((Expression) mi.arguments().get(paramIdx));
if (arg instanceof MethodInvocation innerMi) {
return unwrapMethodInvocation(innerMi, depth + 1, methodFqn);
}
return arg;
}
}
}
}
if (mi.getExpression() != null) { if (mi.getExpression() != null) {
String exprStr = mi.getExpression().toString(); String exprStr = mi.getExpression().toString();
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects") && !exprStr.equals("Mono") && !exprStr.equals("Flux")) { if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects") && !exprStr.equals("Mono") && !exprStr.equals("Flux")) {
@@ -538,12 +705,61 @@ public class VariableTracer {
return mi; return mi;
} }
Expression arg = (Expression) mi.arguments().get(0); Expression arg = peelExpression((Expression) mi.arguments().get(0));
if (arg instanceof MethodInvocation innerMi) { if (arg instanceof MethodInvocation innerMi) {
return unwrapMethodInvocation(innerMi, depth + 1); return unwrapMethodInvocation(innerMi, depth + 1, methodFqn);
} }
return arg; return arg;
} }
return mi; return mi;
} }
private String resolveTargetMethodFqn(MethodInvocation mi, String currentMethodFqn) {
if (currentMethodFqn == null || !currentMethodFqn.contains(".")) return null;
String currentClass = currentMethodFqn.substring(0, currentMethodFqn.lastIndexOf('.'));
if (mi.getExpression() == null || mi.getExpression().toString().equals("this")) {
return currentClass + "." + mi.getName().getIdentifier();
}
if (mi.getExpression() instanceof SimpleName sn) {
String declaredType = getVariableDeclaredType(currentMethodFqn, sn.getIdentifier());
if (declaredType != null) {
return declaredType + "." + mi.getName().getIdentifier();
}
} else if (mi.getExpression() instanceof FieldAccess fa) {
String declaredType = getVariableDeclaredType(currentMethodFqn, fa.getName().getIdentifier());
if (declaredType != null) {
return declaredType + "." + mi.getName().getIdentifier();
}
}
return null;
}
private int getReturnedParameterIndex(MethodDeclaration md) {
if (md.getBody() == null) return -1;
List<?> parameters = md.parameters();
if (parameters.isEmpty()) return -1;
final List<String> returnedExprs = new ArrayList<>();
md.getBody().accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement node) {
if (node.getExpression() != null) {
returnedExprs.add(node.getExpression().toString());
}
return super.visit(node);
}
});
if (returnedExprs.size() == 1) {
String retStr = returnedExprs.get(0);
for (int i = 0; i < parameters.size(); i++) {
if (parameters.get(i) instanceof SingleVariableDeclaration svd) {
if (svd.getName().getIdentifier().equals(retStr)) {
return i;
}
}
}
}
return -1;
}
} }

View File

@@ -17,11 +17,11 @@ public class SpringContextScanner extends ASTVisitor {
ITypeBinding typeBinding = node.resolveBinding(); ITypeBinding typeBinding = node.resolveBinding();
if (typeBinding == null) return true; if (typeBinding == null) return true;
if (isSpringComponent(typeBinding)) { if (isSpringComponent(typeBinding, node)) {
SpringBean bean = SpringBean.builder() SpringBean bean = SpringBean.builder()
.typeFqn(typeBinding.getQualifiedName()) .typeFqn(typeBinding.getQualifiedName())
.assignableTypes(getAssignableTypes(typeBinding)) .assignableTypes(getAssignableTypes(typeBinding))
.isPrimary(hasAnnotation(typeBinding, "org.springframework.context.annotation.Primary")) .isPrimary(hasPrimary(typeBinding, node))
.qualifiers(extractQualifiers(typeBinding)) .qualifiers(extractQualifiers(typeBinding))
.order(extractOrder(typeBinding)) .order(extractOrder(typeBinding))
.declaringClassFqn(typeBinding.getQualifiedName()) .declaringClassFqn(typeBinding.getQualifiedName())
@@ -73,10 +73,10 @@ public class SpringContextScanner extends ASTVisitor {
SpringBean bean = SpringBean.builder() SpringBean bean = SpringBean.builder()
.typeFqn(concreteType[0]) .typeFqn(concreteType[0])
.assignableTypes(assignables) .assignableTypes(assignables)
.isPrimary(hasAnnotation(methodBinding, "org.springframework.context.annotation.Primary")) .isPrimary(hasPrimaryMethod(methodBinding, node))
.qualifiers(extractQualifiers(methodBinding)) .qualifiers(extractQualifiers(methodBinding))
.order(extractOrder(methodBinding)) .order(extractOrder(methodBinding))
.declaringClassFqn(methodBinding.getDeclaringClass().getQualifiedName()) .declaringClassFqn(methodBinding.getDeclaringClass() != null ? methodBinding.getDeclaringClass().getQualifiedName() : null)
.factoryMethodName(node.getName().getIdentifier()) .factoryMethodName(node.getName().getIdentifier())
.build(); .build();
@@ -97,11 +97,50 @@ public class SpringContextScanner extends ASTVisitor {
return true; return true;
} }
private boolean isSpringComponent(ITypeBinding binding) { private boolean isSpringComponent(ITypeBinding binding, TypeDeclaration node) {
return hasMetaAnnotation(binding, "org.springframework.stereotype.Component") || if (hasMetaAnnotation(binding, "org.springframework.stereotype.Component") ||
hasMetaAnnotation(binding, "org.springframework.web.bind.annotation.RestController"); hasMetaAnnotation(binding, "org.springframework.web.bind.annotation.RestController") ||
hasAnnotation(binding, "org.springframework.stereotype.Component") ||
hasAnnotation(binding, "org.springframework.stereotype.Service") ||
hasAnnotation(binding, "org.springframework.stereotype.Repository") ||
hasAnnotation(binding, "org.springframework.stereotype.Controller") ||
hasAnnotation(binding, "org.springframework.web.bind.annotation.RestController") ||
hasAnnotation(binding, "org.springframework.context.annotation.Configuration")) {
return true;
}
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Component") || name.equals("Service") || name.equals("Repository") ||
name.equals("Controller") || name.equals("RestController") || name.equals("Configuration")) {
return true;
}
}
}
return false;
} }
private boolean hasPrimary(ITypeBinding binding, TypeDeclaration node) {
if (hasAnnotation(binding, "org.springframework.context.annotation.Primary")) return true;
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Primary")) return true;
}
}
return false;
}
private boolean hasPrimaryMethod(IMethodBinding binding, MethodDeclaration node) {
if (hasAnnotation(binding, "org.springframework.context.annotation.Primary")) return true;
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Primary")) return true;
}
}
return false;
}
private boolean hasMetaAnnotation(ITypeBinding binding, String targetFqn) { private boolean hasMetaAnnotation(ITypeBinding binding, String targetFqn) {
return checkMetaAnnotation(binding.getAnnotations(), targetFqn, new HashSet<>()); return checkMetaAnnotation(binding.getAnnotations(), targetFqn, new HashSet<>());
} }
@@ -202,9 +241,15 @@ public class SpringContextScanner extends ASTVisitor {
private String extractStereotypeValue(ITypeBinding binding) { private String extractStereotypeValue(ITypeBinding binding) {
for (IAnnotationBinding ann : binding.getAnnotations()) { for (IAnnotationBinding ann : binding.getAnnotations()) {
// Check if annotation itself is meta-annotated with @Component if (ann.getAnnotationType() == null) continue;
String fqn = ann.getAnnotationType().getQualifiedName();
if (checkMetaAnnotation(new IAnnotationBinding[]{ann}, "org.springframework.stereotype.Component", new HashSet<>()) || if (checkMetaAnnotation(new IAnnotationBinding[]{ann}, "org.springframework.stereotype.Component", new HashSet<>()) ||
"org.springframework.stereotype.Component".equals(ann.getAnnotationType().getQualifiedName())) { "org.springframework.stereotype.Component".equals(fqn) ||
"org.springframework.stereotype.Service".equals(fqn) ||
"org.springframework.stereotype.Repository".equals(fqn) ||
"org.springframework.stereotype.Controller".equals(fqn) ||
"org.springframework.web.bind.annotation.RestController".equals(fqn) ||
"org.springframework.context.annotation.Configuration".equals(fqn)) {
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) { for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
if ("value".equals(pair.getName()) && pair.getValue() instanceof String) { if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
return (String) pair.getValue(); return (String) pair.getValue();

View File

@@ -4,6 +4,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SpringDependencyResolver { public class SpringDependencyResolver {
private final SpringBeanRegistry registry; private final SpringBeanRegistry registry;
@@ -21,17 +24,17 @@ public class SpringDependencyResolver {
*/ */
public List<SpringBean> resolve(String requiredTypeFqn, String qualifier, String injectionName) { public List<SpringBean> resolve(String requiredTypeFqn, String qualifier, String injectionName) {
if (requiredTypeFqn == null) return new ArrayList<>(); if (requiredTypeFqn == null) return new ArrayList<>();
log.debug("RESOLVING {} qual={} injectionName={}", requiredTypeFqn, qualifier, injectionName);
// 1. Filter by Type (Exact FQN or Assignable Type) // 1. Filter by Type (Exact FQN or Assignable Type)
System.out.println("RESOLVING " + requiredTypeFqn + " qual=" + qualifier + " injectionName=" + injectionName);
List<SpringBean> candidates = registry.getBeans().stream() List<SpringBean> candidates = registry.getBeans().stream()
.filter(bean -> requiredTypeFqn.equals(bean.getTypeFqn()) || bean.getAssignableTypes().contains(requiredTypeFqn)) .filter(bean -> requiredTypeFqn.equals(bean.getTypeFqn()) || bean.getAssignableTypes().contains(requiredTypeFqn))
.collect(Collectors.toList()); .collect(Collectors.toList());
System.out.println("CANDIDATES AFTER TYPE: " + candidates.stream().map(c -> c.getTypeFqn() + " (primary=" + c.isPrimary() + " names=" + c.getBeanNames() + " qual=" + c.getQualifiers() + ")").collect(Collectors.toList())); log.debug("CANDIDATES AFTER TYPE: {}", candidates.stream().map(c -> c.getTypeFqn() + " (primary=" + c.isPrimary() + " names=" + c.getBeanNames() + " qual=" + c.getQualifiers() + ")").collect(Collectors.toList()));
if (candidates.isEmpty() || candidates.size() == 1) { if (candidates.isEmpty() || candidates.size() == 1) {
System.out.println("RETURNING: " + candidates.size()); log.debug("RETURNING: {}", candidates.size());
return candidates; return candidates;
} }
// 2. Filter by Qualifier (if provided) // 2. Filter by Qualifier (if provided)
@@ -81,7 +84,7 @@ public class SpringDependencyResolver {
} }
// Return whatever candidates remain (could be ambiguous) // Return whatever candidates remain (could be ambiguous)
System.out.println("RETURNING: " + candidates.size()); log.debug("RETURNING: {}", candidates.size());
return candidates; return candidates;
} }
} }

View File

@@ -210,7 +210,7 @@ public class AstTransitionParser {
Expression resolved = resolveArg((Expression) arg, argsMap); Expression resolved = resolveArg((Expression) arg, argsMap);
t.getTargetStates().add(context.resolveState(resolved, cu)); t.getTargetStates().add(context.resolveState(resolved, cu));
}); });
case "event" -> { case "event" -> {
Expression resolved = resolveArg((Expression) args.get(0), argsMap); Expression resolved = resolveArg((Expression) args.get(0), argsMap);
String eventValue = constantResolver.resolve(resolved, context); String eventValue = constantResolver.resolve(resolved, context);
if (eventValue != null) { if (eventValue != null) {
@@ -239,15 +239,31 @@ public class AstTransitionParser {
} else { } else {
t.setEvent(Event.of(QuotedExpression.of(resolved).toStringWithoutQuotes())); t.setEvent(Event.of(QuotedExpression.of(resolved).toStringWithoutQuotes()));
} }
if (args.size() > 1) {
Expression actionArg = resolveArg((Expression) args.get(1), argsMap);
parseAction(actionArg, t, cu, context);
}
} }
case "guard", "guardExpression" -> { case "guard", "guardExpression" -> {
Expression resolved = resolveArg((Expression) args.get(0), argsMap); Expression resolved = resolveArg((Expression) args.get(0), argsMap);
parseGuard(resolved, t, cu, context); parseGuard(resolved, t, cu, context);
} }
case "guards" -> {
args.forEach(arg -> {
Expression resolved = resolveArg((Expression) arg, argsMap);
parseGuard(resolved, t, cu, context);
});
}
case "action" -> { case "action" -> {
Expression resolved = resolveArg((Expression) args.get(0), argsMap); Expression resolved = resolveArg((Expression) args.get(0), argsMap);
parseAction(resolved, t, cu, context); parseAction(resolved, t, cu, context);
} }
case "actions" -> {
args.forEach(arg -> {
Expression resolved = resolveArg((Expression) arg, argsMap);
parseAction(resolved, t, cu, context);
});
}
case "timer", "timerOnce" -> { case "timer", "timerOnce" -> {
Expression resolved = resolveArg((Expression) args.get(0), argsMap); Expression resolved = resolveArg((Expression) args.get(0), argsMap);
t.setEvent(Event.of(methodName + "(" + resolved.toString() + ")", methodName + "(" + resolved.toString() + ")")); t.setEvent(Event.of(methodName + "(" + resolved.toString() + ")", methodName + "(" + resolved.toString() + ")"));
@@ -521,7 +537,7 @@ public class AstTransitionParser {
TypeDeclaration td = findEnclosingType(quotedExpr.getExpression()); TypeDeclaration td = findEnclosingType(quotedExpr.getExpression());
String fqn = td != null ? context.getFqn(td) : null; String fqn = td != null ? context.getFqn(td) : null;
String sourceFile = fqn != null ? context.getRelativePath(fqn) : null; String sourceFile = fqn != null ? context.getRelativePath(fqn) : null;
t.setGuard(Guard.of(quotedExpr.toStringWithoutQuotes(), isLambdaOrAnonymous(quotedExpr.getExpression()), internalLogic, lineNumber, fqn, sourceFile)); t.getGuards().add(Guard.of(quotedExpr.toStringWithoutQuotes(), isLambdaOrAnonymous(quotedExpr.getExpression()), internalLogic, lineNumber, fqn, sourceFile));
} }
} }

View File

@@ -95,4 +95,26 @@ public final class AstUtils {
return false; return false;
} }
public static String findConditionConstraint(org.eclipse.jdt.core.dom.ASTNode node) {
java.util.List<String> conditions = new java.util.ArrayList<>();
org.eclipse.jdt.core.dom.ASTNode current = node;
while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
org.eclipse.jdt.core.dom.ASTNode parent = current.getParent();
if (parent instanceof org.eclipse.jdt.core.dom.IfStatement ifStmt) {
if (ifStmt.getExpression() != current) {
org.eclipse.jdt.core.dom.Expression cond = ifStmt.getExpression();
if (current == ifStmt.getElseStatement()) {
conditions.add("!(" + cond.toString() + ")");
} else {
conditions.add(cond.toString());
}
}
}
current = parent;
}
if (conditions.isEmpty()) return null;
java.util.Collections.reverse(conditions);
return String.join(" && ", conditions);
}
} }

View File

@@ -22,9 +22,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import click.kamil.springstatemachineexporter.spi.SourceContext;
@Slf4j @Slf4j
public class CodebaseContext { public class CodebaseContext implements SourceContext {
private final Map<String, CompilationUnit> classes = new HashMap<>(); private final Map<String, CompilationUnit> classes = new HashMap<>();
private final Map<String, Path> classPaths = new HashMap<>(); private final Map<String, Path> classPaths = new HashMap<>();
private final Map<String, String> simpleNameToFqn = new HashMap<>(); private final Map<String, String> simpleNameToFqn = new HashMap<>();
@@ -41,11 +42,21 @@ public class CodebaseContext {
private List<LibraryHint> libraryHints = new ArrayList<>(); private List<LibraryHint> libraryHints = new ArrayList<>();
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private Path projectRoot; private Path projectRoot;
private final Map<String, Object> cache = new HashMap<>();
public Map<String, Object> getCache() {
return cache;
}
public void setProjectRoot(Path root) { public void setProjectRoot(Path root) {
this.projectRoot = root; this.projectRoot = root;
} }
@Override
public Path getProjectRoot() {
return projectRoot;
}
public String getRelativePath(Path fullPath) { public String getRelativePath(Path fullPath) {
if (projectRoot != null && fullPath.isAbsolute() && fullPath.startsWith(projectRoot)) { if (projectRoot != null && fullPath.isAbsolute() && fullPath.startsWith(projectRoot)) {
return projectRoot.relativize(fullPath).toString(); return projectRoot.relativize(fullPath).toString();
@@ -83,6 +94,10 @@ public class CodebaseContext {
this.classpath = classpath.toArray(new String[0]); this.classpath = classpath.toArray(new String[0]);
} }
public List<String> getClasspath() {
return java.util.Arrays.asList(classpath);
}
public void setSourcepath(List<String> sourcepath) { public void setSourcepath(List<String> sourcepath) {
this.sourcepath = sourcepath.toArray(new String[0]); this.sourcepath = sourcepath.toArray(new String[0]);
} }
@@ -285,17 +300,24 @@ public class CodebaseContext {
} }
public List<String> getImplementations(String interfaceName) { public List<String> getImplementations(String interfaceName) {
String cleanName = interfaceName;
if (interfaceName != null && interfaceName.contains("<")) {
cleanName = interfaceName.substring(0, interfaceName.indexOf('<'));
}
Set<String> allImpls = new HashSet<>(); Set<String> allImpls = new HashSet<>();
collectImplementations(interfaceName, allImpls, new HashSet<>()); collectImplementations(cleanName, allImpls, new HashSet<>());
System.out.println("GET IMPLEMENTATIONS FOR: " + interfaceName + " -> " + allImpls);
return new ArrayList<>(allImpls); return new ArrayList<>(allImpls);
} }
private void collectImplementations(String typeName, Set<String> results, Set<String> visited) { private void collectImplementations(String typeName, Set<String> results, Set<String> visited) {
if (!visited.add(typeName)) return; String cleanName = typeName;
if (typeName != null && typeName.contains("<")) {
cleanName = typeName.substring(0, typeName.indexOf('<'));
}
if (!visited.add(cleanName)) return;
// Try direct match // Try direct match
List<String> directImpls = interfaceToImpls.get(typeName); List<String> directImpls = interfaceToImpls.get(cleanName);
// Try FQN match if input was simple name // Try FQN match if input was simple name
if (directImpls == null) { if (directImpls == null) {
@@ -367,10 +389,12 @@ public class CodebaseContext {
cleanName = cleanName.trim(); cleanName = cleanName.trim();
List<String> values = enumValues.get(cleanName); List<String> values = enumValues.get(cleanName);
if (values != null) return values; if (values == null) {
String fqn = simpleNameToFqn.get(cleanName); String fqn = simpleNameToFqn.get(cleanName);
if (fqn != null) return enumValues.get(fqn); if (fqn != null) values = enumValues.get(fqn);
return null; }
log.debug("getEnumValues called for: {} resolved to {}. Returns: {}", fqnOrSimpleName, cleanName, values);
return values;
} }
public State resolveState(Expression expr, CompilationUnit cu) { public State resolveState(Expression expr, CompilationUnit cu) {

View File

@@ -3,12 +3,25 @@ package click.kamil.springstatemachineexporter.ast.common;
import org.eclipse.jdt.core.dom.*; import org.eclipse.jdt.core.dom.*;
import java.util.*; import java.util.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JdtDataFlowModel implements DataFlowModel { public class JdtDataFlowModel implements DataFlowModel {
private final CodebaseContext context; private final CodebaseContext context;
private final Map<MethodDeclaration, ControlFlowGraph> cfgCache = new HashMap<>(); private final Map<MethodDeclaration, ControlFlowGraph> cfgCache = new HashMap<>();
private final Map<MethodDeclaration, ReachingDefinitions> rdCache = new HashMap<>(); private final Map<MethodDeclaration, ReachingDefinitions> rdCache = new HashMap<>();
private final Map<ASTNode, Map<IVariableBinding, Expression>> objectFieldBindings = new HashMap<>(); private final Map<ASTNode, Map<IVariableBinding, Expression>> objectFieldBindings = new HashMap<>();
private static final ThreadLocal<List<String>> CURRENT_PATH = new ThreadLocal<>();
public static void setCurrentPath(List<String> path) {
CURRENT_PATH.set(path);
}
public static void clearCurrentPath() {
CURRENT_PATH.remove();
}
public JdtDataFlowModel(CodebaseContext context) { public JdtDataFlowModel(CodebaseContext context) {
this.context = context; this.context = context;
} }
@@ -82,14 +95,16 @@ public class JdtDataFlowModel implements DataFlowModel {
} }
} }
// 3. Handle Parameter mapping for SimpleName // 3. Handle Parameter Variable Bindings (Propagate values through parameters)
if (expr instanceof SimpleName sn) { if (expr instanceof SimpleName sn) {
IBinding b = sn.resolveBinding(); IBinding b = sn.resolveBinding();
if (b instanceof IVariableBinding paramVb && paramBindings.containsKey(paramVb)) { if (b instanceof IVariableBinding paramVb) {
Expression argExpr = paramBindings.get(paramVb); if (paramBindings.containsKey(paramVb)) {
List<Expression> resolved = getReachingDefinitions(argExpr, visited, paramBindings, instanceFieldBindings, depth + 1); Expression argExpr = paramBindings.get(paramVb);
visited.remove(expr); List<Expression> resolved = getReachingDefinitions(argExpr, visited, paramBindings, instanceFieldBindings, depth + 1);
return resolved; visited.remove(expr);
return resolved;
}
} }
// Fallback: Local Reaching Definitions // Fallback: Local Reaching Definitions
@@ -134,6 +149,44 @@ public class JdtDataFlowModel implements DataFlowModel {
return resolved; return resolved;
} }
// Handle SwitchExpression
if (expr instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
List<Expression> results = new ArrayList<>();
List<Expression> selectorDefs = getReachingDefinitions(se.getExpression(), visited, paramBindings, instanceFieldBindings, depth + 1);
String selectorVal = null;
if (!selectorDefs.isEmpty()) {
selectorVal = resolveValue(selectorDefs.get(0), context);
}
boolean active = (selectorVal == null);
for (Object stmtObj : se.statements()) {
if (stmtObj instanceof org.eclipse.jdt.core.dom.SwitchCase sc) {
if (selectorVal != null) {
active = false;
for (Object expObj : sc.expressions()) {
Expression caseExpr = (Expression) expObj;
String caseVal = resolveValue(caseExpr, context);
if (caseVal != null && (caseVal.equals(selectorVal) || selectorVal.endsWith("." + caseVal) || caseVal.endsWith("." + selectorVal))) {
active = true;
break;
}
}
if (sc.isDefault()) {
active = true;
}
}
} else if (active) {
if (stmtObj instanceof org.eclipse.jdt.core.dom.YieldStatement ys && ys.getExpression() != null) {
results.addAll(getReachingDefinitions(ys.getExpression(), visited, paramBindings, instanceFieldBindings, depth + 1));
} else if (stmtObj instanceof ExpressionStatement es) {
results.addAll(getReachingDefinitions(es.getExpression(), visited, paramBindings, instanceFieldBindings, depth + 1));
}
}
}
visited.remove(expr);
return results;
}
// 4. Handle Method Invocations (Static Factory methods / Transforming Getters) // 4. Handle Method Invocations (Static Factory methods / Transforming Getters)
if (expr instanceof MethodInvocation mi) { if (expr instanceof MethodInvocation mi) {
String mName = mi.getName().getIdentifier(); String mName = mi.getName().getIdentifier();
@@ -374,7 +427,7 @@ public class JdtDataFlowModel implements DataFlowModel {
MethodDeclaration cd = findMethodDeclaration(cb); MethodDeclaration cd = findMethodDeclaration(cb);
if (cd == null || cd.getBody() == null) return; if (cd == null || cd.getBody() == null) return;
Map<IVariableBinding, Expression> currentParamValues = new HashMap<>(); Map<IVariableBinding, Expression> currentParamValues = new HashMap<>(callerParamValues);
List<?> parameters = cd.parameters(); List<?> parameters = cd.parameters();
int count = Math.min(parameters.size(), arguments.size()); int count = Math.min(parameters.size(), arguments.size());
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@@ -387,6 +440,8 @@ public class JdtDataFlowModel implements DataFlowModel {
} }
} }
log.debug("EVAL_CTOR: cd={}, currentParamValues={}", cd.getName(), currentParamValues);
List<?> statements = cd.getBody().statements(); List<?> statements = cd.getBody().statements();
if (!statements.isEmpty()) { if (!statements.isEmpty()) {
Object firstStmt = statements.get(0); Object firstStmt = statements.get(0);
@@ -425,7 +480,12 @@ public class JdtDataFlowModel implements DataFlowModel {
if (fieldVb != null) { if (fieldVb != null) {
Expression resolvedRhs = resolveParamValue(rhs, currentParamValues); Expression resolvedRhs = resolveParamValue(rhs, currentParamValues);
fieldBindings.put(fieldVb, resolvedRhs); List<Expression> evaluated = getReachingDefinitions(resolvedRhs, new HashSet<>(), currentParamValues, fieldBindings, 0);
if (!evaluated.isEmpty()) {
fieldBindings.put(fieldVb, evaluated.get(0));
} else {
fieldBindings.put(fieldVb, resolvedRhs);
}
} }
return super.visit(assignment); return super.visit(assignment);
} }
@@ -533,6 +593,19 @@ public class JdtDataFlowModel implements DataFlowModel {
if (declaringClassFqn != null && !declaringClassFqn.isEmpty()) { if (declaringClassFqn != null && !declaringClassFqn.isEmpty()) {
// Find all known implementation subclasses in the codebase // Find all known implementation subclasses in the codebase
List<String> implClassFqns = context.getImplementations(declaringClassFqn); List<String> implClassFqns = context.getImplementations(declaringClassFqn);
List<String> currentPath = CURRENT_PATH.get();
if (currentPath != null && implClassFqns != null && !implClassFqns.isEmpty()) {
Set<String> contextTypes = getCompatibleContextTypes(currentPath, declaringClassFqn);
List<String> filteredImpls = new ArrayList<>();
for (String implFqn : implClassFqns) {
if (contextTypes.contains(implFqn)) {
filteredImpls.add(implFqn);
}
}
if (!filteredImpls.isEmpty()) {
implClassFqns = filteredImpls;
}
}
if (implClassFqns != null && !implClassFqns.isEmpty()) { if (implClassFqns != null && !implClassFqns.isEmpty()) {
for (String implFqn : implClassFqns) { for (String implFqn : implClassFqns) {
TypeDeclaration td = context.getTypeDeclaration(implFqn); TypeDeclaration td = context.getTypeDeclaration(implFqn);
@@ -895,4 +968,74 @@ public class JdtDataFlowModel implements DataFlowModel {
} }
return firstVal != null ? firstVal : context.resolveExpression(expr); return firstVal != null ? firstVal : context.resolveExpression(expr);
} }
private Set<String> getCompatibleContextTypes(List<String> currentPath, String declaringClassFqn) {
Set<String> contextTypes = new HashSet<>();
List<String> implementations = context.getImplementations(declaringClassFqn);
// 1. Direct class name from the path itself (prioritize concrete implementations in the path)
for (String methodFqn : currentPath) {
int lastDot = methodFqn.lastIndexOf('.');
if (lastDot > 0) {
String pathClass = methodFqn.substring(0, lastDot);
if (implementations.contains(pathClass)) {
contextTypes.add(pathClass);
}
}
}
if (!contextTypes.isEmpty()) {
return contextTypes;
}
// 2. Fallback: Inspect fields and local variables/parameters of the classes in the path
for (String methodFqn : currentPath) {
int lastDot = methodFqn.lastIndexOf('.');
if (lastDot > 0) {
String pathClass = methodFqn.substring(0, lastDot);
if (pathClass.equals(declaringClassFqn) || implementations.contains(pathClass)) {
contextTypes.add(pathClass);
}
TypeDeclaration td = context.getTypeDeclaration(pathClass);
if (td != null) {
// Check fields
for (FieldDeclaration fd : td.getFields()) {
String fieldType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(fd.getType());
TypeDeclaration fieldTd = context.getTypeDeclaration(fieldType);
if (fieldTd == null) {
CompilationUnit cu = (CompilationUnit) td.getRoot();
fieldTd = context.getTypeDeclaration(fieldType, cu);
}
if (fieldTd != null) {
String fieldFqn = context.getFqn(fieldTd);
if (fieldFqn.equals(declaringClassFqn) || implementations.contains(fieldFqn)) {
contextTypes.add(fieldFqn);
}
}
}
// Check method parameters
for (MethodDeclaration md : td.getMethods()) {
for (Object paramObj : md.parameters()) {
if (paramObj instanceof SingleVariableDeclaration svd) {
String paramType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(svd.getType());
TypeDeclaration paramTd = context.getTypeDeclaration(paramType);
if (paramTd == null) {
CompilationUnit cu = (CompilationUnit) td.getRoot();
paramTd = context.getTypeDeclaration(paramType, cu);
}
if (paramTd != null) {
String paramFqn = context.getFqn(paramTd);
if (paramFqn.equals(declaringClassFqn) || implementations.contains(paramFqn)) {
contextTypes.add(paramFqn);
}
}
}
}
}
}
}
}
return contextTypes;
}
} }

View File

@@ -107,13 +107,19 @@ public class Dot implements StateMachineExporter {
} }
} }
// Guard // Guards
if (t.getGuard() != null && !t.getGuard().expression().isBlank()) { if (t.getGuards() != null && !t.getGuards().isEmpty()) {
if (!label.isEmpty()) if (!label.isEmpty())
label.append(" "); label.append(" ");
String guardText = options.isUseLambdaGuards() && t.getGuard().isLambda() ? "λ" label.append("[");
: escapeDotString(t.getGuard().expression().replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim()); for (int i = 0; i < t.getGuards().size(); i++) {
label.append("[").append(guardText).append("]"); if (i > 0) label.append(" && ");
var guard = t.getGuards().get(i);
String guardText = options.isUseLambdaGuards() && guard.isLambda() ? "λ"
: escapeDotString(guard.expression().replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim());
label.append(guardText);
}
label.append("]");
} }
// Actions // Actions

View File

@@ -203,13 +203,20 @@ public class PlantUml implements StateMachineExporter {
parts.add(eventStr); parts.add(eventStr);
} }
} }
if (t.getGuard() != null) { if (t.getGuards() != null && !t.getGuards().isEmpty()) {
String g = t.getGuard().expression(); StringBuilder guardsBuilder = new StringBuilder();
if (options.isUseLambdaGuards() && t.getGuard().isLambda()) { guardsBuilder.append("[");
parts.add("[" + LAMBDA + "]"); for (int i = 0; i < t.getGuards().size(); i++) {
} else { if (i > 0) guardsBuilder.append(" && ");
parts.add("[" + g.replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim() + "]"); var guard = t.getGuards().get(i);
if (options.isUseLambdaGuards() && guard.isLambda()) {
guardsBuilder.append(LAMBDA);
} else {
guardsBuilder.append(guard.expression().replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim());
}
} }
guardsBuilder.append("]");
parts.add(guardsBuilder.toString());
} }
if (t.getActions() != null && !t.getActions().isEmpty()) { if (t.getActions() != null && !t.getActions().isEmpty()) {
parts.add("/ " + t.getActions().stream() parts.add("/ " + t.getActions().stream()

View File

@@ -102,12 +102,20 @@ public class Scxml implements StateMachineExporter {
} }
private static String getGuardText(Transition t, boolean useLambdaGuards) { private static String getGuardText(Transition t, boolean useLambdaGuards) {
if (t.getGuard() == null || t.getGuard().expression().isBlank()) if (t.getGuards() == null || t.getGuards().isEmpty())
return ""; return "";
if (useLambdaGuards && t.getGuard().isLambda()) {
return LAMBDA; StringBuilder sb = new StringBuilder();
for (int i = 0; i < t.getGuards().size(); i++) {
if (i > 0) sb.append(" && ");
var guard = t.getGuards().get(i);
if (useLambdaGuards && guard.isLambda()) {
sb.append(LAMBDA);
} else {
sb.append(guard.expression().replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim());
}
} }
return t.getGuard().expression().replaceAll("[\\n\\r]+", " ").replaceAll("\\s{2,}", " ").trim(); return sb.toString();
} }
private static String escapeXml(String s) { private static String escapeXml(String s) {

View File

@@ -16,7 +16,7 @@ public class Transition {
private List<State> targetStates = new ArrayList<>(); private List<State> targetStates = new ArrayList<>();
private Event event; private Event event;
private Guard guard; private List<Guard> guards = new ArrayList<>();
private List<Action> actions = new ArrayList<>(); private List<Action> actions = new ArrayList<>();
private Integer order = null; // optional order for withChoice or withJunction private Integer order = null; // optional order for withChoice or withJunction

View File

@@ -0,0 +1,147 @@
package click.kamil.springstatemachineexporter.plugin.java;
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
import click.kamil.springstatemachineexporter.analysis.service.CallGraphEngine;
import click.kamil.springstatemachineexporter.analysis.service.CodebaseIntelligenceProvider;
import click.kamil.springstatemachineexporter.analysis.service.JdtCallGraphEngine;
import click.kamil.springstatemachineexporter.analysis.service.JdtIntelligenceProvider;
import click.kamil.springstatemachineexporter.ast.app.AstTransitionParser;
import click.kamil.springstatemachineexporter.ast.app.StateMachineAggregator;
import click.kamil.springstatemachineexporter.ast.app.TransitionStateUtils;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import click.kamil.springstatemachineexporter.model.Transition;
import click.kamil.springstatemachineexporter.spi.LanguageAnalyzerPlugin;
import click.kamil.springstatemachineexporter.spi.SourceContext;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import java.nio.file.Path;
import java.util.*;
public class JavaLanguageAnalyzerPlugin implements LanguageAnalyzerPlugin {
@Override
public boolean supports(Path projectRoot) {
return true;
}
@Override
public SourceContext parseProject(Path projectRoot, List<String> classpath, Set<Path> sourcePaths) {
CodebaseContext context = new CodebaseContext();
context.setProjectRoot(projectRoot);
if (classpath != null && !classpath.isEmpty()) {
context.setClasspath(classpath);
}
Path hintsFile = projectRoot.resolve("hints.json");
if (!java.nio.file.Files.exists(hintsFile)) {
hintsFile = projectRoot.resolve("src/main/resources/hints.json");
}
if (java.nio.file.Files.exists(hintsFile)) {
try {
context.loadLibraryHints(hintsFile);
} catch (java.io.IOException e) {
throw new RuntimeException(e);
}
}
List<String> sp = sourcePaths.stream()
.map(p -> p.resolve("src/main/java"))
.filter(p -> java.nio.file.Files.exists(p))
.map(Path::toString)
.toList();
if (sp.isEmpty()) {
sp = sourcePaths.stream().map(Path::toString).toList();
}
context.setSourcepath(sp);
context.setResolveBindings(true);
try {
context.scan(sourcePaths, Collections.emptySet());
} catch (java.io.IOException e) {
throw new RuntimeException(e);
}
return context;
}
@Override
public CallGraphEngine createCallGraphEngine(SourceContext context) {
if (context instanceof CodebaseContext jdtContext) {
return new JdtCallGraphEngine(jdtContext, null);
}
throw new IllegalArgumentException("Expected CodebaseContext");
}
@Override
public CodebaseIntelligenceProvider createIntelligenceProvider(SourceContext context) {
if (context instanceof CodebaseContext jdtContext) {
return new JdtIntelligenceProvider(jdtContext, jdtContext.getProjectRoot());
}
throw new IllegalArgumentException("Expected CodebaseContext");
}
@Override
public List<AnalysisResult> extractStateMachines(SourceContext context, boolean renderChoicesAsDiamonds) {
if (context instanceof CodebaseContext jdtContext) {
List<AnalysisResult> results = new ArrayList<>();
// 1. Find entry point classes (annotated or extending adapter)
List<TypeDeclaration> entryPoints = jdtContext.findEntryPointClasses(List.of("EnableStateMachineFactory", "EnableStateMachine"));
for (TypeDeclaration td : entryPoints) {
String className = jdtContext.getFqn(td);
StateMachineAggregator aggregator = new StateMachineAggregator(jdtContext);
List<Transition> transitions = aggregator.aggregateTransitions(td);
aggregator.aggregateStates(td);
Set<String> initialStatesAst = aggregator.getInitialStates();
Set<String> endStatesAst = aggregator.getEndStates();
Set<String> startStates = TransitionStateUtils.findStartStates(transitions, initialStatesAst);
Set<String> endStates = TransitionStateUtils.findEndStates(transitions, endStatesAst);
Set<click.kamil.springstatemachineexporter.model.State> allStates = TransitionStateUtils.findAllStates(transitions, initialStatesAst, endStatesAst);
if (allStates.isEmpty() && transitions.isEmpty()) {
continue;
}
results.add(AnalysisResult.builder()
.name(className)
.states(allStates)
.transitions(transitions)
.startStates(startStates)
.endStates(endStates)
.renderChoicesAsDiamonds(renderChoicesAsDiamonds)
.build());
}
// 2. Find methods returning StateMachine beans
List<MethodDeclaration> beanMethods = jdtContext.findBeanMethodsReturning(Set.of("StateMachine", "StateMachineFactory", "StateMachineModelFactory"));
for (MethodDeclaration m : beanMethods) {
TypeDeclaration parentClass = (TypeDeclaration) m.getParent();
String parentFqn = jdtContext.getFqn(parentClass);
String methodName = m.getName().getIdentifier();
String uniqueName = parentFqn + "#" + methodName;
List<Transition> transitions = AstTransitionParser.parseTransitions(m, jdtContext);
Set<String> startStates = TransitionStateUtils.findStartStates(transitions, null);
Set<String> endStates = TransitionStateUtils.findEndStates(transitions, null);
Set<click.kamil.springstatemachineexporter.model.State> allStates = TransitionStateUtils.findAllStates(transitions, null, null);
if (allStates.isEmpty() && transitions.isEmpty()) {
continue;
}
results.add(AnalysisResult.builder()
.name(uniqueName)
.states(allStates)
.transitions(transitions)
.startStates(startStates)
.endStates(endStates)
.renderChoicesAsDiamonds(renderChoicesAsDiamonds)
.build());
}
return results;
}
return Collections.emptyList();
}
}

View File

@@ -20,6 +20,11 @@ import click.kamil.springstatemachineexporter.exporter.StateMachineExporter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration;
import click.kamil.springstatemachineexporter.spi.LanguageAnalyzerPlugin;
import click.kamil.springstatemachineexporter.spi.SourceContext;
import click.kamil.springstatemachineexporter.analysis.service.CompositeIntelligenceProvider;
import click.kamil.springstatemachineexporter.analysis.service.CallGraphEngine;
import click.kamil.springstatemachineexporter.analysis.service.JdtCallGraphEngine;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -137,8 +142,57 @@ public class ExportService {
context.scan(allPaths, Collections.emptySet()); context.scan(allPaths, Collections.emptySet());
CodebaseIntelligenceProvider intelligence = new JdtIntelligenceProvider(context, inputDir); // Load SPI plugins dynamically
exportAll(context, intelligence, outputDir, selectedFormats, renderChoicesAsDiamonds, flows, machineFilter, activeProfiles != null ? activeProfiles : Collections.emptyList(), eventFormat, stateFormat); ServiceLoader<LanguageAnalyzerPlugin> loader = ServiceLoader.load(LanguageAnalyzerPlugin.class);
List<LanguageAnalyzerPlugin> plugins = new ArrayList<>();
for (LanguageAnalyzerPlugin plugin : loader) {
if (plugin.supports(inputDir)) {
plugins.add(plugin);
}
}
if (plugins.isEmpty()) {
plugins.add(new click.kamil.springstatemachineexporter.plugin.java.JavaLanguageAnalyzerPlugin());
}
List<SourceContext> sourceContexts = new ArrayList<>();
List<CodebaseIntelligenceProvider> providers = new ArrayList<>();
List<CallGraphEngine> engines = new ArrayList<>();
List<AnalysisResult> extractedResults = new ArrayList<>();
for (LanguageAnalyzerPlugin plugin : plugins) {
SourceContext sc = plugin.parseProject(projectRoot, context.getClasspath(), allPaths);
sourceContexts.add(sc);
providers.add(plugin.createIntelligenceProvider(sc));
engines.add(plugin.createCallGraphEngine(sc));
extractedResults.addAll(plugin.extractStateMachines(sc, renderChoicesAsDiamonds));
}
CodebaseContext codebaseContext = context;
CodebaseIntelligenceProvider unifiedIntelligence;
if (plugins.size() == 1) {
unifiedIntelligence = providers.get(0);
if (sourceContexts.get(0) instanceof CodebaseContext jdtCtx) {
codebaseContext = jdtCtx;
}
} else {
unifiedIntelligence = new CompositeIntelligenceProvider(providers, engines);
}
// Process all extracted state machines
for (AnalysisResult result : extractedResults) {
String name = result.getName();
if (machineFilter != null && !name.contains(machineFilter)) {
continue;
}
if (result.getFlows() == null || result.getFlows().isEmpty()) {
result.setFlows(flows);
}
enrichmentService.enrich(result, codebaseContext, unifiedIntelligence);
resolveProperties(result, activeProfiles);
generateOutputs(outputDir, result, selectedFormats, eventFormat, stateFormat);
}
} }
public void runJsonExporter(Path jsonFile, Path outputDir, List<String> selectedFormats) throws IOException { public void runJsonExporter(Path jsonFile, Path outputDir, List<String> selectedFormats) throws IOException {

View File

@@ -0,0 +1,43 @@
package click.kamil.springstatemachineexporter.spi;
import click.kamil.springstatemachineexporter.analysis.service.CallGraphEngine;
import click.kamil.springstatemachineexporter.analysis.service.CodebaseIntelligenceProvider;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
/**
* Service Provider Interface (SPI) for introducing support for different languages (Java, Kotlin, etc.).
*/
public interface LanguageAnalyzerPlugin {
/**
* Determines if this plugin is capable of handling the provided project root.
* Often checks for file extensions (.kt, .java) within the root.
*/
boolean supports(Path projectRoot);
/**
* Parses the codebase and sets up a SourceContext loaded with the language's AST structures.
*/
SourceContext parseProject(Path projectRoot, List<String> classpath, Set<Path> sourcePaths);
/**
* Creates a CallGraphEngine specialized for this language's syntax and paradigms.
*/
CallGraphEngine createCallGraphEngine(SourceContext context);
/**
* Creates a CodebaseIntelligenceProvider specialized for this language to discover
* Spring State Machine configurations, entry points, and transitions.
*/
CodebaseIntelligenceProvider createIntelligenceProvider(SourceContext context);
/**
* Finds and extracts all Spring State Machine models (configurations and beans) from the project.
*/
default List<click.kamil.springstatemachineexporter.analysis.model.AnalysisResult> extractStateMachines(SourceContext context, boolean renderChoicesAsDiamonds) {
return java.util.Collections.emptyList();
}
}

View File

@@ -0,0 +1,43 @@
package click.kamil.springstatemachineexporter.spi;
import click.kamil.springstatemachineexporter.analysis.model.LibraryHint;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
/**
* An abstraction over a parsed source codebase (Java, Kotlin, etc.).
* Provides access to common metadata required by analysis engines.
*/
public interface SourceContext {
/**
* Gets the root directory of the scanned project.
*/
Path getProjectRoot();
/**
* Relativizes an absolute path against the project root.
*/
String getRelativePath(Path fullPath);
/**
* Returns a relative file path for a given fully qualified class/type name.
*/
String getRelativePath(String fqn);
/**
* Retrieves any loaded library hints (e.g. from hints.json).
*/
List<LibraryHint> getLibraryHints();
/**
* Retrieves application properties (e.g. from application.yml).
*/
Map<String, Map<String, String>> getProperties();
/**
* Gets an unstructured cache map that plugins can use to share data across processing phases.
*/
Map<String, Object> getCache();
}

View File

@@ -158,6 +158,24 @@ public class GoldenUpdater {
Path.of("state_machines/inheritance_extra_functions3_state_machine"), Path.of("state_machines/inheritance_extra_functions3_state_machine"),
Path.of("src/test/resources/golden/G2StateMachineConfiguration"), Path.of("src/test/resources/golden/G2StateMachineConfiguration"),
"G2StateMachineConfiguration" "G2StateMachineConfiguration"
),
new TestScenario(
"Enterprise Order State Machine",
Path.of("state_machines/state_machine_enterprise"),
Path.of("src/test/resources/golden/OrderStateMachineConfiguration"),
"OrderStateMachineConfiguration"
),
new TestScenario(
"Enterprise Document State Machine",
Path.of("state_machines/state_machine_enterprise"),
Path.of("src/test/resources/golden/DocumentStateMachineConfiguration"),
"DocumentStateMachineConfiguration"
),
new TestScenario(
"Enterprise User State Machine",
Path.of("state_machines/state_machine_enterprise"),
Path.of("src/test/resources/golden/UserStateMachineConfiguration"),
"UserStateMachineConfiguration"
) )
); );
} }

View File

@@ -0,0 +1,17 @@
package click.kamil.springstatemachineexporter;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestTriggerPoint {
public static void main(String[] args) throws Exception {
TriggerPoint tp = TriggerPoint.builder()
.event("PLACE_ORDER")
.eventTypeFqn("String")
.build();
System.out.println("Result event: " + tp.getEvent());
ObjectMapper mapper = new ObjectMapper();
System.out.println("JSON: " + mapper.writeValueAsString(tp));
}
}

View File

@@ -196,7 +196,7 @@ class TransitionLinkerEnricherTest {
enricher.enrich(result, null, null); enricher.enrich(result, null, null);
CallChain updatedChain = result.getMetadata().getCallChains().get(0); CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).isNull(); assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
} }
@Test @Test
@@ -248,6 +248,31 @@ class TransitionLinkerEnricherTest {
assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty(); assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty();
} }
@Test
void shouldExecuteDeterministicallyAndUtilizeInternalCaches() {
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
t1.setEvent(Event.of("PAY", "PAY"));
CallChain chain1 = CallChain.builder().triggerPoint(TriggerPoint.builder().event("PAY").build()).build();
CallChain chain2 = CallChain.builder().triggerPoint(TriggerPoint.builder().event("PAY").build()).build();
CallChain chain3 = CallChain.builder().triggerPoint(TriggerPoint.builder().event("PAY").build()).build();
AnalysisResult result = AnalysisResult.builder()
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1, chain2, chain3)).build())
.build();
enricher.enrich(result, null, null);
// All 3 identical trigger point chains should be linked seamlessly using the memoization cache.
for (CallChain chain : result.getMetadata().getCallChains()) {
assertThat(chain.getMatchedTransitions()).hasSize(1);
assertThat(chain.getMatchedTransitions().get(0).getEvent()).isEqualTo("PAY");
}
}
@Test @Test
void shouldFilterCrossStateMachineRoutingByVertical() { void shouldFilterCrossStateMachineRoutingByVertical() {
@@ -408,4 +433,83 @@ class TransitionLinkerEnricherTest {
assertThat(resComp.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc assertThat(resComp.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc
} }
@Test
void shouldHandleMultipleNegations() {
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
t1.setEvent(Event.of("PAY", "PAY"));
// Case 1: Double Negation on ORDER -> !(!(ORDER)) -> Should act as POSITIVE (meaning order is required).
// Since we are running OrderStateMachineConfiguration, it should match!
CallChain chain1 = CallChain.builder()
.triggerPoint(TriggerPoint.builder()
.event("PAY")
.constraint("!(!(ORDER.equals(type)))")
.build())
.build();
AnalysisResult resultOrder = AnalysisResult.builder()
.name("com.example.OrderStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
.build();
enricher.enrich(resultOrder, null, null);
assertThat(resultOrder.getMetadata().getCallChains().get(0).getMatchedTransitions()).hasSize(1);
// Case 2: Triple Negation on ORDER -> !(!(!(ORDER))) -> Should act as NEGATIVE (meaning order is rejected/forbidden).
// Under OrderStateMachineConfiguration, this chain should be rejected!
CallChain chain2 = CallChain.builder()
.triggerPoint(TriggerPoint.builder()
.event("PAY")
.constraint("!(!(!(ORDER.equals(type))))")
.build())
.build();
AnalysisResult resultOrder2 = AnalysisResult.builder()
.name("com.example.OrderStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain2)).build())
.build();
enricher.enrich(resultOrder2, null, null);
assertThat(resultOrder2.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty();
}
@Test
void shouldHandleComplexBooleanLogic() {
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
t1.setEvent(Event.of("PAY", "PAY"));
// Case 1: OR expression -> "ORDER".equals(type) || "DOCUMENT".equals(type)
// Under OrderStateMachineConfiguration, it should match!
CallChain chain1 = CallChain.builder()
.triggerPoint(TriggerPoint.builder()
.event("PAY")
.constraint("\"ORDER\".equalsIgnoreCase(type) || \"DOCUMENT\".equalsIgnoreCase(type)")
.build())
.build();
AnalysisResult resultOrder = AnalysisResult.builder()
.name("com.example.OrderStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
.build();
enricher.enrich(resultOrder, null, null);
assertThat(resultOrder.getMetadata().getCallChains().get(0).getMatchedTransitions()).hasSize(1);
// Case 2: Under UserStateMachineConfiguration, it should be rejected!
AnalysisResult resultUser = AnalysisResult.builder()
.name("com.example.UserStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
.build();
enricher.enrich(resultUser, null, null);
assertThat(resultUser.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty();
}
} }

View File

@@ -125,4 +125,26 @@ class HeuristicEventMatchingEngineTest {
assertThat(engine.matches(smEvent, triggerPoint)).isFalse(); assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
} }
@Test
void shouldMatchWildcardIfTypeMatches() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("event")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldNotMatchWildcardIfTypeMismatches() {
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("event")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
} }

View File

@@ -0,0 +1,209 @@
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.model.Event;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class StrictFqnMatchingEngineTest {
private final StrictFqnMatchingEngine engine = new StrictFqnMatchingEngine();
@Test
void shouldMatchExactFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("PAY")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchEnumQualifierToFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.PAY")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldPreventCrossEnumContamination() {
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.PAY")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldPreventCrossEnumContaminationWithPolymorphicEvents() {
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("getType()")
.eventTypeFqn("com.example.OrderEvents")
.polymorphicEvents(List.of("OrderEvents.PAY"))
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldMatchPolymorphicEnumQualifierToFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("getType()")
.eventTypeFqn("com.example.OrderEvents")
.polymorphicEvents(List.of("OrderEvents.PAY"))
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchRawStringToFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("PAY")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchRawStringToRawString() {
Event smEvent = Event.of("PAY", "PAY");
TriggerPoint triggerPoint = TriggerPoint.builder().event("PAY").build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchPolymorphicRawStringToFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("getType()")
.eventTypeFqn("com.example.OrderEvents")
.polymorphicEvents(List.of("PAY"))
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchWildcards() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder().event("event").build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldNotMatchMismatchedFqn() {
Event smEvent = Event.of("CREATE", "com.example.other.OrderEvent.CREATE");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("CREATE")
.eventTypeFqn("com.example.some.OrderEvent")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldNotMatchStringTypeToEnumFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("PAY")
.eventTypeFqn("java.lang.String")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldNotMatchEnumTypeToStringFqn() {
Event smEvent = Event.of("PAY", "PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.PAY")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldMatchValueOfWithCorrectEnum() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.valueOf(str)")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldNotMatchValueOfWithIncorrectEnum() {
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.valueOf(str)")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldMatchDynamicVariableWithMatchingTypeFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("myCustomEvt") // Not "event", "e", etc., but a custom variable name
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldNotMatchDynamicVariableWithMismatchedTypeFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("myCustomEvt")
.eventTypeFqn("com.example.InvoiceEvents") // Mismatched type
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldMatchDynamicVariableWithUnknownTypeFqn() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("myCustomEvt")
.eventTypeFqn(null) // Unknown type, fallback to true
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
@Test
void shouldMatchDynamicMethodCallAsVariable() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("event.getPayload()") // Dynamic method call
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
}

View File

@@ -482,4 +482,46 @@ public class ConstantResolverTest {
assertThat(result).contains("OrderEvents.B2"); assertThat(result).contains("OrderEvents.B2");
assertThat(result).contains("OrderEvents.DEF"); assertThat(result).contains("OrderEvents.DEF");
} }
@Test
void shouldResolveEnumValueOfWithConstantString(@TempDir Path tempDir) throws IOException {
Path dir = tempDir.resolve("com/example");
Files.createDirectories(dir);
Files.writeString(dir.resolve("OrderEvent.java"),
"package com.example;\n" +
"public enum OrderEvent { RECEIVED, SHIPPED }\n");
Files.writeString(dir.resolve("Caller.java"),
"package com.example;\n" +
"public class Caller {\n" +
" public void call() {\n" +
" OrderEvent e1 = OrderEvent.valueOf(\"RECEIVED\");\n" +
" OrderEvent e2 = OrderEvent.valueOf(getEventString());\n" +
" }\n" +
" private String getEventString() {\n" +
" return \"SHIPPED\";\n" +
" }\n" +
"}");
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
TypeDeclaration callerTd = context.getTypeDeclaration("com.example.Caller");
MethodDeclaration callMethod = callerTd.getMethods()[0];
VariableDeclarationStatement vds1 = (VariableDeclarationStatement) callMethod.getBody().statements().get(0);
VariableDeclarationFragment fragment1 = (VariableDeclarationFragment) vds1.fragments().get(0);
MethodInvocation mi1 = (MethodInvocation) fragment1.getInitializer();
VariableDeclarationStatement vds2 = (VariableDeclarationStatement) callMethod.getBody().statements().get(1);
VariableDeclarationFragment fragment2 = (VariableDeclarationFragment) vds2.fragments().get(0);
MethodInvocation mi2 = (MethodInvocation) fragment2.getInitializer();
ConstantResolver resolver = new ConstantResolver();
String result1 = resolver.resolve(mi1, context);
assertThat(result1).isEqualTo("com.example.OrderEvent.RECEIVED");
String result2 = resolver.resolve(mi2, context);
assertThat(result2).isEqualTo("com.example.OrderEvent.SHIPPED");
}
} }

View File

@@ -0,0 +1,40 @@
package click.kamil.springstatemachineexporter.analysis.resolver;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RelativePathTest {
@Test
public void testRelativePathDirectory(@TempDir Path tempDir) throws Exception {
Path rootDir = tempDir.resolve("root");
Path childDir = tempDir.resolve("child");
Files.createDirectories(rootDir);
Files.createDirectories(childDir);
Files.writeString(rootDir.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>root</artifactId></project>");
Files.writeString(childDir.resolve("pom.xml"), "<project><parent><relativePath>../root</relativePath></parent></project>");
SiblingDependencyResolver resolver = new SiblingDependencyResolver();
Set<Path> siblings = resolver.resolveSiblings(childDir);
assertTrue(siblings.contains(rootDir), "Should resolve parent directory from <relativePath>");
}
@Test
public void testRelativePathFile(@TempDir Path tempDir) throws Exception {
Path rootDir = tempDir.resolve("root");
Path childDir = tempDir.resolve("child");
Files.createDirectories(rootDir);
Files.createDirectories(childDir);
Files.writeString(rootDir.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>root</artifactId></project>");
Files.writeString(childDir.resolve("pom.xml"), "<project><parent><relativePath>../root/pom.xml</relativePath></parent></project>");
SiblingDependencyResolver resolver = new SiblingDependencyResolver();
Set<Path> siblings = resolver.resolveSiblings(childDir);
assertTrue(siblings.contains(rootDir), "Should resolve parent directory from <relativePath> file");
}
}

View File

@@ -0,0 +1,102 @@
package click.kamil.springstatemachineexporter.analysis.resolver;
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 java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
class SiblingDependencyResolverTest {
@Test
void shouldResolveGradleSiblingsTransitively(@TempDir Path tempDir) throws IOException {
// Create root project
Path rootDir = tempDir.resolve("my-root-project");
Files.createDirectories(rootDir);
Files.writeString(rootDir.resolve("settings.gradle"), "include 'module-a', 'module-b', 'module-c'");
// Create module A (depends on B)
Path moduleA = rootDir.resolve("module-a");
Files.createDirectories(moduleA);
Files.writeString(moduleA.resolve("build.gradle"), "dependencies { implementation project(':module-b') }");
// Create module B (depends on C)
Path moduleB = rootDir.resolve("module-b");
Files.createDirectories(moduleB);
Files.writeString(moduleB.resolve("build.gradle"), "dependencies { implementation project(':module-c') }");
// Create module C (no deps)
Path moduleC = rootDir.resolve("module-c");
Files.createDirectories(moduleC);
Files.writeString(moduleC.resolve("build.gradle"), "");
SiblingDependencyResolver resolver = new SiblingDependencyResolver();
// When we resolve from A, it should transitively find B and C
Set<Path> siblings = resolver.resolveSiblings(moduleA);
assertThat(siblings).containsExactlyInAnyOrder(moduleB.normalize(), moduleC.normalize());
}
@Test
void shouldResolveMavenSiblingsTransitively(@TempDir Path tempDir) throws IOException {
// Create root project
Path rootDir = tempDir.resolve("my-maven-root");
Files.createDirectories(rootDir);
Files.writeString(rootDir.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>root</artifactId></project>");
// Create a dummy .git so findMavenRoot knows where to stop
Files.createDirectories(rootDir.resolve(".git"));
// Create module A (depends on B)
Path moduleA = rootDir.resolve("module-a");
Files.createDirectories(moduleA);
Files.writeString(moduleA.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>module-a</artifactId><dependencies><dependency><groupId>com.acme</groupId><artifactId>module-b</artifactId><version>1.0</version></dependency></dependencies></project>");
// Create module B (depends on C)
Path moduleB = rootDir.resolve("module-b");
Files.createDirectories(moduleB);
Files.writeString(moduleB.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>module-b</artifactId><dependencies><dependency><groupId>com.acme</groupId><artifactId>module-c</artifactId><version>1.0</version></dependency></dependencies></project>");
// Create module C (no deps)
Path moduleC = rootDir.resolve("module-c");
Files.createDirectories(moduleC);
Files.writeString(moduleC.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>module-c</artifactId></project>");
SiblingDependencyResolver resolver = new SiblingDependencyResolver();
// When we resolve from A, it should transitively find B and C and the parent rootDir
Set<Path> siblings = resolver.resolveSiblings(moduleA);
assertThat(siblings).containsExactlyInAnyOrder(moduleB.normalize(), moduleC.normalize(), rootDir.normalize());
}
@Test
void shouldResolveMavenModulesFromAggregator(@TempDir Path tempDir) throws IOException {
// Create root project
Path rootDir = tempDir.resolve("my-aggregator-root");
Files.createDirectories(rootDir);
Files.writeString(rootDir.resolve("pom.xml"), "<project><groupId>com.acme</groupId><artifactId>root</artifactId><modules><module>service-x</module><module>service-y</module></modules></project>");
Files.createDirectories(rootDir.resolve(".git"));
// Create service X
Path serviceX = rootDir.resolve("service-x");
Files.createDirectories(serviceX);
Files.writeString(serviceX.resolve("pom.xml"), "<project><artifactId>service-x</artifactId></project>");
// Create service Y
Path serviceY = rootDir.resolve("service-y");
Files.createDirectories(serviceY);
Files.writeString(serviceY.resolve("pom.xml"), "<project><artifactId>service-y</artifactId></project>");
SiblingDependencyResolver resolver = new SiblingDependencyResolver();
// Resolving from rootDir should pick up the <module> tags and resolve service-x and service-y
Set<Path> siblings = resolver.resolveSiblings(rootDir);
assertThat(siblings).containsExactlyInAnyOrder(serviceX.normalize(), serviceY.normalize());
}
}

View File

@@ -66,4 +66,59 @@ class BuilderLocalVariableTest {
assertThat(chain.getTriggerPoint().getPolymorphicEvents()) assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("OrderEvents.PAY"); .containsExactlyInAnyOrder("OrderEvents.PAY");
} }
@Test
void shouldTraceFluentBuilder(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class MyController {
private MyService service;
public void processEvent() {
MyEvent event = MyEvent.builder().type(MyEnum.STATE_2).build();
service.process(event.getType());
}
}
class MyService {
public void process(MyEnum event) {}
}
class MyEvent {
private MyEnum type;
public static Builder builder() { return new Builder(); }
public MyEnum getType() { return type; }
public static class Builder {
private MyEnum type;
public Builder type(MyEnum type) { this.type = type; return this; }
public MyEvent build() { return new MyEvent(); }
}
}
enum MyEnum { STATE_1, STATE_2 }
""";
Files.writeString(tempDir.resolve("MyConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.MyController")
.methodName("processEvent")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.MyService")
.methodName("process")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
CallChain chain = chains.get(0);
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("MyEnum.STATE_2");
}
} }

View File

@@ -0,0 +1,81 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
class CallGraphPathFinderTest {
private CallGraphPathFinder pathFinder;
@BeforeEach
void setUp() {
pathFinder = new CallGraphPathFinder(new CodebaseContext());
}
@Test
void shouldSkipFrameworkAndJdkPaths() {
Map<String, List<CallEdge>> graph = new HashMap<>();
// Setup an anonymized call graph where business logic delegates to a platform boundary
// e.g. BusinessService.process -> java.util.stream.Stream.map -> AnotherBusinessService.doWork
// The pathfinder should skip the java.* path, avoiding spurious hops.
graph.put("com.acme.BusinessService.process", List.of(
new CallEdge("java.util.stream.Stream.map", List.of()),
new CallEdge("com.acme.DirectService.execute", List.of())
));
graph.put("java.util.stream.Stream.map", List.of(
new CallEdge("com.acme.AnotherBusinessService.doWork", List.of())
));
graph.put("com.acme.DirectService.execute", List.of(
new CallEdge("com.acme.AnotherBusinessService.doWork", List.of())
));
List<List<String>> paths = pathFinder.findAllPaths(
"com.acme.BusinessService.process",
"com.acme.AnotherBusinessService.doWork",
graph,
new HashSet<>()
);
// It should ONLY find the direct business path, skipping the java.util path.
assertThat(paths).hasSize(1);
assertThat(paths.get(0)).containsExactly(
"com.acme.BusinessService.process",
"com.acme.DirectService.execute",
"com.acme.AnotherBusinessService.doWork"
);
}
@Test
void shouldFollowValidBusinessPaths() {
Map<String, List<CallEdge>> graph = new HashMap<>();
graph.put("com.acme.Start.run", List.of(
new CallEdge("com.acme.Middle.process", List.of())
));
graph.put("com.acme.Middle.process", List.of(
new CallEdge("com.acme.End.finish", List.of())
));
List<List<String>> paths = pathFinder.findAllPaths(
"com.acme.Start.run",
"com.acme.End.finish",
graph,
new HashSet<>()
);
assertThat(paths).hasSize(1);
assertThat(paths.get(0)).containsExactly(
"com.acme.Start.run",
"com.acme.Middle.process",
"com.acme.End.finish"
);
}
}

View File

@@ -31,7 +31,7 @@ class DynamicClasspathResolverTest {
.filter(arg -> arg.startsWith("-Dmdep.outputFile=")) .filter(arg -> arg.startsWith("-Dmdep.outputFile="))
.findFirst() .findFirst()
.orElseThrow(); .orElseThrow();
Path outputFile = Path.of(outputFileArg.substring("-Dmdep.outputFile=".length())); Path outputFile = pb.directory().toPath().resolve(outputFileArg.substring("-Dmdep.outputFile=".length()));
Files.writeString(outputFile, cp); Files.writeString(outputFile, cp);
} }

View File

@@ -60,6 +60,7 @@ class EnterpriseBugsTest {
Files.writeString(tempDir.resolve("OrderConfig.java"), source); Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext(); CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.scan(tempDir); context.scan(tempDir);
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context); HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
@@ -168,4 +169,108 @@ class EnterpriseBugsTest {
.build(); .build();
assertThat(engine.isRoutedToCorrectMachine(chain2, "com.example.OrderStateMachineConfig", context)).isFalse(); assertThat(engine.isRoutedToCorrectMachine(chain2, "com.example.OrderStateMachineConfig", context)).isFalse();
} }
@Test
void shouldFindPathsUsingEagerEntryPointExpansion(@TempDir Path tempDir) throws IOException {
String apiSrc = """
package com.example;
public interface OrderApi {
void submit();
}
""";
String controllerSrc = """
package com.example;
public class OrderController implements OrderApi {
private OrderService service;
public void submit() {
service.trigger("someEvent");
}
}
""";
String serviceSrc = """
package com.example;
public class OrderService {
public void trigger(String EVENT) {}
}
""";
Files.writeString(tempDir.resolve("OrderApi.java"), apiSrc);
Files.writeString(tempDir.resolve("OrderController.java"), controllerSrc);
Files.writeString(tempDir.resolve("OrderService.java"), serviceSrc);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.OrderApi")
.methodName("submit")
.build();
TriggerPoint triggerPoint = TriggerPoint.builder()
.className("com.example.OrderService")
.methodName("trigger")
.event("EVENT")
.build();
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(triggerPoint));
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getMethodChain()).contains(
"com.example.OrderController.submit"
);
}
@Test
void shouldNotPolymorphicallyExpandExternalInterfacesWithManyImpls(@TempDir Path tempDir) throws IOException {
String impl1 = "package com.example; public class R1 implements java.lang.Runnable { public void run() {} }";
String impl2 = "package com.example; public class R2 implements java.lang.Runnable { public void run() {} }";
String impl3 = "package com.example; public class R3 implements java.lang.Runnable { public void run() {} }";
String impl4 = "package com.example; public class R4 implements java.lang.Runnable { public void run() {} }";
String executorSrc = """
package com.example;
public class ExecutorService {
public void execute(java.lang.Runnable task) {
task.run();
}
}
""";
Files.writeString(tempDir.resolve("R1.java"), impl1);
Files.writeString(tempDir.resolve("R2.java"), impl2);
Files.writeString(tempDir.resolve("R3.java"), impl3);
Files.writeString(tempDir.resolve("R4.java"), impl4);
Files.writeString(tempDir.resolve("ExecutorService.java"), executorSrc);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
var graph = engine.buildCallGraph();
// The execute method calls task.run(). Since java.lang.Runnable is external (td == null) and has > 3 impls,
// it should NOT resolve task.run() to R1.run(), R2.run(), etc.
var edges = graph.get("com.example.ExecutorService.execute");
if (edges != null) {
for (var edge : edges) {
assertThat(edge.getTargetMethod()).isNotEqualTo("com.example.R1.run");
}
assertThat(edges).extracting("targetMethod").containsExactly("java.lang.Runnable.run");
}
}
@Test
void shouldNotTreatValueOfExpressionAsWildcardMatchingAllTransitions() {
click.kamil.springstatemachineexporter.analysis.enricher.matching.StrictFqnMatchingEngine engine =
new click.kamil.springstatemachineexporter.analysis.enricher.matching.StrictFqnMatchingEngine();
click.kamil.springstatemachineexporter.model.Event smEvent =
click.kamil.springstatemachineexporter.model.Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("OrderEvents.valueOf(someVar)")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
}
} }

View File

@@ -1036,4 +1036,47 @@ class HeuristicCallGraphEngineCoreTest {
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1); org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("REACTIVE_EVENT"); org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("REACTIVE_EVENT");
} }
@Test
void shouldExtractEventFromSwitchExpressionAndParentheses(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class SwitchOrderService {
public void processOrder(int type) {
String e = (((switch(type) {
case 1 -> "PAY";
default -> { yield "CANCEL"; }
})));
sendEvent(e);
}
public void sendEvent(String event) {
}
}
""";
Files.writeString(tempDir.resolve("SwitchOrderService.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.SwitchOrderService")
.methodName("processOrder")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.SwitchOrderService")
.methodName("sendEvent")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
System.out.println("POLY EVENTS: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("PAY", "CANCEL");
}
} }

View File

@@ -102,6 +102,7 @@ class HeuristicCallGraphEngineExtendedTest {
java.nio.file.Files.writeString(tempDir.resolve("OrderConfig.java"), source); java.nio.file.Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext(); CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.scan(tempDir); context.scan(tempDir);
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context); HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);

View File

@@ -954,4 +954,42 @@ class HeuristicCallGraphEngineGetterTest {
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("SMEvent.PAY_RECEIVED"); .containsExactlyInAnyOrder("SMEvent.PAY_RECEIVED");
} }
@Test
void shouldResolveGetterOnInterProceduralClassInstanceCreation() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handle() {
service.process(new DomainEvent("PAYLOAD_ID"));
}
}
class DomainEvent {
private String id;
public DomainEvent(String id) { this.id = id; }
public String getEvent() { return "INLINE_EVENT"; }
}
class OrderService {
public void process(DomainEvent event) {
sendEvent(event.getEvent());
}
public void sendEvent(String ev) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_interproc_cic");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handle").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("sendEvent").event("ev").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("INLINE_EVENT");
}
} }

View File

@@ -313,6 +313,186 @@ class HeuristicCallGraphEnginePolymorphicTest {
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).contains("POST_ACCEPTANCE_REJECTED"); org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).contains("POST_ACCEPTANCE_REJECTED");
} }
@Test
void shouldResolvePolymorphicEventsThroughAssertSupportedMethod() throws IOException {
String source = """
package com.example;
public class Dispatcher {
private HandlerService service;
public void dispatchRequest(Payload domainEvent) {
doDispatch(domainEvent);
}
private void doDispatch(Payload domainEvent) {
service.handleEvent(checkAndMapEvent(domainEvent.getType()));
}
private CustomEvents checkAndMapEvent(CustomEvents e) {
return e;
}
}
class HandlerService {
public void handleEvent(CustomEvents event) {}
}
interface Payload {
CustomEvents getType();
}
class AlphaPayload implements Payload {
public CustomEvents getType() { return CustomEvents.ALPHA; }
}
class BetaPayload implements Payload {
public CustomEvents getType() { return CustomEvents.BETA; }
}
enum CustomEvents { ALPHA, BETA }
""";
Path tempDir = Files.createTempDirectory("callgraph_test_poly_assert");
Files.writeString(tempDir.resolve("DispatcherConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.Dispatcher")
.methodName("dispatchRequest")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.HandlerService")
.methodName("handleEvent")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
CallChain chain = chains.get(0);
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("CustomEvents.ALPHA", "CustomEvents.BETA");
}
@Test
void shouldResolvePolymorphicEventsThroughNestedConverters() throws IOException {
String source = """
package com.example;
public class Dispatcher {
private HandlerService service;
public void dispatchRequest(Payload domainEvent) {
doDispatch(domainEvent);
}
private void doDispatch(Payload domainEvent) {
// NESTED CONVERTERS
service.handleEvent(verify(sanitize(domainEvent.type())));
}
private CustomEvents sanitize(CustomEvents e) { return e; }
private CustomEvents verify(CustomEvents e) { return e; }
}
class HandlerService {
public void handleEvent(CustomEvents event) {}
}
interface Payload {
CustomEvents type();
}
class AlphaPayload implements Payload {
public CustomEvents type() { return CustomEvents.ALPHA; }
}
enum CustomEvents { ALPHA, BETA }
""";
Path tempDir = Files.createTempDirectory("callgraph_test_poly_nested");
Files.writeString(tempDir.resolve("DispatcherConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.Dispatcher")
.methodName("dispatchRequest")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.HandlerService")
.methodName("handleEvent")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
System.out.println("DEBUG context.getImplementations(Payload) = " + context.getImplementations("Payload"));
System.out.println("DEBUG context.getImplementations(com.example.Payload) = " + context.getImplementations("com.example.Payload"));
System.out.println("DEBUG getPolymorphicEvents() = " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactly("CustomEvents.ALPHA");
}
@Test
void shouldResolvePolymorphicEventsWithCustomGetterSuffixes() throws IOException {
String source = """
package com.example;
public class Dispatcher {
private HandlerService service;
public void dispatchRequest(Payload domainEvent) {
doDispatch(domainEvent);
}
private void doDispatch(Payload domainEvent) {
// Using .event() instead of .getEvent()
service.handleEvent(check(domainEvent.event()));
}
private CustomEvents check(CustomEvents e) { return e; }
}
class HandlerService {
public void handleEvent(CustomEvents event) {}
}
interface Payload {
CustomEvents event();
}
class BetaPayload implements Payload {
public CustomEvents event() { return CustomEvents.BETA; }
}
enum CustomEvents { ALPHA, BETA }
""";
Path tempDir = Files.createTempDirectory("callgraph_test_poly_suffix");
Files.writeString(tempDir.resolve("DispatcherConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.Dispatcher")
.methodName("dispatchRequest")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.HandlerService")
.methodName("handleEvent")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactly("CustomEvents.BETA");
}
@Test @Test
void shouldResolveMappedTransitionEnumThroughAbstractBaseClassAndMultipleArgs() throws IOException { void shouldResolveMappedTransitionEnumThroughAbstractBaseClassAndMultipleArgs() throws IOException {
String source = """ String source = """

View File

@@ -466,7 +466,75 @@ class HeuristicCallGraphEngineTypeTest {
assertThat(chains).hasSize(1); assertThat(chains).hasSize(1);
CallChain chain = chains.get(0); CallChain chain = chains.get(0);
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("mapToEnum(str)");
assertThat(chain.getTriggerPoint().getPolymorphicEvents()) assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("MyEvents.A", "MyEvents.B"); .containsExactly("<SYMBOLIC: com.example.MyEvents.*>");
}
@Test
void shouldResolveTernaryValueOf(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class Dispatcher {
public void dispatch(String type, String eventStr) {
StateMachine sm = new StateMachine();
if ("ORDER".equals(type)) {
OrderEvent ev = OrderEvent.valueOf(eventStr);
sm.sendEvent(ev);
} else {
UserEvent ev = UserEvent.valueOf(eventStr);
sm.sendEvent(ev);
}
}
}
enum OrderEvent { PAY }
enum UserEvent { VERIFY }
class StateMachine {
public void sendEvent(Object e) {}
}
""";
Files.writeString(tempDir.resolve("Dispatcher.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entry = EntryPoint.builder()
.className("com.example.Dispatcher")
.methodName("dispatch")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.StateMachine")
.methodName("sendEvent")
.event("e")
.build();
List<CallChain> chains = builder.findChains(List.of(entry), List.of(trigger));
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: UserEvent.*>");
}
@Test
void testEnterpriseDispatcherResolution() throws IOException {
CodebaseContext context = new CodebaseContext();
context.scan(Path.of("../state_machines/state_machine_enterprise"));
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entry = EntryPoint.builder()
.className("click.kamil.enterprise.web.StateMachineController")
.methodName("transition")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("click.kamil.enterprise.web.StateMachineDispatcher")
.methodName("dispatch")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entry), List.of(trigger));
assertThat(chains).isNotEmpty();
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>");
} }
} }

View File

@@ -282,4 +282,48 @@ class JdtCallGraphEngineIntegrationTest {
"click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk" "click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk"
); );
} }
@Test
void testEnterpriseDispatcherResolutionJdt() throws IOException {
Path projectRoot = Path.of("../state_machines/state_machine_enterprise").toAbsolutePath().normalize();
CodebaseContext ctx = new CodebaseContext();
ctx.setProjectRoot(projectRoot);
ctx.setSourcepath(List.of(projectRoot.resolve("src/main/java").toString()));
DynamicClasspathResolver resolver = new DynamicClasspathResolver();
List<String> cp = resolver.resolveClasspath(projectRoot);
if (!cp.isEmpty()) {
ctx.setClasspath(cp);
}
ctx.setResolveBindings(true);
ctx.scan(Set.of(projectRoot), Collections.emptySet());
SpringBeanRegistry reg = new SpringBeanRegistry();
SpringContextScanner scanner = new SpringContextScanner(reg);
for (var cu : ctx.getCompilationUnits()) {
cu.accept(scanner);
}
SpringDependencyResolver dependencyResolver = new SpringDependencyResolver(reg);
InjectionPointAnalyzer injectionAnalyzer = new InjectionPointAnalyzer(dependencyResolver);
JdtCallGraphEngine jdtEngine = new JdtCallGraphEngine(ctx, injectionAnalyzer);
EntryPoint entry = EntryPoint.builder()
.className("click.kamil.enterprise.web.StateMachineController")
.methodName("transition")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("click.kamil.enterprise.web.StateMachineDispatcher")
.methodName("dispatch")
.event("event")
.build();
List<CallChain> chains = jdtEngine.findChains(List.of(entry), List.of(trigger));
assertThat(chains).isNotEmpty();
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>");
}
} }

View File

@@ -173,4 +173,111 @@ class LocalVariableTest {
assertThat(chain.getTriggerPoint().getPolymorphicEvents()) assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("OrderEvents.CANCEL"); .containsExactlyInAnyOrder("OrderEvents.CANCEL");
} }
@Test
void shouldUnwrapExplicitPassthroughMethod(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
private EventValidator validator;
public void processOrderEvent(RichOrderEvent domainEvent) {
OrderEvents resolved = validator.passthrough(domainEvent.getType());
service.updateOrderState(resolved);
}
}
class EventValidator {
public OrderEvents passthrough(OrderEvents e) {
if (e == null) throw new IllegalArgumentException();
return e;
}
}
class OrderService {
public void updateOrderState(OrderEvents event) {}
}
class RichOrderEvent {
public OrderEvents getType() {
return OrderEvents.PAY;
}
}
enum OrderEvents { PAY, CANCEL }
""";
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.OrderController")
.methodName("processOrderEvent")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.OrderService")
.methodName("updateOrderState")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
CallChain chain = chains.get(0);
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("OrderEvents.PAY");
}
@Test
void shouldFallbackToPrefixMatchingForExternalMethods(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void processOrderEvent(RichOrderEvent domainEvent) {
OrderEvents resolved = UnknownExternalClass.validate(domainEvent.getType());
service.updateOrderState(resolved);
}
}
class OrderService {
public void updateOrderState(OrderEvents event) {}
}
class RichOrderEvent {
public OrderEvents getType() {
return OrderEvents.PAY;
}
}
enum OrderEvents { PAY, CANCEL }
""";
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.OrderController")
.methodName("processOrderEvent")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.OrderService")
.methodName("updateOrderState")
.event("event")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
CallChain chain = chains.get(0);
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.doesNotContain("OrderEvents.PAY");
}
} }

View File

@@ -92,4 +92,153 @@ class PolymorphicDispatchCallGraphTest {
"com.example.TransportOrderService.processOrderEvent" "com.example.TransportOrderService.processOrderEvent"
); );
} }
@Test
void shouldFilterPolymorphicDispatchContextually(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
public class LogisticsController {
private LogisticsService service;
public void handle(String event) {
service.process(event);
}
}
abstract class AbstractOrderService {
public void process(String event) {
String resolved = assertSupportedOrderEvent(event);
sendEvent(resolved);
}
protected abstract String assertSupportedOrderEvent(String event);
public void sendEvent(String ev) {}
}
class LogisticsService extends AbstractOrderService {
@Override
protected String assertSupportedOrderEvent(String event) {
return LogisticsEvent.valueOf(event).name();
}
}
class ComputerStoreService extends AbstractOrderService {
@Override
protected String assertSupportedOrderEvent(String event) {
return ComputerEvent.valueOf(event).name();
}
}
enum LogisticsEvent { DELIVERED, RETURNED }
enum ComputerEvent { SHIPPED, REFUNDED }
""";
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.LogisticsController")
.methodName("handle")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.AbstractOrderService")
.methodName("sendEvent")
.event("ev")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("LogisticsEvent.DELIVERED", "LogisticsEvent.RETURNED");
}
@Test
void shouldResolveMapBasedDynamicDispatch(@TempDir Path tempDir) throws IOException {
String source = """
package com.example;
import java.util.Map;
import java.util.HashMap;
public class DispatcherController {
private Map<String, AbstractService> serviceMap = new HashMap<>();
public DispatcherController(LogisticsService logistics, ComputerStoreService computerStore) {
serviceMap.put("LOGISTICS", logistics);
serviceMap.put("COMPUTER", computerStore);
}
public void handle(String machineType, String event) {
serviceMap.get(machineType).process(event);
}
}
abstract class AbstractService {
public void process(String event) {
String resolved = assertSupportedEvent(event);
sendEvent(resolved);
}
protected abstract String assertSupportedEvent(String event);
public void sendEvent(String ev) {}
}
class LogisticsService extends AbstractService {
@Override
protected String assertSupportedEvent(String event) {
return LogisticsEvent.valueOf(event).name();
}
}
class ComputerStoreService extends AbstractService {
@Override
protected String assertSupportedEvent(String event) {
return ComputerEvent.valueOf(event).name();
}
}
enum LogisticsEvent { DELIVERED, RETURNED }
enum ComputerEvent { SHIPPED, REFUNDED }
""";
Files.writeString(tempDir.resolve("DispatchConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.scan(tempDir);
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.DispatcherController")
.methodName("handle")
.build();
TriggerPoint trigger = TriggerPoint.builder()
.className("com.example.AbstractService")
.methodName("sendEvent")
.event("ev")
.build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
assertThat(chains).hasSize(2);
CallChain chainLogistics = chains.stream()
.filter(c -> "machineType == \"LOGISTICS\"".equals(c.getTriggerPoint().getConstraint()))
.findFirst().orElseThrow();
assertThat(chainLogistics.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("LogisticsEvent.DELIVERED", "LogisticsEvent.RETURNED");
CallChain chainComputer = chains.stream()
.filter(c -> "machineType == \"COMPUTER\"".equals(c.getTriggerPoint().getConstraint()))
.findFirst().orElseThrow();
assertThat(chainComputer.getTriggerPoint().getPolymorphicEvents())
.containsExactlyInAnyOrder("ComputerEvent.SHIPPED", "ComputerEvent.REFUNDED");
}
} }

View File

@@ -0,0 +1,69 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
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 java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
class VariableTracerTest {
@Test
void shouldShortCircuitLiteralStringResolution() {
CodebaseContext context = new CodebaseContext();
VariableTracer tracer = new VariableTracer(context, new ConstantResolver());
// Even with an empty context and call graph, passing a raw String literal should return instantly
// without trying to recursively traverse the path or crash.
String resolved = tracer.resolveArgumentValue("\"PAY_EVENT\"", "com.example.Order.process", List.of("com.example.Order.process"), 0, Map.of());
assertThat(resolved).isEqualTo("\"PAY_EVENT\"");
}
@Test
void shouldBridgePolymorphicParametersPositionally(@TempDir Path tempDir) throws IOException {
Path comFoo = tempDir.resolve("com/foo");
Files.createDirectories(comFoo);
// Setup an interface and implementation with DIFFERENT parameter names
Files.writeString(comFoo.resolve("PaymentService.java"),
"package com.foo;\n" +
"public interface PaymentService {\n" +
" void submit(String eventName);\n" +
"}"
);
Files.writeString(comFoo.resolve("PaymentServiceImpl.java"),
"package com.foo;\n" +
"public class PaymentServiceImpl implements PaymentService {\n" +
" @Override\n" +
" public void submit(String e) {}\n" +
"}"
);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
VariableTracer tracer = new VariableTracer(context, new ConstantResolver());
// Simulate tracing from the Interface backwards to the Implementation.
// Because this is a polymorphic bridge, there is NO direct call edge in the AST call graph.
Map<String, String> params = tracer.buildParameterValuesMap(
"com.foo.PaymentService.submit",
"com.foo.PaymentServiceImpl.submit",
Map.of(), // Empty call graph (no direct edges)
List.of("com.foo.PaymentService.submit", "com.foo.PaymentServiceImpl.submit"),
0
);
// The tracer should have scanned both ASTs and mapped parameter 0 -> parameter 0
assertThat(params).hasSize(1);
assertThat(params).containsEntry("e", "eventName");
}
}

View File

@@ -104,7 +104,7 @@ class AstTransitionParserTest {
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
Transition transition = transitions.getFirst(); Transition transition = transitions.getFirst();
assertThat(transition.getGuard()) assertThat(transition.getGuards().getFirst())
.extracting(Guard::expression) .extracting(Guard::expression)
.isEqualTo("amount > 0"); .isEqualTo("amount > 0");
} }
@@ -129,7 +129,7 @@ class AstTransitionParserTest {
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
Transition transition = transitions.getFirst(); Transition transition = transitions.getFirst();
assertThat(transition.getGuard().isLambda()).isTrue(); assertThat(transition.getGuards().getFirst().isLambda()).isTrue();
assertThat(transition.getActions()) assertThat(transition.getActions())
.extracting(Action::isLambda) .extracting(Action::isLambda)
.containsExactly(true); .containsExactly(true);
@@ -230,7 +230,7 @@ class AstTransitionParserTest {
List<Transition> transitions = AstTransitionParser.parseTransitions(method, context); List<Transition> transitions = AstTransitionParser.parseTransitions(method, context);
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
assertThat(transitions.getFirst().getGuard().internalLogic()) assertThat(transitions.getFirst().getGuards().getFirst().internalLogic())
.contains("context.getMessage() != null"); .contains("context.getMessage() != null");
} }
@@ -287,7 +287,7 @@ class AstTransitionParserTest {
List<Transition> transitions = AstTransitionParser.parseTransitions(method, context); List<Transition> transitions = AstTransitionParser.parseTransitions(method, context);
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
assertThat(transitions.getFirst().getGuard().internalLogic()) assertThat(transitions.getFirst().getGuards().getFirst().internalLogic())
.contains("return expected.equals(context.getMessage());") .contains("return expected.equals(context.getMessage());")
.doesNotContain("protected Guard<String, String> guardVarEquals"); .doesNotContain("protected Guard<String, String> guardVarEquals");
} }
@@ -364,7 +364,7 @@ class AstTransitionParserTest {
List<Transition> transitions = AstTransitionParser.parseTransitions(method, context); List<Transition> transitions = AstTransitionParser.parseTransitions(method, context);
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
assertThat(transitions.getFirst().getGuard().internalLogic()) assertThat(transitions.getFirst().getGuards().getFirst().internalLogic())
.contains("return \"OK\".equals(context.getMessage().getPayload());") .contains("return \"OK\".equals(context.getMessage().getPayload());")
.contains("public boolean evaluate"); .contains("public boolean evaluate");
} }
@@ -390,7 +390,7 @@ class AstTransitionParserTest {
List<Transition> transitions = AstTransitionParser.parseTransitions(method, context); List<Transition> transitions = AstTransitionParser.parseTransitions(method, context);
assertThat(transitions).hasSize(1); assertThat(transitions).hasSize(1);
assertThat(transitions.getFirst().getGuard().internalLogic()) assertThat(transitions.getFirst().getGuards().getFirst().internalLogic())
.contains("context -> \"LOCAL_OK\".equals(context.getMessage())"); .contains("context -> \"LOCAL_OK\".equals(context.getMessage())");
} }
} }

View File

@@ -31,7 +31,7 @@ class PlantUmlTest {
t.setTargetStates(List.of(state2)); t.setTargetStates(List.of(state2));
// Add a guard and action containing problematic characters // Add a guard and action containing problematic characters
t.setGuard(Guard.of("list.size() < 5", false, null, null, null, null)); t.getGuards().add(Guard.of("list.size() < 5", false, null, null, null, null));
t.setActions(List.of(Action.of("doSomething(new int[]{1, 2})", false, null, null, null, null))); t.setActions(List.of(Action.of("doSomething(new int[]{1, 2})", false, null, null, null, null)));
String result = plantUml.export( String result = plantUml.export(

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT10", "rawName" : "Events.EVENT10",
"fullIdentifier" : "Events.EVENT10" "fullIdentifier" : "Events.EVENT10"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT11", "rawName" : "Events.EVENT11",
"fullIdentifier" : "Events.EVENT11" "fullIdentifier" : "Events.EVENT11"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT12", "rawName" : "Events.EVENT12",
"fullIdentifier" : "Events.EVENT12" "fullIdentifier" : "Events.EVENT12"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT13", "rawName" : "Events.EVENT13",
"fullIdentifier" : "Events.EVENT13" "fullIdentifier" : "Events.EVENT13"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT14", "rawName" : "Events.EVENT14",
"fullIdentifier" : "Events.EVENT14" "fullIdentifier" : "Events.EVENT14"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENT15", "rawName" : "Events.EVENT15",
"fullIdentifier" : "Events.EVENT15" "fullIdentifier" : "Events.EVENT15"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -278,14 +278,14 @@
"fullIdentifier" : "States.STATE17" "fullIdentifier" : "States.STATE17"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 86, "lineNumber" : 86,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -299,14 +299,14 @@
"fullIdentifier" : "States.STATE18" "fullIdentifier" : "States.STATE18"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value2\")", "expression" : "guardVarEquals(\"value2\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 87, "lineNumber" : 87,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -320,7 +320,7 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -334,14 +334,14 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 92, "lineNumber" : 92,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -355,7 +355,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -369,14 +369,14 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value3\")", "expression" : "guardVarEquals(\"value3\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 97, "lineNumber" : 97,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -390,7 +390,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -404,14 +404,14 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"reset\")", "expression" : "guardVarEquals(\"reset\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 102, "lineNumber" : 102,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -425,7 +425,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -439,14 +439,14 @@
"fullIdentifier" : "States.STATE5" "fullIdentifier" : "States.STATE5"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header2\",\"bar\")", "expression" : "guardEventHeaderEquals(\"header2\",\"bar\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 107, "lineNumber" : 107,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -460,7 +460,7 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -474,14 +474,14 @@
"fullIdentifier" : "States.STATE13" "fullIdentifier" : "States.STATE13"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo13\")", "expression" : "guardVarEquals(\"goTo13\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 112, "lineNumber" : 112,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -495,14 +495,14 @@
"fullIdentifier" : "States.STATE14" "fullIdentifier" : "States.STATE14"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo14\")", "expression" : "guardVarEquals(\"goTo14\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 113, "lineNumber" : 113,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -516,7 +516,7 @@
"fullIdentifier" : "States.STATE15" "fullIdentifier" : "States.STATE15"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -530,14 +530,14 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goBack10\")", "expression" : "guardVarEquals(\"goBack10\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 118, "lineNumber" : 118,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -551,7 +551,7 @@
"fullIdentifier" : "States.STATE11" "fullIdentifier" : "States.STATE11"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -565,14 +565,14 @@
"fullIdentifier" : "States.STATE12" "fullIdentifier" : "States.STATE12"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"loop12\")", "expression" : "guardVarEquals(\"loop12\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 123, "lineNumber" : 123,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -586,7 +586,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -600,14 +600,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 128, "lineNumber" : 128,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -621,14 +621,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 129, "lineNumber" : 129,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -642,7 +642,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -656,14 +656,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 134, "lineNumber" : 134,
"className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig", "className" : "click.kamil.examples.statemachine.complex.ComplexStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/complex/ComplexStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -677,7 +677,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
} ], } ],

View File

@@ -0,0 +1,13 @@
digraph statemachine {
rankdir=LR;
node [shape=rounded, style=filled, fillcolor=white, fontname="Arial"];
edge [fontname="Arial", fontsize=10];
_start [shape=circle, label="", fillcolor=black, width=0.1];
_start -> DRAFT;
APPROVED [fillcolor=lightgray];
DRAFT -> IN_REVIEW [label="DocumentEvent.SUBMIT", style="solid", color="black"];
IN_REVIEW -> APPROVED [label="DocumentEvent.APPROVE", style="solid", color="black"];
IN_REVIEW -> DRAFT [label="DocumentEvent.REJECT", style="solid", color="black"];
}

View File

@@ -0,0 +1,700 @@
{
"metadata" : {
"triggers" : [ {
"event" : "OrderEvent.PAY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 30,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "OrderEvent.SHIP",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 37,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.SUBMIT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 44,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.APPROVE",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 51,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.REJECT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 58,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "UserEvent.VERIFY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 65,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "UserEvent.SUSPEND",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 72,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "ORDER",
"lineNumber" : 81,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "DOCUMENT",
"lineNumber" : 87,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "USER",
"lineNumber" : 93,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
} ],
"entryPoints" : [ {
"type" : "REST",
"name" : "POST /api/machine/order/pay",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/pay",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/order/ship",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/ship",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/submit",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/approve",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/approve",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/reject",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/reject",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/user/verify",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/verify",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/user/suspend",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/suspend",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
} ],
"callChains" : [ {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/order/pay",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/pay",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.payOrder", "click.kamil.enterprise.web.StateMachineDispatcher.payOrder" ],
"triggerPoint" : {
"event" : "OrderEvent.PAY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 30,
"polymorphicEvents" : [ "OrderEvent.PAY" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/order/ship",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/ship",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.shipOrder", "click.kamil.enterprise.web.StateMachineDispatcher.shipOrder" ],
"triggerPoint" : {
"event" : "OrderEvent.SHIP",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 37,
"polymorphicEvents" : [ "OrderEvent.SHIP" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/submit",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.submitDocument", "click.kamil.enterprise.web.StateMachineDispatcher.submitDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.SUBMIT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 44,
"polymorphicEvents" : [ "DocumentEvent.SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "DocumentState.DRAFT",
"targetState" : "DocumentState.IN_REVIEW",
"event" : "DocumentEvent.SUBMIT"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/approve",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/approve",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.approveDocument", "click.kamil.enterprise.web.StateMachineDispatcher.approveDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.APPROVE",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 51,
"polymorphicEvents" : [ "DocumentEvent.APPROVE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "DocumentState.IN_REVIEW",
"targetState" : "DocumentState.APPROVED",
"event" : "DocumentEvent.APPROVE"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/reject",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/reject",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.rejectDocument", "click.kamil.enterprise.web.StateMachineDispatcher.rejectDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.REJECT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 58,
"polymorphicEvents" : [ "DocumentEvent.REJECT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "DocumentState.IN_REVIEW",
"targetState" : "DocumentState.DRAFT",
"event" : "DocumentEvent.REJECT"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/user/verify",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/verify",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.verifyUser", "click.kamil.enterprise.web.StateMachineDispatcher.verifyUser" ],
"triggerPoint" : {
"event" : "UserEvent.VERIFY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 65,
"polymorphicEvents" : [ "UserEvent.VERIFY" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/user/suspend",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/suspend",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.suspendUser", "click.kamil.enterprise.web.StateMachineDispatcher.suspendUser" ],
"triggerPoint" : {
"event" : "UserEvent.SUSPEND",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 72,
"polymorphicEvents" : [ "UserEvent.SUSPEND" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "ORDER",
"lineNumber" : 81,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "DOCUMENT",
"lineNumber" : 87,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "USER",
"lineNumber" : 93,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }
}
},
"name" : "click.kamil.enterprise.machines.document.DocumentStateMachineConfiguration",
"renderChoicesAsDiamonds" : true,
"startStates" : [ "DocumentState.DRAFT" ],
"transitions" : [ {
"type" : "EXTERNAL",
"sourceStates" : [ {
"rawName" : "DocumentState.DRAFT",
"fullIdentifier" : "DocumentState.DRAFT"
} ],
"targetStates" : [ {
"rawName" : "DocumentState.IN_REVIEW",
"fullIdentifier" : "DocumentState.IN_REVIEW"
} ],
"event" : {
"rawName" : "DocumentEvent.SUBMIT",
"fullIdentifier" : "DocumentEvent.SUBMIT"
},
"guards" : [ ],
"actions" : [ ],
"order" : null
}, {
"type" : "EXTERNAL",
"sourceStates" : [ {
"rawName" : "DocumentState.IN_REVIEW",
"fullIdentifier" : "DocumentState.IN_REVIEW"
} ],
"targetStates" : [ {
"rawName" : "DocumentState.APPROVED",
"fullIdentifier" : "DocumentState.APPROVED"
} ],
"event" : {
"rawName" : "DocumentEvent.APPROVE",
"fullIdentifier" : "DocumentEvent.APPROVE"
},
"guards" : [ ],
"actions" : [ ],
"order" : null
}, {
"type" : "EXTERNAL",
"sourceStates" : [ {
"rawName" : "DocumentState.IN_REVIEW",
"fullIdentifier" : "DocumentState.IN_REVIEW"
} ],
"targetStates" : [ {
"rawName" : "DocumentState.DRAFT",
"fullIdentifier" : "DocumentState.DRAFT"
} ],
"event" : {
"rawName" : "DocumentEvent.REJECT",
"fullIdentifier" : "DocumentEvent.REJECT"
},
"guards" : [ ],
"actions" : [ ],
"order" : null
} ],
"endStates" : [ "DocumentState.APPROVED" ]
}

View File

@@ -0,0 +1,33 @@
@startuml
!pragma layout smetana
set separator none
hide empty description
hide stereotype
skinparam state {
BackgroundColor white
BorderColor #94a3b8
BorderThickness 1
FontName Inter
FontSize 9
FontStyle bold
RoundCorner 20
Padding 1
}
skinparam shadowing false
skinparam ArrowFontName JetBrains Mono
skinparam ArrowFontSize 8
skinparam ArrowColor #cbd5e1
skinparam ArrowThickness 1
skinparam dpi 110
skinparam svgLinkTarget _self
[*] --> DocumentState.DRAFT
DocumentState.DRAFT -[#1E90FF,bold]-> DocumentState.IN_REVIEW <<external>> : DocumentEvent.SUBMIT
DocumentState.IN_REVIEW -[#1E90FF,bold]-> DocumentState.APPROVED <<external>> : DocumentEvent.APPROVE
DocumentState.IN_REVIEW -[#1E90FF,bold]-> DocumentState.DRAFT <<external>> : DocumentEvent.REJECT
DocumentState.APPROVED --> [*]
@enduml

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="DRAFT">
<state id="DRAFT">
<transition target="IN_REVIEW" event="DocumentEvent.SUBMIT"/>
</state>
<state id="IN_REVIEW">
<transition target="APPROVED" event="DocumentEvent.APPROVE"/>
<transition target="DRAFT" event="DocumentEvent.REJECT"/>
</state>
<state id="APPROVED">
</state>
</scxml>

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PLACE_ORDER", "event" : "PLACE_ORDER",
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl", "className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
@@ -19,7 +22,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "CANCEL_ORDER", "event" : "CANCEL_ORDER",
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl", "className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
@@ -29,7 +35,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 21, "lineNumber" : 21,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PAY_ORDER", "event" : "PAY_ORDER",
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService", "className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
@@ -39,7 +48,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "SHIP_ORDER", "event" : "SHIP_ORDER",
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener", "className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
@@ -49,7 +61,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "RETURN_ORDER", "event" : "RETURN_ORDER",
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener", "className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
@@ -59,7 +74,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -170,6 +188,78 @@
} ] } ]
} ], } ],
"callChains" : [ { "callChains" : [ {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/enterprise/orders/place",
"className" : "click.kamil.examples.enterprise.api.OrderApi",
"methodName" : "placeOrder",
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/api/OrderApi.java",
"metadata" : {
"path" : "/api/enterprise/orders/place",
"verb" : "POST"
},
"parameters" : [ ]
},
"methodChain" : [ "click.kamil.examples.enterprise.api.OrderApi.placeOrder", "click.kamil.examples.enterprise.api.OrderController.placeOrder", "click.kamil.examples.enterprise.service.OrderServiceImpl.place" ],
"triggerPoint" : {
"event" : "PLACE_ORDER",
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
"methodName" : "place",
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/service/OrderServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 16,
"polymorphicEvents" : [ "PLACE_ORDER" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "NEW",
"targetState" : "CHECK_AVAILABILITY",
"event" : "PLACE_ORDER"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/enterprise/orders/{id}/cancel",
"className" : "click.kamil.examples.enterprise.api.OrderApi",
"methodName" : "cancelOrder",
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/api/OrderApi.java",
"metadata" : {
"path" : "/api/enterprise/orders/{id}/cancel",
"verb" : "POST"
},
"parameters" : [ {
"name" : "id",
"type" : "String",
"annotations" : [ "PathVariable" ]
} ]
},
"methodChain" : [ "click.kamil.examples.enterprise.api.OrderApi.cancelOrder", "click.kamil.examples.enterprise.api.OrderController.cancelOrder", "click.kamil.examples.enterprise.service.OrderServiceImpl.cancel" ],
"triggerPoint" : {
"event" : "CANCEL_ORDER",
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
"methodName" : "cancel",
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/service/OrderServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 21,
"polymorphicEvents" : [ "CANCEL_ORDER" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "PAID",
"targetState" : "CANCELLED",
"event" : "CANCEL_ORDER"
} ]
}, {
"entryPoint" : { "entryPoint" : {
"type" : "REST", "type" : "REST",
"name" : "POST /api/enterprise/orders/place", "name" : "POST /api/enterprise/orders/place",
@@ -192,7 +282,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : [ "PLACE_ORDER" ] "polymorphicEvents" : [ "PLACE_ORDER" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -227,7 +320,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 21, "lineNumber" : 21,
"polymorphicEvents" : [ "CANCEL_ORDER" ] "polymorphicEvents" : [ "CANCEL_ORDER" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -257,7 +353,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -288,7 +387,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : [ "PAY_ORDER" ] "polymorphicEvents" : [ "PAY_ORDER" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -323,7 +425,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -358,7 +463,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -388,7 +496,7 @@
"rawName" : "OrderEvents.PLACE", "rawName" : "OrderEvents.PLACE",
"fullIdentifier" : "PLACE_ORDER" "fullIdentifier" : "PLACE_ORDER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -402,14 +510,14 @@
"fullIdentifier" : "PENDING_PAYMENT" "fullIdentifier" : "PENDING_PAYMENT"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "(c) -> true", "expression" : "(c) -> true",
"isLambda" : true, "isLambda" : true,
"internalLogic" : "(c) -> true", "internalLogic" : "(c) -> true",
"lineNumber" : 41, "lineNumber" : 41,
"className" : "click.kamil.examples.enterprise.config.EnterpriseStateMachineConfig", "className" : "click.kamil.examples.enterprise.config.EnterpriseStateMachineConfig",
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/config/EnterpriseStateMachineConfig.java" "sourceFile" : "src/main/java/click/kamil/examples/enterprise/config/EnterpriseStateMachineConfig.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -423,7 +531,7 @@
"fullIdentifier" : "CANCELLED" "fullIdentifier" : "CANCELLED"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -440,7 +548,7 @@
"rawName" : "OrderEvents.PAY", "rawName" : "OrderEvents.PAY",
"fullIdentifier" : "PAY_ORDER" "fullIdentifier" : "PAY_ORDER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -457,7 +565,7 @@
"rawName" : "OrderEvents.SHIP", "rawName" : "OrderEvents.SHIP",
"fullIdentifier" : "SHIP_ORDER" "fullIdentifier" : "SHIP_ORDER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -474,7 +582,7 @@
"rawName" : "\"FINALIZE\"", "rawName" : "\"FINALIZE\"",
"fullIdentifier" : "FINALIZE" "fullIdentifier" : "FINALIZE"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -491,7 +599,7 @@
"rawName" : "OrderEvents.CANCEL", "rawName" : "OrderEvents.CANCEL",
"fullIdentifier" : "CANCEL_ORDER" "fullIdentifier" : "CANCEL_ORDER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -508,7 +616,7 @@
"rawName" : "OrderEvents.RETURN", "rawName" : "OrderEvents.RETURN",
"fullIdentifier" : "RETURN_ORDER" "fullIdentifier" : "RETURN_ORDER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 34, "lineNumber" : 34,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "AUDIT_EVENT", "event" : "AUDIT_EVENT",
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor", "className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
@@ -19,7 +22,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "FALLBACK_EVENT", "event" : "FALLBACK_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService",
@@ -29,7 +35,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PROFILED_EVENT", "event" : "PROFILED_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService",
@@ -39,7 +48,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PRIMARY_EVENT", "event" : "PRIMARY_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
@@ -49,7 +61,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "SUBMIT_EVENT", "event" : "SUBMIT_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -59,7 +74,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 20, "lineNumber" : 20,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "CANCEL_EVENT", "event" : "CANCEL_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -69,7 +87,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 25, "lineNumber" : 25,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "[LIFECYCLE:RESTORE]", "event" : "[LIFECYCLE:RESTORE]",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -79,7 +100,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 29, "lineNumber" : 29,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "QUALIFIER_EVENT", "event" : "QUALIFIER_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
@@ -89,7 +113,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "NAMED_EVENT", "event" : "NAMED_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
@@ -99,7 +126,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "AUTHORIZE", "event" : "AUTHORIZE",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -109,7 +139,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 22, "lineNumber" : 22,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "CAPTURE", "event" : "CAPTURE",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -119,7 +152,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 35, "lineNumber" : 35,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "[LIFECYCLE:RESTORE]", "event" : "[LIFECYCLE:RESTORE]",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -129,7 +165,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 28, "lineNumber" : 28,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "REACTIVE_EVENT", "event" : "REACTIVE_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService", "className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
@@ -139,7 +178,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -367,7 +409,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 20, "lineNumber" : 20,
"polymorphicEvents" : [ "SUBMIT_EVENT" ] "polymorphicEvents" : [ "SUBMIT_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -398,7 +443,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 25, "lineNumber" : 25,
"polymorphicEvents" : [ "CANCEL_EVENT" ] "polymorphicEvents" : [ "CANCEL_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -429,7 +477,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 34, "lineNumber" : 34,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -464,7 +515,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 29, "lineNumber" : 29,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "orderId", "contextMachineId" : "orderId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -495,7 +549,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : [ "REACTIVE_EVENT" ] "polymorphicEvents" : [ "REACTIVE_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -525,7 +582,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -556,7 +616,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -583,7 +646,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : [ "PRIMARY_EVENT" ] "polymorphicEvents" : [ "PRIMARY_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -610,7 +676,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -637,7 +706,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "QUALIFIER_EVENT" ] "polymorphicEvents" : [ "QUALIFIER_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -664,7 +736,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : [ "PRIMARY_EVENT" ] "polymorphicEvents" : [ "PRIMARY_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -691,7 +766,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "QUALIFIER_EVENT" ] "polymorphicEvents" : [ "QUALIFIER_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -718,7 +796,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -749,7 +830,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 22, "lineNumber" : 22,
"polymorphicEvents" : null "polymorphicEvents" : [ "AUTHORIZE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -780,7 +864,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 35, "lineNumber" : 35,
"polymorphicEvents" : null "polymorphicEvents" : [ "CAPTURE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "paymentId", "contextMachineId" : "paymentId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -811,7 +898,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 28, "lineNumber" : 28,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "paymentId", "contextMachineId" : "paymentId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -842,7 +932,7 @@
"rawName" : "MyEvents.SUBMIT", "rawName" : "MyEvents.SUBMIT",
"fullIdentifier" : "SUBMIT_EVENT" "fullIdentifier" : "SUBMIT_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -859,7 +949,7 @@
"rawName" : "\"FINISH\"", "rawName" : "\"FINISH\"",
"fullIdentifier" : "FINISH" "fullIdentifier" : "FINISH"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -876,7 +966,7 @@
"rawName" : "MyEvents.CANCEL", "rawName" : "MyEvents.CANCEL",
"fullIdentifier" : "CANCEL_EVENT" "fullIdentifier" : "CANCEL_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -893,7 +983,7 @@
"rawName" : "\"REACTIVE_EVENT\"", "rawName" : "\"REACTIVE_EVENT\"",
"fullIdentifier" : "REACTIVE_EVENT" "fullIdentifier" : "REACTIVE_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -910,7 +1000,7 @@
"rawName" : "\"AUDIT_EVENT\"", "rawName" : "\"AUDIT_EVENT\"",
"fullIdentifier" : "AUDIT_EVENT" "fullIdentifier" : "AUDIT_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -927,7 +1017,7 @@
"rawName" : "\"EXTERNAL_TRIGGER\"", "rawName" : "\"EXTERNAL_TRIGGER\"",
"fullIdentifier" : "EXTERNAL_TRIGGER" "fullIdentifier" : "EXTERNAL_TRIGGER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 34, "lineNumber" : 34,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "AUDIT_EVENT", "event" : "AUDIT_EVENT",
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor", "className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
@@ -19,7 +22,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "FALLBACK_EVENT", "event" : "FALLBACK_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService",
@@ -29,7 +35,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PROFILED_EVENT", "event" : "PROFILED_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService",
@@ -39,7 +48,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "PRIMARY_EVENT", "event" : "PRIMARY_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
@@ -49,7 +61,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "SUBMIT_EVENT", "event" : "SUBMIT_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -59,7 +74,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 20, "lineNumber" : 20,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "CANCEL_EVENT", "event" : "CANCEL_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -69,7 +87,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 25, "lineNumber" : 25,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "[LIFECYCLE:RESTORE]", "event" : "[LIFECYCLE:RESTORE]",
"className" : "click.kamil.examples.statemachine.extended.service.OrderService", "className" : "click.kamil.examples.statemachine.extended.service.OrderService",
@@ -79,7 +100,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 29, "lineNumber" : 29,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "QUALIFIER_EVENT", "event" : "QUALIFIER_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
@@ -89,7 +113,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "NAMED_EVENT", "event" : "NAMED_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService", "className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
@@ -99,7 +126,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "AUTHORIZE", "event" : "AUTHORIZE",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -109,7 +139,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 22, "lineNumber" : 22,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "CAPTURE", "event" : "CAPTURE",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -119,7 +152,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 35, "lineNumber" : 35,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "[LIFECYCLE:RESTORE]", "event" : "[LIFECYCLE:RESTORE]",
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl", "className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
@@ -129,7 +165,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 28, "lineNumber" : 28,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "REACTIVE_EVENT", "event" : "REACTIVE_EVENT",
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService", "className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
@@ -139,7 +178,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -367,7 +409,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 20, "lineNumber" : 20,
"polymorphicEvents" : [ "SUBMIT_EVENT" ] "polymorphicEvents" : [ "SUBMIT_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -398,7 +443,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 25, "lineNumber" : 25,
"polymorphicEvents" : [ "CANCEL_EVENT" ] "polymorphicEvents" : [ "CANCEL_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -429,7 +477,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 34, "lineNumber" : 34,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -464,7 +515,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 29, "lineNumber" : 29,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "orderId", "contextMachineId" : "orderId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -495,7 +549,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : [ "REACTIVE_EVENT" ] "polymorphicEvents" : [ "REACTIVE_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -525,7 +582,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 16, "lineNumber" : 16,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -556,7 +616,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -583,7 +646,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : [ "PRIMARY_EVENT" ] "polymorphicEvents" : [ "PRIMARY_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -610,7 +676,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -637,7 +706,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "QUALIFIER_EVENT" ] "polymorphicEvents" : [ "QUALIFIER_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -664,7 +736,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 19, "lineNumber" : 19,
"polymorphicEvents" : [ "PRIMARY_EVENT" ] "polymorphicEvents" : [ "PRIMARY_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -691,7 +766,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "QUALIFIER_EVENT" ] "polymorphicEvents" : [ "QUALIFIER_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -718,7 +796,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 17, "lineNumber" : 17,
"polymorphicEvents" : [ "NAMED_EVENT" ] "polymorphicEvents" : [ "NAMED_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -749,7 +830,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 22, "lineNumber" : 22,
"polymorphicEvents" : null "polymorphicEvents" : [ "AUTHORIZE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : null "matchedTransitions" : null
@@ -780,7 +864,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 35, "lineNumber" : 35,
"polymorphicEvents" : null "polymorphicEvents" : [ "CAPTURE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "paymentId", "contextMachineId" : "paymentId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -811,7 +898,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 28, "lineNumber" : 28,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : "paymentId", "contextMachineId" : "paymentId",
"matchedTransitions" : null "matchedTransitions" : null
@@ -842,7 +932,7 @@
"rawName" : "MyEvents.SUBMIT", "rawName" : "MyEvents.SUBMIT",
"fullIdentifier" : "SUBMIT_EVENT" "fullIdentifier" : "SUBMIT_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -859,7 +949,7 @@
"rawName" : "\"FINISH\"", "rawName" : "\"FINISH\"",
"fullIdentifier" : "FINISH" "fullIdentifier" : "FINISH"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -876,7 +966,7 @@
"rawName" : "MyEvents.CANCEL", "rawName" : "MyEvents.CANCEL",
"fullIdentifier" : "CANCEL_EVENT" "fullIdentifier" : "CANCEL_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -893,7 +983,7 @@
"rawName" : "\"REACTIVE_EVENT\"", "rawName" : "\"REACTIVE_EVENT\"",
"fullIdentifier" : "REACTIVE_EVENT" "fullIdentifier" : "REACTIVE_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -910,7 +1000,7 @@
"rawName" : "\"AUDIT_EVENT\"", "rawName" : "\"AUDIT_EVENT\"",
"fullIdentifier" : "AUDIT_EVENT" "fullIdentifier" : "AUDIT_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -927,7 +1017,7 @@
"rawName" : "\"EXTERNAL_TRIGGER\"", "rawName" : "\"EXTERNAL_TRIGGER\"",
"fullIdentifier" : "EXTERNAL_TRIGGER" "fullIdentifier" : "EXTERNAL_TRIGGER"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT10", "rawName" : "Events.EVENT10",
"fullIdentifier" : "Events.EVENT10" "fullIdentifier" : "Events.EVENT10"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT11", "rawName" : "Events.EVENT11",
"fullIdentifier" : "Events.EVENT11" "fullIdentifier" : "Events.EVENT11"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT12", "rawName" : "Events.EVENT12",
"fullIdentifier" : "Events.EVENT12" "fullIdentifier" : "Events.EVENT12"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT13", "rawName" : "Events.EVENT13",
"fullIdentifier" : "Events.EVENT13" "fullIdentifier" : "Events.EVENT13"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT14", "rawName" : "Events.EVENT14",
"fullIdentifier" : "Events.EVENT14" "fullIdentifier" : "Events.EVENT14"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENT15", "rawName" : "Events.EVENT15",
"fullIdentifier" : "Events.EVENT15" "fullIdentifier" : "Events.EVENT15"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -281,7 +281,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -298,7 +298,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -315,7 +315,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -332,7 +332,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -349,7 +349,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -366,7 +366,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -380,14 +380,14 @@
"fullIdentifier" : "States.STATEX" "fullIdentifier" : "States.STATEX"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 120, "lineNumber" : 120,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -401,7 +401,7 @@
"fullIdentifier" : "States.STATEZ" "fullIdentifier" : "States.STATEZ"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -415,14 +415,14 @@
"fullIdentifier" : "States.STATE17" "fullIdentifier" : "States.STATE17"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 126, "lineNumber" : 126,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -436,14 +436,14 @@
"fullIdentifier" : "States.STATE18" "fullIdentifier" : "States.STATE18"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value2\")", "expression" : "guardVarEquals(\"value2\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 127, "lineNumber" : 127,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -457,7 +457,7 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -471,14 +471,14 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 132, "lineNumber" : 132,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -492,7 +492,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -506,14 +506,14 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value3\")", "expression" : "guardVarEquals(\"value3\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 137, "lineNumber" : 137,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -527,7 +527,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -541,14 +541,14 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"reset\")", "expression" : "guardVarEquals(\"reset\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 142, "lineNumber" : 142,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -562,7 +562,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -576,14 +576,14 @@
"fullIdentifier" : "States.STATE5" "fullIdentifier" : "States.STATE5"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header2\",\"bar\")", "expression" : "guardEventHeaderEquals(\"header2\",\"bar\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 147, "lineNumber" : 147,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -597,7 +597,7 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -611,14 +611,14 @@
"fullIdentifier" : "States.STATE13" "fullIdentifier" : "States.STATE13"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo13\")", "expression" : "guardVarEquals(\"goTo13\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 152, "lineNumber" : 152,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -632,14 +632,14 @@
"fullIdentifier" : "States.STATE14" "fullIdentifier" : "States.STATE14"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo14\")", "expression" : "guardVarEquals(\"goTo14\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 153, "lineNumber" : 153,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -653,7 +653,7 @@
"fullIdentifier" : "States.STATE15" "fullIdentifier" : "States.STATE15"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -667,14 +667,14 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goBack10\")", "expression" : "guardVarEquals(\"goBack10\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 158, "lineNumber" : 158,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -688,7 +688,7 @@
"fullIdentifier" : "States.STATE11" "fullIdentifier" : "States.STATE11"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -702,14 +702,14 @@
"fullIdentifier" : "States.STATE12" "fullIdentifier" : "States.STATE12"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"loop12\")", "expression" : "guardVarEquals(\"loop12\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 163, "lineNumber" : 163,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -723,7 +723,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -737,14 +737,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 168, "lineNumber" : 168,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -758,14 +758,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 169, "lineNumber" : 169,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -779,7 +779,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -793,14 +793,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 174, "lineNumber" : 174,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -814,7 +814,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -831,7 +831,7 @@
"rawName" : "Events.EVENTX", "rawName" : "Events.EVENTX",
"fullIdentifier" : "Events.EVENTX" "fullIdentifier" : "Events.EVENTX"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -845,14 +845,14 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 31, "lineNumber" : 31,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.F1StateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.F1StateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/F1StateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/F1StateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -866,7 +866,7 @@
"fullIdentifier" : "States.STATE_EXTRA_1" "fullIdentifier" : "States.STATE_EXTRA_1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -883,7 +883,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -900,7 +900,7 @@
"rawName" : "Events.EVENT_CANCEL_2", "rawName" : "Events.EVENT_CANCEL_2",
"fullIdentifier" : "Events.EVENT_CANCEL_2" "fullIdentifier" : "Events.EVENT_CANCEL_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -917,7 +917,7 @@
"rawName" : "Events.EVENT_CANCEL_2", "rawName" : "Events.EVENT_CANCEL_2",
"fullIdentifier" : "Events.EVENT_CANCEL_2" "fullIdentifier" : "Events.EVENT_CANCEL_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT10", "rawName" : "Events.EVENT10",
"fullIdentifier" : "Events.EVENT10" "fullIdentifier" : "Events.EVENT10"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT11", "rawName" : "Events.EVENT11",
"fullIdentifier" : "Events.EVENT11" "fullIdentifier" : "Events.EVENT11"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT12", "rawName" : "Events.EVENT12",
"fullIdentifier" : "Events.EVENT12" "fullIdentifier" : "Events.EVENT12"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT13", "rawName" : "Events.EVENT13",
"fullIdentifier" : "Events.EVENT13" "fullIdentifier" : "Events.EVENT13"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT14", "rawName" : "Events.EVENT14",
"fullIdentifier" : "Events.EVENT14" "fullIdentifier" : "Events.EVENT14"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENT15", "rawName" : "Events.EVENT15",
"fullIdentifier" : "Events.EVENT15" "fullIdentifier" : "Events.EVENT15"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -281,7 +281,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -298,7 +298,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -315,7 +315,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -332,7 +332,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -349,7 +349,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -366,7 +366,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -380,14 +380,14 @@
"fullIdentifier" : "States.STATEX" "fullIdentifier" : "States.STATEX"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 120, "lineNumber" : 120,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -401,7 +401,7 @@
"fullIdentifier" : "States.STATEZ" "fullIdentifier" : "States.STATEZ"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -415,14 +415,14 @@
"fullIdentifier" : "States.STATE17" "fullIdentifier" : "States.STATE17"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 126, "lineNumber" : 126,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -436,14 +436,14 @@
"fullIdentifier" : "States.STATE18" "fullIdentifier" : "States.STATE18"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value2\")", "expression" : "guardVarEquals(\"value2\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 127, "lineNumber" : 127,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -457,7 +457,7 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -471,14 +471,14 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 132, "lineNumber" : 132,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -492,7 +492,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -506,14 +506,14 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value3\")", "expression" : "guardVarEquals(\"value3\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 137, "lineNumber" : 137,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -527,7 +527,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -541,14 +541,14 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"reset\")", "expression" : "guardVarEquals(\"reset\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 142, "lineNumber" : 142,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -562,7 +562,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -576,14 +576,14 @@
"fullIdentifier" : "States.STATE5" "fullIdentifier" : "States.STATE5"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header2\",\"bar\")", "expression" : "guardEventHeaderEquals(\"header2\",\"bar\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 147, "lineNumber" : 147,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -597,7 +597,7 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -611,14 +611,14 @@
"fullIdentifier" : "States.STATE13" "fullIdentifier" : "States.STATE13"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo13\")", "expression" : "guardVarEquals(\"goTo13\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 152, "lineNumber" : 152,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -632,14 +632,14 @@
"fullIdentifier" : "States.STATE14" "fullIdentifier" : "States.STATE14"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo14\")", "expression" : "guardVarEquals(\"goTo14\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 153, "lineNumber" : 153,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -653,7 +653,7 @@
"fullIdentifier" : "States.STATE15" "fullIdentifier" : "States.STATE15"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -667,14 +667,14 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goBack10\")", "expression" : "guardVarEquals(\"goBack10\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 158, "lineNumber" : 158,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -688,7 +688,7 @@
"fullIdentifier" : "States.STATE11" "fullIdentifier" : "States.STATE11"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -702,14 +702,14 @@
"fullIdentifier" : "States.STATE12" "fullIdentifier" : "States.STATE12"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"loop12\")", "expression" : "guardVarEquals(\"loop12\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 163, "lineNumber" : 163,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -723,7 +723,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -737,14 +737,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 168, "lineNumber" : 168,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -758,14 +758,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 169, "lineNumber" : 169,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -779,7 +779,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -793,14 +793,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 174, "lineNumber" : 174,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractFStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractFStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -814,7 +814,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -831,7 +831,7 @@
"rawName" : "Events.EVENT_1_1", "rawName" : "Events.EVENT_1_1",
"fullIdentifier" : "Events.EVENT_1_1" "fullIdentifier" : "Events.EVENT_1_1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -845,14 +845,14 @@
"fullIdentifier" : "States.STATE_EXTRA_1_1" "fullIdentifier" : "States.STATE_EXTRA_1_1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 33, "lineNumber" : 33,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.F2StateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.F2StateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/F2StateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/F2StateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -866,7 +866,7 @@
"fullIdentifier" : "States.STATE_EXTRA_1_3" "fullIdentifier" : "States.STATE_EXTRA_1_3"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -883,7 +883,7 @@
"rawName" : "Events.EVENT_1_2", "rawName" : "Events.EVENT_1_2",
"fullIdentifier" : "Events.EVENT_1_2" "fullIdentifier" : "Events.EVENT_1_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -900,7 +900,7 @@
"rawName" : "Events.EVENT_CANCEL_2", "rawName" : "Events.EVENT_CANCEL_2",
"fullIdentifier" : "Events.EVENT_CANCEL_2" "fullIdentifier" : "Events.EVENT_CANCEL_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -917,7 +917,7 @@
"rawName" : "Events.EVENT_CANCEL_2", "rawName" : "Events.EVENT_CANCEL_2",
"fullIdentifier" : "Events.EVENT_CANCEL_2" "fullIdentifier" : "Events.EVENT_CANCEL_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -934,7 +934,7 @@
"rawName" : "Events.EVENT_CANCEL_2", "rawName" : "Events.EVENT_CANCEL_2",
"fullIdentifier" : "Events.EVENT_CANCEL_2" "fullIdentifier" : "Events.EVENT_CANCEL_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.TO_FORK", "rawName" : "Events.TO_FORK",
"fullIdentifier" : "Events.TO_FORK" "fullIdentifier" : "Events.TO_FORK"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"fullIdentifier" : "States.REGION2_STATE1" "fullIdentifier" : "States.REGION2_STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.R1_NEXT", "rawName" : "Events.R1_NEXT",
"fullIdentifier" : "Events.R1_NEXT" "fullIdentifier" : "Events.R1_NEXT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.R2_NEXT", "rawName" : "Events.R2_NEXT",
"fullIdentifier" : "Events.R2_NEXT" "fullIdentifier" : "Events.R2_NEXT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"fullIdentifier" : "States.JOIN" "fullIdentifier" : "States.JOIN"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.TO_END", "rawName" : "Events.TO_END",
"fullIdentifier" : "Events.TO_END" "fullIdentifier" : "Events.TO_END"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -278,14 +278,14 @@
"fullIdentifier" : "States.STATEX" "fullIdentifier" : "States.STATEX"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 98, "lineNumber" : 98,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -299,7 +299,7 @@
"fullIdentifier" : "States.STATEZ" "fullIdentifier" : "States.STATEZ"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -313,14 +313,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 104, "lineNumber" : 104,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -334,14 +334,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 105, "lineNumber" : 105,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -355,7 +355,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -369,14 +369,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 110, "lineNumber" : 110,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -390,7 +390,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -407,7 +407,7 @@
"rawName" : "Events.EVENT_1_2", "rawName" : "Events.EVENT_1_2",
"fullIdentifier" : "Events.EVENT_1_2" "fullIdentifier" : "Events.EVENT_1_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -424,7 +424,7 @@
"rawName" : "Events.EVENT_1_2", "rawName" : "Events.EVENT_1_2",
"fullIdentifier" : "Events.EVENT_1_2" "fullIdentifier" : "Events.EVENT_1_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -441,7 +441,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -278,14 +278,14 @@
"fullIdentifier" : "States.STATEX" "fullIdentifier" : "States.STATEX"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 98, "lineNumber" : 98,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -299,7 +299,7 @@
"fullIdentifier" : "States.STATEZ" "fullIdentifier" : "States.STATEZ"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -313,14 +313,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 104, "lineNumber" : 104,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -334,14 +334,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 105, "lineNumber" : 105,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -355,7 +355,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -369,14 +369,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 110, "lineNumber" : 110,
"className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.AbstractGStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritanceextrafunctionsstate3/AbstractGStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -390,7 +390,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -407,7 +407,7 @@
"rawName" : "Events.EVENT_1_2", "rawName" : "Events.EVENT_1_2",
"fullIdentifier" : "Events.EVENT_1_2" "fullIdentifier" : "Events.EVENT_1_2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -424,7 +424,7 @@
"rawName" : "Events.EVENT_1_3", "rawName" : "Events.EVENT_1_3",
"fullIdentifier" : "Events.EVENT_1_3" "fullIdentifier" : "Events.EVENT_1_3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -441,7 +441,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -458,7 +458,7 @@
"rawName" : "Events.EVENT_CANCEL", "rawName" : "Events.EVENT_CANCEL",
"fullIdentifier" : "Events.EVENT_CANCEL" "fullIdentifier" : "Events.EVENT_CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 15, "lineNumber" : 15,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -35,6 +38,40 @@
"parameters" : [ ] "parameters" : [ ]
} ], } ],
"callChains" : [ { "callChains" : [ {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/v2/orders/submit",
"className" : "click.kamil.examples.statemachine.inheritance.api.OrderApi",
"methodName" : "submitOrder",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritance/api/OrderApi.java",
"metadata" : {
"path" : "/api/v2/orders/submit",
"verb" : "POST"
},
"parameters" : [ ]
},
"methodChain" : [ "click.kamil.examples.statemachine.inheritance.api.OrderApi.submitOrder", "click.kamil.examples.statemachine.inheritance.api.OrderControllerImpl.submitOrder", "click.kamil.examples.statemachine.inheritance.service.ProcessingServiceImpl.doProcess" ],
"triggerPoint" : {
"event" : "INHERITED_SUBMIT",
"className" : "click.kamil.examples.statemachine.inheritance.service.ProcessingServiceImpl",
"methodName" : "doProcess",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritance/service/ProcessingServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 15,
"polymorphicEvents" : [ "INHERITED_SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "START",
"targetState" : "WORKING",
"event" : "INHERITED_SUBMIT"
} ]
}, {
"entryPoint" : { "entryPoint" : {
"type" : "REST", "type" : "REST",
"name" : "POST /api/v2/orders/submit", "name" : "POST /api/v2/orders/submit",
@@ -57,7 +94,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 15, "lineNumber" : 15,
"polymorphicEvents" : [ "INHERITED_SUBMIT" ] "polymorphicEvents" : [ "INHERITED_SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -87,7 +127,7 @@
"rawName" : "\"INHERITED_SUBMIT\"", "rawName" : "\"INHERITED_SUBMIT\"",
"fullIdentifier" : "INHERITED_SUBMIT" "fullIdentifier" : "INHERITED_SUBMIT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "OrderEvents.PAY", "rawName" : "OrderEvents.PAY",
"fullIdentifier" : "OrderEvents.PAY" "fullIdentifier" : "OrderEvents.PAY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "OrderEvents.FULFILL", "rawName" : "OrderEvents.FULFILL",
"fullIdentifier" : "OrderEvents.FULFILL" "fullIdentifier" : "OrderEvents.FULFILL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,14 +60,14 @@
"rawName" : "OrderEvents.CANCEL", "rawName" : "OrderEvents.CANCEL",
"fullIdentifier" : "OrderEvents.CANCEL" "fullIdentifier" : "OrderEvents.CANCEL"
}, },
"guard" : { "guards" : [ {
"expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n", "expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n",
"isLambda" : true, "isLambda" : true,
"internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n", "internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n",
"lineNumber" : 29, "lineNumber" : 29,
"className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -84,7 +84,7 @@
"rawName" : "OrderEvents.CANCEL", "rawName" : "OrderEvents.CANCEL",
"fullIdentifier" : "OrderEvents.CANCEL" "fullIdentifier" : "OrderEvents.CANCEL"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -98,7 +98,7 @@
"rawName" : "OrderEvents.ABCD", "rawName" : "OrderEvents.ABCD",
"fullIdentifier" : "OrderEvents.ABCD" "fullIdentifier" : "OrderEvents.ABCD"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -115,7 +115,7 @@
"rawName" : "OrderEvents.ABCD", "rawName" : "OrderEvents.ABCD",
"fullIdentifier" : "OrderEvents.ABCD" "fullIdentifier" : "OrderEvents.ABCD"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -129,14 +129,14 @@
"fullIdentifier" : "OrderStates.PAID2" "fullIdentifier" : "OrderStates.PAID2"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return true;\n }\n}\n", "expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return true;\n }\n}\n",
"isLambda" : true, "isLambda" : true,
"internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return true;\n }\n}\n", "internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return true;\n }\n}\n",
"lineNumber" : 46, "lineNumber" : 46,
"className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -150,14 +150,14 @@
"fullIdentifier" : "OrderStates.PAID3" "fullIdentifier" : "OrderStates.PAID3"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guard1", "expression" : "guard1",
"isLambda" : false, "isLambda" : false,
"internalLogic" : null, "internalLogic" : null,
"lineNumber" : 52, "lineNumber" : 52,
"className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -171,14 +171,14 @@
"fullIdentifier" : "OrderStates.HAPPEN" "fullIdentifier" : "OrderStates.HAPPEN"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n", "expression" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n",
"isLambda" : true, "isLambda" : true,
"internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n", "internalLogic" : "new Guard<OrderStates,OrderEvents>(){\n @Override public boolean evaluate( StateContext<OrderStates,OrderEvents> context){\n return false;\n }\n}\n",
"lineNumber" : 53, "lineNumber" : 53,
"className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.enumstate.KamilEnumStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/enumstate/KamilEnumStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -192,7 +192,7 @@
"fullIdentifier" : "OrderStates.CANCELED" "fullIdentifier" : "OrderStates.CANCELED"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 3 "order" : 3
}, { }, {
@@ -206,7 +206,7 @@
"fullIdentifier" : "OrderStates.CANCELED" "fullIdentifier" : "OrderStates.CANCELED"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -220,7 +220,7 @@
"fullIdentifier" : "OrderStates.CANCELED" "fullIdentifier" : "OrderStates.CANCELED"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -234,7 +234,7 @@
"rawName" : "OrderEvents.ABCD", "rawName" : "OrderEvents.ABCD",
"fullIdentifier" : "OrderEvents.ABCD" "fullIdentifier" : "OrderEvents.ABCD"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ { "actions" : [ {
"expression" : "new Action<OrderStates,OrderEvents>(){\n @Override public void execute( StateContext<OrderStates,OrderEvents> context){\n }\n}\n", "expression" : "new Action<OrderStates,OrderEvents>(){\n @Override public void execute( StateContext<OrderStates,OrderEvents> context){\n }\n}\n",
"isLambda" : true, "isLambda" : true,

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 40, "lineNumber" : 40,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, { }, {
"event" : "ORDER_EVENT", "event" : "ORDER_EVENT",
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService", "className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
@@ -19,7 +22,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 52, "lineNumber" : 52,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -106,7 +112,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 40, "lineNumber" : 40,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -114,6 +123,82 @@
"targetState" : "BUSY", "targetState" : "BUSY",
"event" : "SUBMIT" "event" : "SUBMIT"
} ] } ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /maven/orders/submit",
"className" : "click.kamil.maven.api.MavenOrderApi",
"methodName" : "submitOrder",
"sourceFile" : "api-module/src/main/java/click/kamil/maven/api/MavenOrderApi.java",
"metadata" : {
"path" : "/maven/orders/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "orderId",
"type" : "String",
"annotations" : [ "RequestBody" ]
} ]
},
"methodChain" : [ "click.kamil.maven.api.MavenOrderApi.submitOrder", "click.kamil.maven.core.MavenOrderStateMachine.OrderController.submitOrder" ],
"triggerPoint" : {
"event" : "SUBMIT",
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderController",
"methodName" : "submitOrder",
"sourceFile" : "core-module/src/main/java/click/kamil/maven/core/MavenOrderStateMachine.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 40,
"polymorphicEvents" : [ "SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "INIT",
"targetState" : "BUSY",
"event" : "SUBMIT"
} ]
}, {
"entryPoint" : {
"type" : "JMS",
"name" : "JMS: order.queue",
"className" : "click.kamil.maven.api.JmsOrderListener",
"methodName" : "onMessage",
"sourceFile" : "api-module/src/main/java/click/kamil/maven/api/JmsOrderListener.java",
"metadata" : {
"protocol" : "JMS",
"destination" : "order.queue"
},
"parameters" : [ {
"name" : "message",
"type" : "String",
"annotations" : [ ]
} ]
},
"methodChain" : [ "click.kamil.maven.api.JmsOrderListener.onMessage", "click.kamil.maven.core.MavenOrderStateMachine.OrderService.onMessage" ],
"triggerPoint" : {
"event" : "ORDER_EVENT",
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
"methodName" : "onMessage",
"sourceFile" : "core-module/src/main/java/click/kamil/maven/core/MavenOrderStateMachine.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 52,
"polymorphicEvents" : [ "ORDER_EVENT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "BUSY",
"targetState" : "DONE",
"event" : "ORDER_EVENT"
} ]
} ], } ],
"properties" : { "properties" : {
"default" : { } "default" : { }
@@ -136,7 +221,7 @@
"rawName" : "\"SUBMIT\"", "rawName" : "\"SUBMIT\"",
"fullIdentifier" : "SUBMIT" "fullIdentifier" : "SUBMIT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ { "actions" : [ {
"expression" : "loggingAction()", "expression" : "loggingAction()",
"isLambda" : false, "isLambda" : false,
@@ -160,7 +245,7 @@
"rawName" : "\"ORDER_EVENT\"", "rawName" : "\"ORDER_EVENT\"",
"fullIdentifier" : "ORDER_EVENT" "fullIdentifier" : "ORDER_EVENT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -26,7 +26,7 @@
"rawName" : "Events.EVENT1", "rawName" : "Events.EVENT1",
"fullIdentifier" : "Events.EVENT1" "fullIdentifier" : "Events.EVENT1"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -43,7 +43,7 @@
"rawName" : "Events.EVENT2", "rawName" : "Events.EVENT2",
"fullIdentifier" : "Events.EVENT2" "fullIdentifier" : "Events.EVENT2"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -60,7 +60,7 @@
"rawName" : "Events.EVENT3", "rawName" : "Events.EVENT3",
"fullIdentifier" : "Events.EVENT3" "fullIdentifier" : "Events.EVENT3"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -77,7 +77,7 @@
"rawName" : "Events.EVENT4", "rawName" : "Events.EVENT4",
"fullIdentifier" : "Events.EVENT4" "fullIdentifier" : "Events.EVENT4"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -94,7 +94,7 @@
"rawName" : "Events.EVENT5", "rawName" : "Events.EVENT5",
"fullIdentifier" : "Events.EVENT5" "fullIdentifier" : "Events.EVENT5"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -111,7 +111,7 @@
"rawName" : "Events.EVENT6", "rawName" : "Events.EVENT6",
"fullIdentifier" : "Events.EVENT6" "fullIdentifier" : "Events.EVENT6"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -128,7 +128,7 @@
"rawName" : "Events.EVENT7", "rawName" : "Events.EVENT7",
"fullIdentifier" : "Events.EVENT7" "fullIdentifier" : "Events.EVENT7"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -145,7 +145,7 @@
"rawName" : "Events.EVENT8", "rawName" : "Events.EVENT8",
"fullIdentifier" : "Events.EVENT8" "fullIdentifier" : "Events.EVENT8"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -162,7 +162,7 @@
"rawName" : "Events.EVENT9", "rawName" : "Events.EVENT9",
"fullIdentifier" : "Events.EVENT9" "fullIdentifier" : "Events.EVENT9"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -179,7 +179,7 @@
"rawName" : "Events.EVENT10", "rawName" : "Events.EVENT10",
"fullIdentifier" : "Events.EVENT10" "fullIdentifier" : "Events.EVENT10"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -196,7 +196,7 @@
"rawName" : "Events.EVENT11", "rawName" : "Events.EVENT11",
"fullIdentifier" : "Events.EVENT11" "fullIdentifier" : "Events.EVENT11"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -213,7 +213,7 @@
"rawName" : "Events.EVENT12", "rawName" : "Events.EVENT12",
"fullIdentifier" : "Events.EVENT12" "fullIdentifier" : "Events.EVENT12"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -230,7 +230,7 @@
"rawName" : "Events.EVENT13", "rawName" : "Events.EVENT13",
"fullIdentifier" : "Events.EVENT13" "fullIdentifier" : "Events.EVENT13"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -247,7 +247,7 @@
"rawName" : "Events.EVENT14", "rawName" : "Events.EVENT14",
"fullIdentifier" : "Events.EVENT14" "fullIdentifier" : "Events.EVENT14"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -264,7 +264,7 @@
"rawName" : "Events.EVENT15", "rawName" : "Events.EVENT15",
"fullIdentifier" : "Events.EVENT15" "fullIdentifier" : "Events.EVENT15"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -281,7 +281,7 @@
"rawName" : "Events.EVENTY", "rawName" : "Events.EVENTY",
"fullIdentifier" : "Events.EVENTY" "fullIdentifier" : "Events.EVENTY"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
}, { }, {
@@ -295,14 +295,14 @@
"fullIdentifier" : "States.STATEX" "fullIdentifier" : "States.STATEX"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 91, "lineNumber" : 91,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -316,7 +316,7 @@
"fullIdentifier" : "States.STATEZ" "fullIdentifier" : "States.STATEZ"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -330,14 +330,14 @@
"fullIdentifier" : "States.STATE17" "fullIdentifier" : "States.STATE17"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value1\")", "expression" : "guardVarEquals(\"value1\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 97, "lineNumber" : 97,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -351,14 +351,14 @@
"fullIdentifier" : "States.STATE18" "fullIdentifier" : "States.STATE18"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value2\")", "expression" : "guardVarEquals(\"value2\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 98, "lineNumber" : 98,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -372,7 +372,7 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -386,14 +386,14 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header1\",\"foo\")", "expression" : "guardEventHeaderEquals(\"header1\",\"foo\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 103, "lineNumber" : 103,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -407,7 +407,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -421,14 +421,14 @@
"fullIdentifier" : "States.STATE19" "fullIdentifier" : "States.STATE19"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"value3\")", "expression" : "guardVarEquals(\"value3\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 108, "lineNumber" : 108,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -442,7 +442,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -456,14 +456,14 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"reset\")", "expression" : "guardVarEquals(\"reset\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 113, "lineNumber" : 113,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -477,7 +477,7 @@
"fullIdentifier" : "States.STATE20" "fullIdentifier" : "States.STATE20"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -491,14 +491,14 @@
"fullIdentifier" : "States.STATE5" "fullIdentifier" : "States.STATE5"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardEventHeaderEquals(\"header2\",\"bar\")", "expression" : "guardEventHeaderEquals(\"header2\",\"bar\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n", "internalLogic" : "context -> {\n Object header=context.getMessageHeader(headerName);\n return expected.equals(header);\n}\n",
"lineNumber" : 118, "lineNumber" : 118,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -512,7 +512,7 @@
"fullIdentifier" : "States.STATE1" "fullIdentifier" : "States.STATE1"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -526,14 +526,14 @@
"fullIdentifier" : "States.STATE13" "fullIdentifier" : "States.STATE13"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo13\")", "expression" : "guardVarEquals(\"goTo13\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 123, "lineNumber" : 123,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -547,14 +547,14 @@
"fullIdentifier" : "States.STATE14" "fullIdentifier" : "States.STATE14"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goTo14\")", "expression" : "guardVarEquals(\"goTo14\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 124, "lineNumber" : 124,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -568,7 +568,7 @@
"fullIdentifier" : "States.STATE15" "fullIdentifier" : "States.STATE15"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -582,14 +582,14 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"goBack10\")", "expression" : "guardVarEquals(\"goBack10\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 129, "lineNumber" : 129,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -603,7 +603,7 @@
"fullIdentifier" : "States.STATE11" "fullIdentifier" : "States.STATE11"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -617,14 +617,14 @@
"fullIdentifier" : "States.STATE12" "fullIdentifier" : "States.STATE12"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"loop12\")", "expression" : "guardVarEquals(\"loop12\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 134, "lineNumber" : 134,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -638,7 +638,7 @@
"fullIdentifier" : "States.STATE16" "fullIdentifier" : "States.STATE16"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -652,14 +652,14 @@
"fullIdentifier" : "States.STATE8" "fullIdentifier" : "States.STATE8"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBack\")", "expression" : "guardVarEquals(\"stepBack\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 139, "lineNumber" : 139,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -673,14 +673,14 @@
"fullIdentifier" : "States.STATE7" "fullIdentifier" : "States.STATE7"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"stepBackMore\")", "expression" : "guardVarEquals(\"stepBackMore\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 140, "lineNumber" : 140,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -694,7 +694,7 @@
"fullIdentifier" : "States.STATE6" "fullIdentifier" : "States.STATE6"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 2 "order" : 2
}, { }, {
@@ -708,14 +708,14 @@
"fullIdentifier" : "States.STATE9" "fullIdentifier" : "States.STATE9"
} ], } ],
"event" : null, "event" : null,
"guard" : { "guards" : [ {
"expression" : "guardVarEquals(\"forward9\")", "expression" : "guardVarEquals(\"forward9\")",
"isLambda" : false, "isLambda" : false,
"internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n", "internalLogic" : "context -> {\n Object var=context.getExtendedState().getVariables().get(\"var\");\n return expected.equals(var);\n}\n",
"lineNumber" : 145, "lineNumber" : 145,
"className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration", "className" : "click.kamil.examples.statemachine.inheritancestate.AbstractOneStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java" "sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritancestate/AbstractOneStateMachineConfiguration.java"
}, } ],
"actions" : [ ], "actions" : [ ],
"order" : 0 "order" : 0
}, { }, {
@@ -729,7 +729,7 @@
"fullIdentifier" : "States.STATE10" "fullIdentifier" : "States.STATE10"
} ], } ],
"event" : null, "event" : null,
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : 1 "order" : 1
}, { }, {
@@ -746,7 +746,7 @@
"rawName" : "Events.EVENTX", "rawName" : "Events.EVENTX",
"fullIdentifier" : "Events.EVENTX" "fullIdentifier" : "Events.EVENTX"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -9,7 +9,10 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
} ], } ],
"entryPoints" : [ { "entryPoints" : [ {
"type" : "REST", "type" : "REST",
@@ -69,7 +72,48 @@
"stateMachineId" : null, "stateMachineId" : null,
"sourceState" : null, "sourceState" : null,
"lineNumber" : 18, "lineNumber" : 18,
"polymorphicEvents" : null "polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "NEW",
"targetState" : "PROCESSING",
"event" : "SUBMIT"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /orders/submit",
"className" : "click.kamil.multi.api.OrderApi",
"methodName" : "submitOrder",
"sourceFile" : "api-module/src/main/java/click/kamil/multi/api/OrderApi.java",
"metadata" : {
"path" : "/orders/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "orderId",
"type" : "String",
"annotations" : [ "RequestBody" ]
} ]
},
"methodChain" : [ "click.kamil.multi.api.OrderApi.submitOrder", "click.kamil.multi.core.OrderController.submitOrder" ],
"triggerPoint" : {
"event" : "SUBMIT",
"className" : "click.kamil.multi.core.OrderController",
"methodName" : "submitOrder",
"sourceFile" : "core-module/src/main/java/click/kamil/multi/core/OrderController.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 18,
"polymorphicEvents" : [ "SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
}, },
"contextMachineId" : null, "contextMachineId" : null,
"matchedTransitions" : [ { "matchedTransitions" : [ {
@@ -99,7 +143,7 @@
"rawName" : "\"SUBMIT\"", "rawName" : "\"SUBMIT\"",
"fullIdentifier" : "SUBMIT" "fullIdentifier" : "SUBMIT"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ { "actions" : [ {
"expression" : "processAction()", "expression" : "processAction()",
"isLambda" : false, "isLambda" : false,
@@ -123,7 +167,7 @@
"rawName" : "\"FINISH\"", "rawName" : "\"FINISH\"",
"fullIdentifier" : "FINISH" "fullIdentifier" : "FINISH"
}, },
"guard" : null, "guards" : [ ],
"actions" : [ ], "actions" : [ ],
"order" : null "order" : null
} ], } ],

View File

@@ -0,0 +1,12 @@
digraph statemachine {
rankdir=LR;
node [shape=rounded, style=filled, fillcolor=white, fontname="Arial"];
edge [fontname="Arial", fontsize=10];
_start [shape=circle, label="", fillcolor=black, width=0.1];
_start -> NEW;
SHIPPED [fillcolor=lightgray];
NEW -> PENDING [label="OrderEvent.PAY / λ", style="solid", color="black"];
PENDING -> SHIPPED [label="OrderEvent.SHIP", style="solid", color="black"];
}

View File

@@ -0,0 +1,686 @@
{
"metadata" : {
"triggers" : [ {
"event" : "OrderEvent.PAY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 30,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "OrderEvent.SHIP",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 37,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.SUBMIT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 44,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.APPROVE",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 51,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "DocumentEvent.REJECT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 58,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "UserEvent.VERIFY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 65,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "UserEvent.SUSPEND",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 72,
"polymorphicEvents" : null,
"external" : false,
"constraint" : null,
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "ORDER",
"lineNumber" : 81,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "DOCUMENT",
"lineNumber" : 87,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
"ambiguous" : false
}, {
"event" : "event",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "USER",
"lineNumber" : 93,
"polymorphicEvents" : null,
"external" : false,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
} ],
"entryPoints" : [ {
"type" : "REST",
"name" : "POST /api/machine/order/pay",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/pay",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/order/ship",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/ship",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/submit",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/approve",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/approve",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/document/reject",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/reject",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/user/verify",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/verify",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/user/suspend",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/suspend",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
}, {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
} ],
"callChains" : [ {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/order/pay",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/pay",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.payOrder", "click.kamil.enterprise.web.StateMachineDispatcher.payOrder" ],
"triggerPoint" : {
"event" : "OrderEvent.PAY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "payOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 30,
"polymorphicEvents" : [ "OrderEvent.PAY" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "OrderState.NEW",
"targetState" : "OrderState.PENDING",
"event" : "OrderEvent.PAY"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/order/ship",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/order/ship",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.shipOrder", "click.kamil.enterprise.web.StateMachineDispatcher.shipOrder" ],
"triggerPoint" : {
"event" : "OrderEvent.SHIP",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "shipOrder",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 37,
"polymorphicEvents" : [ "OrderEvent.SHIP" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "OrderState.PENDING",
"targetState" : "OrderState.SHIPPED",
"event" : "OrderEvent.SHIP"
} ]
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/submit",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/submit",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.submitDocument", "click.kamil.enterprise.web.StateMachineDispatcher.submitDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.SUBMIT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "submitDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 44,
"polymorphicEvents" : [ "DocumentEvent.SUBMIT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/approve",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/approve",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.approveDocument", "click.kamil.enterprise.web.StateMachineDispatcher.approveDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.APPROVE",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "approveDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 51,
"polymorphicEvents" : [ "DocumentEvent.APPROVE" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/document/reject",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/document/reject",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.rejectDocument", "click.kamil.enterprise.web.StateMachineDispatcher.rejectDocument" ],
"triggerPoint" : {
"event" : "DocumentEvent.REJECT",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "rejectDocument",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 58,
"polymorphicEvents" : [ "DocumentEvent.REJECT" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/user/verify",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/verify",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.verifyUser", "click.kamil.enterprise.web.StateMachineDispatcher.verifyUser" ],
"triggerPoint" : {
"event" : "UserEvent.VERIFY",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "verifyUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 65,
"polymorphicEvents" : [ "UserEvent.VERIFY" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/user/suspend",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/user/suspend",
"verb" : "POST"
},
"parameters" : [ {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.suspendUser", "click.kamil.enterprise.web.StateMachineDispatcher.suspendUser" ],
"triggerPoint" : {
"event" : "UserEvent.SUSPEND",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "suspendUser",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 72,
"polymorphicEvents" : [ "UserEvent.SUSPEND" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "ORDER",
"lineNumber" : 81,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "DOCUMENT",
"lineNumber" : 87,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
"name" : "POST /api/machine/{machineType}/transition/{event}",
"className" : "click.kamil.enterprise.web.StateMachineController",
"methodName" : "transition",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
"metadata" : {
"path" : "/api/machine/{machineType}/transition/{event}",
"verb" : "POST"
},
"parameters" : [ {
"name" : "machineType",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "event",
"type" : "String",
"annotations" : [ "PathVariable" ]
}, {
"name" : "userId",
"type" : "String",
"annotations" : [ "RequestParam" ]
} ]
},
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
"triggerPoint" : {
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
"methodName" : "dispatch",
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : "USER",
"lineNumber" : 93,
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
},
"contextMachineId" : null,
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }
}
},
"name" : "click.kamil.enterprise.machines.order.OrderStateMachineConfiguration",
"renderChoicesAsDiamonds" : true,
"startStates" : [ "OrderState.NEW" ],
"transitions" : [ {
"type" : "EXTERNAL",
"sourceStates" : [ {
"rawName" : "OrderState.NEW",
"fullIdentifier" : "OrderState.NEW"
} ],
"targetStates" : [ {
"rawName" : "OrderState.PENDING",
"fullIdentifier" : "OrderState.PENDING"
} ],
"event" : {
"rawName" : "OrderEvent.PAY",
"fullIdentifier" : "OrderEvent.PAY"
},
"guards" : [ ],
"actions" : [ {
"expression" : "context -> System.out.println(\"Payment processed.\")",
"isLambda" : true,
"internalLogic" : "context -> System.out.println(\"Payment processed.\")",
"lineNumber" : 28,
"className" : "click.kamil.enterprise.machines.order.OrderStateMachineConfiguration",
"sourceFile" : "src/main/java/click/kamil/enterprise/machines/order/OrderStateMachineConfiguration.java"
} ],
"order" : null
}, {
"type" : "EXTERNAL",
"sourceStates" : [ {
"rawName" : "OrderState.PENDING",
"fullIdentifier" : "OrderState.PENDING"
} ],
"targetStates" : [ {
"rawName" : "OrderState.SHIPPED",
"fullIdentifier" : "OrderState.SHIPPED"
} ],
"event" : {
"rawName" : "OrderEvent.SHIP",
"fullIdentifier" : "OrderEvent.SHIP"
},
"guards" : [ ],
"actions" : [ ],
"order" : null
} ],
"endStates" : [ "OrderState.SHIPPED" ]
}

View File

@@ -0,0 +1,32 @@
@startuml
!pragma layout smetana
set separator none
hide empty description
hide stereotype
skinparam state {
BackgroundColor white
BorderColor #94a3b8
BorderThickness 1
FontName Inter
FontSize 9
FontStyle bold
RoundCorner 20
Padding 1
}
skinparam shadowing false
skinparam ArrowFontName JetBrains Mono
skinparam ArrowFontSize 8
skinparam ArrowColor #cbd5e1
skinparam ArrowThickness 1
skinparam dpi 110
skinparam svgLinkTarget _self
[*] --> OrderState.NEW
OrderState.NEW -[#1E90FF,bold]-> OrderState.PENDING <<external>> : OrderEvent.PAY / λ
OrderState.PENDING -[#1E90FF,bold]-> OrderState.SHIPPED <<external>> : OrderEvent.SHIP
OrderState.SHIPPED --> [*]
@enduml

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="NEW">
<state id="NEW">
<transition target="PENDING" event="OrderEvent.PAY"/>
</state>
<state id="PENDING">
<transition target="SHIPPED" event="OrderEvent.SHIP"/>
</state>
<state id="SHIPPED">
</state>
</scxml>

Some files were not shown because too many files have changed in this diff Show More