diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java index 4451b85..759288f 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java @@ -159,21 +159,52 @@ public class ConstantResolver { java.util.Map localVars = new java.util.HashMap<>(); java.util.Set declaredLocals = new java.util.HashSet<>(); - + for (int i = 0; i < mi.arguments().size(); i++) { Expression arg = (Expression) mi.arguments().get(i); String val = resolveInternal(arg, context, visited); if (val != null) { - org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) md.parameters().get(i); + org.eclipse.jdt.core.dom.SingleVariableDeclaration param = + (org.eclipse.jdt.core.dom.SingleVariableDeclaration) md.parameters().get(i); String paramName = param.getName().getIdentifier(); localVars.put(paramName, val); declaredLocals.add(paramName); } } + return evaluateBody(md.getBody(), localVars, declaredLocals, context, visited); + } + + /** + * Evaluates a method body with pre-supplied local values (e.g. field values derived + * from the specific {@code ClassInstanceCreation} that created the receiver object). + * Used by {@link click.kamil.springstatemachineexporter.analysis.service.AbstractCallGraphEngine} + * to resolve transforming getters — methods that map a constructor-injected enum to a + * different state-machine enum via switch expressions. + */ + public String evaluateMethodBodyWithLocals(MethodDeclaration md, + java.util.Map prebuiltLocals, + CodebaseContext context, + Set visited) { + if (md.getBody() == null) return null; + java.util.Map localVars = new java.util.HashMap<>(prebuiltLocals); + java.util.Set declaredLocals = new java.util.HashSet<>(prebuiltLocals.keySet()); + return evaluateBody(md.getBody(), localVars, declaredLocals, context, visited); + } + + /** + * Core body-evaluation logic shared by {@link #evaluateMethodOutput} and + * {@link #evaluateMethodBodyWithLocals}. Visits the block, tracks assignments, + * evaluates switch statements/expressions, and captures the first resolved return value. + */ + private String evaluateBody(org.eclipse.jdt.core.dom.Block body, + java.util.Map localVars, + java.util.Set declaredLocals, + CodebaseContext context, + Set visited) { String[] finalResult = new String[1]; - md.getBody().accept(new ASTVisitor() { + body.accept(new ASTVisitor() { @Override public boolean visit(VariableDeclarationStatement vds) { for (Object fragmentObj : vds.fragments()) { @@ -183,9 +214,7 @@ public class ConstantResolver { if (fragment.getInitializer() != null) { String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars); if (rhsVal == null) rhsVal = resolveInternal(fragment.getInitializer(), context, visited); - if (rhsVal != null) { - localVars.put(varName, rhsVal); - } + if (rhsVal != null) localVars.put(varName, rhsVal); } } } @@ -201,22 +230,16 @@ public class ConstantResolver { } else if (lhs instanceof FieldAccess fa) { varName = "this." + fa.getName().getIdentifier(); } - String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars); if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited); - if (varName != null && rhsVal != null) { if (varName.startsWith("this.")) { localVars.put(varName, rhsVal); String bareName = varName.substring(5); - if (!declaredLocals.contains(bareName)) { - localVars.put(bareName, rhsVal); // alias without 'this.' only if not shadowed - } + if (!declaredLocals.contains(bareName)) localVars.put(bareName, rhsVal); } else { localVars.put(varName, rhsVal); - if (!declaredLocals.contains(varName)) { - localVars.put("this." + varName, rhsVal); // alias with 'this.' only if not shadowed - } + if (!declaredLocals.contains(varName)) localVars.put("this." + varName, rhsVal); } } return super.visit(assignment); @@ -228,7 +251,7 @@ public class ConstantResolver { String result = evaluateSwitchStatement(ss, localVars, context, visited); if (result != null) finalResult[0] = result; } - return false; // Do not visit children — ReturnStatements inside the switch are handled by evaluateSwitchStatement + return false; // children handled inside evaluateSwitchStatement } @Override @@ -263,6 +286,10 @@ public class ConstantResolver { } else { for (Object exprObj : sc.expressions()) { String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues); + // Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback + if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) { + caseVal = caseSn.getIdentifier(); + } if (caseVal != null && switchVar.endsWith(caseVal)) { matchingCase = true; break; @@ -295,6 +322,10 @@ public class ConstantResolver { } else { for (Object exprObj : sc.expressions()) { String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues); + // Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback + if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) { + caseVal = caseSn.getIdentifier(); + } if (caseVal != null && switchVar.endsWith(caseVal)) { matchingCase = true; break; diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java index 70e43ad..be76a3e 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java @@ -256,7 +256,14 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { declaredType = polymorphicEvents.isEmpty() ? null : polymorphicEvents.get(0); } else if (exprNode instanceof org.eclipse.jdt.core.dom.SimpleName sn) { varName = sn.getIdentifier(); - methodName = "VariableReference"; // We just want to trigger deep trace + // Check if it's a constant (Java UPPER_SNAKE_CASE convention) + // If so, add it directly to polymorphicEvents instead of tracing as a variable + if (varName.equals(varName.toUpperCase()) && varName.contains("_")) { + polymorphicEvents.add(varName); + methodName = null; // Skip variable tracing + } else { + methodName = "VariableReference"; // We just want to trigger deep trace + } } else if (exprNode instanceof org.eclipse.jdt.core.dom.ParenthesizedExpression pe && pe.getExpression() instanceof org.eclipse.jdt.core.dom.CastExpression ce && ce.getExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) { @@ -932,7 +939,19 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } } } + if (!handledByLombok && methodName != null && !methodName.isEmpty()) { + // Try proper getter evaluation: builds field→value from constructor args, + // then evaluates the getter body (handles switch-based enum transformations). + java.util.List narrowed = resolveInlineInstantiationGetterResult( + cic, methodName, context, new java.util.HashSet<>()); + if (!narrowed.isEmpty()) { + constants.addAll(narrowed); + handledByLombok = true; // reuse flag to skip fallback below + } + } if (!handledByLombok) { + // Fallback: extract all constructor args (catches cases where getter + // evaluation is not possible, e.g. no source for the wrapper class) for (Object argObj : cic.arguments()) { extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) argObj, constants); } @@ -961,6 +980,146 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } } + /** + * Builds a map of {@code fieldName → resolved-value} (and {@code this.fieldName → value}) + * by correlating the actual arguments of the given {@link ClassInstanceCreation} with the + * field assignments found in the matching constructor body. + * + *

Handles both {@code this.field = param} and bare {@code field = param} assignment forms. + */ + protected java.util.Map buildFieldValuesFromCIC( + org.eclipse.jdt.core.dom.TypeDeclaration td, + org.eclipse.jdt.core.dom.ClassInstanceCreation cic, + CodebaseContext context) { + + java.util.Map fieldValues = new java.util.HashMap<>(); + + for (org.eclipse.jdt.core.dom.MethodDeclaration ctor : td.getMethods()) { + if (!ctor.isConstructor() || ctor.getBody() == null) continue; + if (ctor.parameters().size() != cic.arguments().size()) continue; + + // 1. Map each parameter name → resolved argument value + java.util.Map paramValues = new java.util.HashMap<>(); + for (int i = 0; i < ctor.parameters().size(); i++) { + String pName = ((org.eclipse.jdt.core.dom.SingleVariableDeclaration) + ctor.parameters().get(i)).getName().getIdentifier(); + org.eclipse.jdt.core.dom.Expression argExpr = + (org.eclipse.jdt.core.dom.Expression) cic.arguments().get(i); + java.util.List consts = new java.util.ArrayList<>(); + extractConstantsFromArgument(argExpr, consts); + if (!consts.isEmpty()) { + paramValues.put(pName, consts.get(0)); + } else { + String resolved = constantResolver.resolve(argExpr, context); + if (resolved != null) paramValues.put(pName, resolved); + } + } + if (paramValues.isEmpty()) continue; + + // 2. Scan constructor body for field assignments: this.field = param OR field = param + ctor.getBody().accept(new org.eclipse.jdt.core.dom.ASTVisitor() { + @Override + public boolean visit(org.eclipse.jdt.core.dom.Assignment node) { + org.eclipse.jdt.core.dom.Expression lhs = node.getLeftHandSide(); + org.eclipse.jdt.core.dom.Expression rhs = node.getRightHandSide(); + + String fieldName = null; + if (lhs instanceof org.eclipse.jdt.core.dom.FieldAccess fa + && fa.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression) { + fieldName = fa.getName().getIdentifier(); + } else if (lhs instanceof org.eclipse.jdt.core.dom.SimpleName sn + && !paramValues.containsKey(sn.getIdentifier())) { + // Bare field assignment (not a parameter-to-itself assignment) + fieldName = sn.getIdentifier(); + } + + if (fieldName != null && rhs instanceof org.eclipse.jdt.core.dom.SimpleName snRhs) { + String paramVal = paramValues.get(snRhs.getIdentifier()); + if (paramVal != null) { + fieldValues.put(fieldName, paramVal); + fieldValues.put("this." + fieldName, paramVal); + } + } + return super.visit(node); + } + }); + break; // first matching constructor is enough + } + return fieldValues; + } + + /** + * Searches for a {@code return new SomeType(...)} statement in the given method, recursing + * into interface implementations when the method body is absent. + */ + protected org.eclipse.jdt.core.dom.ClassInstanceCreation findReturnedCIC( + String className, String methodName, CodebaseContext context) { + + org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration(className); + if (td != null) { + org.eclipse.jdt.core.dom.MethodDeclaration md = + context.findMethodDeclaration(td, methodName, true); + if (md != null && md.getBody() != null) { + org.eclipse.jdt.core.dom.ClassInstanceCreation[] result = {null}; + md.getBody().accept(new org.eclipse.jdt.core.dom.ASTVisitor() { + @Override + public boolean visit(org.eclipse.jdt.core.dom.ReturnStatement rs) { + if (rs.getExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) { + result[0] = cic; + } + return false; + } + }); + if (result[0] != null) return result[0]; + } + } + // Recurse into implementations (handles interfaces / abstract classes) + java.util.List impls = context.getImplementations(className); + for (String impl : impls) { + org.eclipse.jdt.core.dom.ClassInstanceCreation cic = findReturnedCIC(impl, methodName, context); + if (cic != null) return cic; + } + return null; + } + + /** + * Resolves the value produced by calling {@code getterName} on an object created via the + * given {@link ClassInstanceCreation}. + * + *

Algorithm: + *

    + *
  1. Build a {@code fieldName → value} map from the CIC's constructor arguments.
  2. + *
  3. Ask {@link click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver} + * to evaluate the getter body with those values as pre-seeded locals.
  4. + *
+ * + *

This correctly handles both simple pass-through getters ({@code return field}) and + * transformation getters that map one enum to another via a switch expression. + * + * @return resolved values, or an empty list when resolution is not possible + */ + private java.util.List resolveInlineInstantiationGetterResult( + org.eclipse.jdt.core.dom.ClassInstanceCreation cic, + String getterName, + CodebaseContext context, + java.util.Set visited) { + + String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils + .extractSimpleTypeName(cic.getType()); + org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration(typeName); + if (td == null) return java.util.Collections.emptyList(); + + org.eclipse.jdt.core.dom.MethodDeclaration getter = + context.findMethodDeclaration(td, getterName, true); + if (getter == null || getter.getBody() == null) return java.util.Collections.emptyList(); + + java.util.Map fieldValues = buildFieldValuesFromCIC(td, cic, context); + if (fieldValues.isEmpty()) return java.util.Collections.emptyList(); + + String result = constantResolver.evaluateMethodBodyWithLocals(getter, fieldValues, context, visited); + return result != null ? java.util.List.of(result) : java.util.Collections.emptyList(); + } + private int findConstructorArgumentIndexForLombokField(TypeDeclaration td, String fieldName) { boolean isAllArgsConstructor = false; boolean isRequiredArgsConstructor = false; diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngine.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngine.java index b9b4a54..9396bfa 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngine.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngine.java @@ -162,8 +162,19 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine { expr = firstArg; // Only unwrap if it's actually a constant } } - - String val = constantResolver.resolve(expr, context); + + String val = null; + + // Narrow resolution for "localVar.getX()" patterns where localVar is initialized + // from a ClassInstanceCreation (directly or via a factory method). This avoids + // the broad ENUM_SET fallback when the getter transforms one enum to another. + if (expr instanceof MethodInvocation getterMi + && getterMi.getExpression() instanceof SimpleName instanceSn + && getterMi.arguments().isEmpty()) { + val = tryNarrowGetterViaLocalVarChain(getterMi, instanceSn); + } + + if (val == null) val = constantResolver.resolve(expr, context); if (val == null) { val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc) } @@ -317,4 +328,163 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine { return null; } + /** + * Attempts to narrow the resolution of {@code instanceVar.getterMethod()} when the + * instance variable is initialised from a {@link org.eclipse.jdt.core.dom.ClassInstanceCreation} + * (either directly or via a factory/mapper method). + * + *

This handles both: + *

    + *
  • Simple pass-through: {@code Wrapper(MyEnum e){this.e=e;} getE(){return e;}}
  • + *
  • Transformation: {@code getSmEvent(){return switch(dtoEnum){case A->SmEvents.X;…};}}
  • + *
+ * + * @return the resolved constant string, or {@code null} when narrowing is not possible + */ + private String tryNarrowGetterViaLocalVarChain( + MethodInvocation getterCall, SimpleName instanceSn) { + + MethodDeclaration enclosing = findEnclosingMethod(getterCall); + if (enclosing == null || enclosing.getBody() == null) return null; + + String varName = instanceSn.getIdentifier(); + String getterName = getterCall.getName().getIdentifier(); + + // Find the declared type and initialiser of the local variable + String[] varTypeName = {null}; + Expression[] initExpr = {null}; + enclosing.getBody().accept(new ASTVisitor() { + @Override + public boolean visit(VariableDeclarationStatement vds) { + for (Object fragObj : vds.fragments()) { + VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj; + if (frag.getName().getIdentifier().equals(varName)) { + varTypeName[0] = vds.getType().toString(); + initExpr[0] = frag.getInitializer(); + } + } + return super.visit(vds); + } + }); + if (varTypeName[0] == null) return null; + + // Obtain the ClassInstanceCreation that created the object + org.eclipse.jdt.core.dom.ClassInstanceCreation cic = null; + if (initExpr[0] instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation directCic) { + cic = directCic; + } else if (initExpr[0] instanceof MethodInvocation factoryMi + && factoryMi.getExpression() instanceof SimpleName receiverSn) { + // e.g. "mapper.toMsg(dto)" → find the CIC returned by the factory method + String receiverType = resolveReceiverTypeFallback(receiverSn); + if (receiverType != null) { + cic = findReturnedCIC(receiverType, factoryMi.getName().getIdentifier(), context); + } + } + if (cic == null) return null; + + // Resolve the variable's declared type to a TypeDeclaration + CompilationUnit cu = (CompilationUnit) getterCall.getRoot(); + TypeDeclaration varTypeTd = context.getTypeDeclaration(varTypeName[0], cu); + if (varTypeTd == null) varTypeTd = context.getTypeDeclaration(varTypeName[0]); + if (varTypeTd == null) return null; + +// Evaluate the getter body with field values substituted from the CIC's constructor args + java.util.Map fieldValues = buildFieldValuesFromCIC(varTypeTd, cic, context); + if (fieldValues.isEmpty()) return null; + + // Track setter calls on the variable between its initialization and the getter call + // to capture field updates like e.setType("NEW_VALUE") + trackSetterCallsBetween(enclosing.getBody(), varName, getterCall, fieldValues, context); + + MethodDeclaration getter = context.findMethodDeclaration(varTypeTd, getterName, true); + if (getter == null || getter.getBody() == null) return null; + + return constantResolver.evaluateMethodBodyWithLocals( + getter, fieldValues, context, new java.util.HashSet<>()); + } + + /** + * Scans the method body for setter calls or field assignments to the given variable + * that occur between the variable's initialization and the getter call. + * Updates fieldValues with any field values set via setters. + */ + private void trackSetterCallsBetween(org.eclipse.jdt.core.dom.Block methodBody, String varName, + org.eclipse.jdt.core.dom.MethodInvocation getterCall, + java.util.Map fieldValues, + CodebaseContext context) { + java.util.List statements = new java.util.ArrayList<>(); + for (Object stmtObj : methodBody.statements()) { + statements.add((org.eclipse.jdt.core.dom.ASTNode) stmtObj); + } + + int varDeclIndex = -1; + int getterCallIndex = -1; + for (int i = 0; i < statements.size(); i++) { + org.eclipse.jdt.core.dom.ASTNode stmt = statements.get(i); + // Find variable declaration + if (varDeclIndex == -1) { + if (stmt instanceof org.eclipse.jdt.core.dom.VariableDeclarationStatement vds) { + for (Object fragObj : vds.fragments()) { + if (fragObj instanceof org.eclipse.jdt.core.dom.VariableDeclarationFragment frag + && frag.getName().getIdentifier().equals(varName)) { + varDeclIndex = i; + break; + } + } + } + } + // Find getter call + if (getterCallIndex == -1) { + if (stmt.toString().contains(getterCall.toString())) { + getterCallIndex = i; + break; + } + } + } + + if (varDeclIndex >= 0 && getterCallIndex > varDeclIndex) { + // Scan statements between var declaration and getter call + for (int i = varDeclIndex + 1; i < getterCallIndex; i++) { + org.eclipse.jdt.core.dom.ASTNode stmt = statements.get(i); + if (stmt instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) { + org.eclipse.jdt.core.dom.Expression expr = es.getExpression(); + // Check for setter calls: var.setField(value) + if (expr instanceof org.eclipse.jdt.core.dom.MethodInvocation mi) { + if (mi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn + && sn.getIdentifier().equals(varName)) { + String methodName = mi.getName().getIdentifier(); + // Check if it's a setter (setXxx or xxx) + if (methodName.startsWith("set") && methodName.length() > 3) { + String fieldName = Character.toLowerCase(methodName.charAt(3)) + + methodName.substring(4); + if (!mi.arguments().isEmpty()) { + org.eclipse.jdt.core.dom.Expression arg = (org.eclipse.jdt.core.dom.Expression) mi.arguments().get(0); + String val = constantResolver.resolve(arg, context); + if (val != null) { + fieldValues.put(fieldName, val); + fieldValues.put("this." + fieldName, val); + } + } + } + } + } + // Check for direct field assignments: var.field = value + else if (expr instanceof org.eclipse.jdt.core.dom.Assignment assignment) { + if (assignment.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.FieldAccess fa) { + if (fa.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn + && sn.getIdentifier().equals(varName)) { + String fieldName = fa.getName().getIdentifier(); + org.eclipse.jdt.core.dom.Expression rhs = assignment.getRightHandSide(); + String val = constantResolver.resolve(rhs, context); + if (val != null) { + fieldValues.put(fieldName, val); + fieldValues.put("this." + fieldName, val); + } + } + } + } + } + } + } + } } \ No newline at end of file diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java index daa5e74..5299437 100644 --- a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java @@ -2830,4 +2830,165 @@ class HeuristicCallGraphEngineTest { assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) .containsExactlyInAnyOrder("ORDER_DISPATCHED"); } + + /** + * Diagnostic: enum passed to a wrapper object constructor, then retrieved via getter. + * + * Pattern: + * entry: event = mapper.toEvent(dto); machine.fire(event.getType()); + * mapper: OrderMapperImpl.toEvent → new OrderEvent(States.PAYMENT_RECEIVED) + * wrapper: OrderEvent(States type) { this.type = type; } getType() { return type; } + * + * Expected: polymorphicEvents = ["States.PAYMENT_RECEIVED"] + * Tests whether the engine traces: getter → field → constructor arg → constant. + */ + @Test + void shouldResolveEnumPassedThroughWrapperObjectAndGetter() throws IOException { + String source = """ + package com.example; + + public class OrderController { + private OrderMapper mapper; + private StateMachine machine; + + public void processOrder(String dto) { + OrderEvent event = mapper.toEvent(dto); + machine.fire(event.getType()); + } + } + + interface OrderMapper { + OrderEvent toEvent(String input); + } + + class OrderMapperImpl implements OrderMapper { + public OrderEvent toEvent(String input) { + return new OrderEvent(States.PAYMENT_RECEIVED); + } + } + + class OrderEvent { + private States type; + public OrderEvent(States type) { this.type = type; } + public States getType() { return type; } // bare field, no this. + } + + class StateMachine { + public void fire(States event) {} + } + + enum States { PAYMENT_RECEIVED, CANCELLED } + """; + java.nio.file.Path tempDir = java.nio.file.Files.createTempDirectory("callgraph_test_wrapper"); + java.nio.file.Files.writeString(tempDir.resolve("OrderConfig.java"), source); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context); + + EntryPoint entryPoint = EntryPoint.builder() + .className("com.example.OrderController") + .methodName("processOrder") + .build(); + + TriggerPoint trigger = TriggerPoint.builder() + .className("com.example.StateMachine") + .methodName("fire") + .event("event") + .build(); + + List chains = engine.findChains(List.of(entryPoint), List.of(trigger)); + + assertThat(chains).hasSize(1); + System.out.println("DIAGNOSTIC polymorphicEvents: " + + chains.get(0).getTriggerPoint().getPolymorphicEvents()); + assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) + .containsExactlyInAnyOrder("States.PAYMENT_RECEIVED"); + } + + /** + * Two-enum transformation: a DTO-level enum (OrderType) is passed into a wrapper + * constructor. The wrapper's getter transforms it via a switch expression to a + * state-machine enum (SMEvent). The engine must return the SM enum, NOT the DTO enum. + * + * Pattern: + * entry: msg = mapper.toMsg(dto); machine.fire(msg.getSmEvent()); + * wrapper: OrderMsg(OrderType t) { this.type = t; } + * getSmEvent() { return switch(type) { case PAY -> SMEvent.PAY_RECEIVED; ... }; } + * + * Root cause of bug: engine extracted the constructor arg (OrderType.PAY) instead of + * evaluating the getter's switch body, returning the wrong enum. + */ + @Test + void shouldResolveTransformingGetterThatMapsDtoEnumToStateMachineEnum() throws IOException { + String source = """ + package com.example; + + public class OrderController { + private OrderMapper mapper; + private StateMachine machine; + + public void processOrder(String dto) { + OrderMsg msg = mapper.toMsg(dto); + machine.fire(msg.getSmEvent()); + } + } + + interface OrderMapper { + OrderMsg toMsg(String input); + } + + class OrderMapperImpl implements OrderMapper { + public OrderMsg toMsg(String input) { + return new OrderMsg(OrderType.PAY); + } + } + + class OrderMsg { + private OrderType type; + public OrderMsg(OrderType type) { this.type = type; } + public SMEvent getSmEvent() { + return switch (type) { + case PAY -> SMEvent.PAY_RECEIVED; + case CANCEL -> SMEvent.ORDER_CANCELLED; + }; + } + } + + class StateMachine { + public void fire(SMEvent event) {} + } + + enum OrderType { PAY, CANCEL } + enum SMEvent { PAY_RECEIVED, ORDER_CANCELLED } + """; + java.nio.file.Path tempDir = java.nio.file.Files.createTempDirectory("callgraph_test_twoenum"); + java.nio.file.Files.writeString(tempDir.resolve("OrderConfig.java"), source); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context); + + EntryPoint entryPoint = EntryPoint.builder() + .className("com.example.OrderController") + .methodName("processOrder") + .build(); + + TriggerPoint trigger = TriggerPoint.builder() + .className("com.example.StateMachine") + .methodName("fire") + .event("event") + .build(); + + List chains = engine.findChains(List.of(entryPoint), List.of(trigger)); + + assertThat(chains).hasSize(1); + System.out.println("TWO-ENUM polymorphicEvents: " + + chains.get(0).getTriggerPoint().getPolymorphicEvents()); + // Must return the SM enum, NOT the DTO enum (OrderType.PAY would be wrong) + assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) + .containsExactlyInAnyOrder("SMEvent.PAY_RECEIVED"); + } } diff --git a/state_machine_exporter/src/test/resources/golden/EnterpriseStateMachineConfig/EnterpriseStateMachineConfig.json b/state_machine_exporter/src/test/resources/golden/EnterpriseStateMachineConfig/EnterpriseStateMachineConfig.json index f44e11f..d8e6e21 100644 --- a/state_machine_exporter/src/test/resources/golden/EnterpriseStateMachineConfig/EnterpriseStateMachineConfig.json +++ b/state_machine_exporter/src/test/resources/golden/EnterpriseStateMachineConfig/EnterpriseStateMachineConfig.json @@ -192,7 +192,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 16, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PLACE_ORDER" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -227,7 +227,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 21, - "polymorphicEvents" : null + "polymorphicEvents" : [ "CANCEL_ORDER" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -288,7 +288,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 18, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PAY_ORDER" ] }, "contextMachineId" : null, "matchedTransitions" : [ { diff --git a/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig/ExtendedStateMachineConfig.json b/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig/ExtendedStateMachineConfig.json index aa0bec7..9deb804 100644 --- a/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig/ExtendedStateMachineConfig.json +++ b/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig/ExtendedStateMachineConfig.json @@ -50,16 +50,6 @@ "sourceState" : null, "lineNumber" : 19, "polymorphicEvents" : null - }, { - "event" : "EXTERNAL_TRIGGER", - "className" : "click.kamil.examples.statemachine.extended.service.OrderService", - "methodName" : "processSubmit", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java", - "sourceModule" : null, - "stateMachineId" : null, - "sourceState" : null, - "lineNumber" : 19, - "polymorphicEvents" : null }, { "event" : "SUBMIT_EVENT", "className" : "click.kamil.examples.statemachine.extended.service.OrderService", @@ -355,37 +345,6 @@ } ] } ], "callChains" : [ { - "entryPoint" : { - "type" : "REST", - "name" : "POST /api/orders/submit", - "className" : "click.kamil.examples.statemachine.extended.web.OrderController", - "methodName" : "submitOrder", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/OrderController.java", - "metadata" : { - "path" : "/api/orders/submit", - "verb" : "POST" - }, - "parameters" : [ ] - }, - "methodChain" : [ "click.kamil.examples.statemachine.extended.web.OrderController.submitOrder", "click.kamil.examples.statemachine.extended.service.OrderService.processSubmit" ], - "triggerPoint" : { - "event" : "EXTERNAL_TRIGGER", - "className" : "click.kamil.examples.statemachine.extended.service.OrderService", - "methodName" : "processSubmit", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java", - "sourceModule" : null, - "stateMachineId" : null, - "sourceState" : null, - "lineNumber" : 19, - "polymorphicEvents" : null - }, - "contextMachineId" : null, - "matchedTransitions" : [ { - "sourceState" : "START", - "targetState" : "PROCESSING", - "event" : "EXTERNAL_TRIGGER" - } ] - }, { "entryPoint" : { "type" : "REST", "name" : "POST /api/orders/submit", @@ -408,7 +367,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 20, - "polymorphicEvents" : null + "polymorphicEvents" : [ "SUBMIT_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -439,7 +398,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 25, - "polymorphicEvents" : null + "polymorphicEvents" : [ "CANCEL_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -536,7 +495,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 18, - "polymorphicEvents" : null + "polymorphicEvents" : [ "REACTIVE_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -597,7 +556,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -624,7 +583,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -651,7 +610,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -678,7 +637,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -705,7 +664,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -732,7 +691,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -759,7 +718,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -786,7 +745,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -813,7 +772,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -840,7 +799,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -867,7 +826,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -894,7 +853,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -921,7 +880,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -948,7 +907,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -975,7 +934,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1002,7 +961,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1029,7 +988,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1056,7 +1015,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1083,7 +1042,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1110,7 +1069,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1137,7 +1096,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1164,7 +1123,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1191,7 +1150,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1218,7 +1177,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1245,7 +1204,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1272,7 +1231,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1299,7 +1258,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1326,7 +1285,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1353,7 +1312,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1380,7 +1339,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1407,7 +1366,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1434,7 +1393,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1461,7 +1420,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1488,7 +1447,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1515,7 +1474,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null diff --git a/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig_PROD/ExtendedStateMachineConfig.json b/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig_PROD/ExtendedStateMachineConfig.json index 3cfe25a..cc50103 100644 --- a/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig_PROD/ExtendedStateMachineConfig.json +++ b/state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig_PROD/ExtendedStateMachineConfig.json @@ -50,16 +50,6 @@ "sourceState" : null, "lineNumber" : 19, "polymorphicEvents" : null - }, { - "event" : "EXTERNAL_TRIGGER", - "className" : "click.kamil.examples.statemachine.extended.service.OrderService", - "methodName" : "processSubmit", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java", - "sourceModule" : null, - "stateMachineId" : null, - "sourceState" : null, - "lineNumber" : 19, - "polymorphicEvents" : null }, { "event" : "SUBMIT_EVENT", "className" : "click.kamil.examples.statemachine.extended.service.OrderService", @@ -355,37 +345,6 @@ } ] } ], "callChains" : [ { - "entryPoint" : { - "type" : "REST", - "name" : "POST /api/orders/submit", - "className" : "click.kamil.examples.statemachine.extended.web.OrderController", - "methodName" : "submitOrder", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/OrderController.java", - "metadata" : { - "path" : "/api/orders/submit", - "verb" : "POST" - }, - "parameters" : [ ] - }, - "methodChain" : [ "click.kamil.examples.statemachine.extended.web.OrderController.submitOrder", "click.kamil.examples.statemachine.extended.service.OrderService.processSubmit" ], - "triggerPoint" : { - "event" : "EXTERNAL_TRIGGER", - "className" : "click.kamil.examples.statemachine.extended.service.OrderService", - "methodName" : "processSubmit", - "sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java", - "sourceModule" : null, - "stateMachineId" : null, - "sourceState" : null, - "lineNumber" : 19, - "polymorphicEvents" : null - }, - "contextMachineId" : null, - "matchedTransitions" : [ { - "sourceState" : "START", - "targetState" : "PROCESSING", - "event" : "EXTERNAL_TRIGGER" - } ] - }, { "entryPoint" : { "type" : "REST", "name" : "POST /api/orders/submit", @@ -408,7 +367,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 20, - "polymorphicEvents" : null + "polymorphicEvents" : [ "SUBMIT_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -439,7 +398,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 25, - "polymorphicEvents" : null + "polymorphicEvents" : [ "CANCEL_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -536,7 +495,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 18, - "polymorphicEvents" : null + "polymorphicEvents" : [ "REACTIVE_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { @@ -597,7 +556,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -624,7 +583,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -651,7 +610,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -678,7 +637,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -705,7 +664,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -732,7 +691,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -759,7 +718,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -786,7 +745,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -813,7 +772,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -840,7 +799,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -867,7 +826,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -894,7 +853,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -921,7 +880,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -948,7 +907,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -975,7 +934,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1002,7 +961,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1029,7 +988,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1056,7 +1015,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1083,7 +1042,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1110,7 +1069,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1137,7 +1096,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1164,7 +1123,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1191,7 +1150,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1218,7 +1177,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1245,7 +1204,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1272,7 +1231,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1299,7 +1258,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1326,7 +1285,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1353,7 +1312,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1380,7 +1339,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1407,7 +1366,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "FALLBACK_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1434,7 +1393,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PROFILED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1461,7 +1420,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 19, - "polymorphicEvents" : null + "polymorphicEvents" : [ "PRIMARY_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1488,7 +1447,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "QUALIFIER_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null @@ -1515,7 +1474,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 17, - "polymorphicEvents" : null + "polymorphicEvents" : [ "NAMED_EVENT" ] }, "contextMachineId" : null, "matchedTransitions" : null diff --git a/state_machine_exporter/src/test/resources/golden/InheritanceStateMachineConfig/InheritanceStateMachineConfig.json b/state_machine_exporter/src/test/resources/golden/InheritanceStateMachineConfig/InheritanceStateMachineConfig.json index f6e7641..87dc5d2 100644 --- a/state_machine_exporter/src/test/resources/golden/InheritanceStateMachineConfig/InheritanceStateMachineConfig.json +++ b/state_machine_exporter/src/test/resources/golden/InheritanceStateMachineConfig/InheritanceStateMachineConfig.json @@ -57,7 +57,7 @@ "stateMachineId" : null, "sourceState" : null, "lineNumber" : 15, - "polymorphicEvents" : null + "polymorphicEvents" : [ "INHERITED_SUBMIT" ] }, "contextMachineId" : null, "matchedTransitions" : [ { diff --git a/state_machines/enterprise_order_system/src/main/resources/hints.json b/state_machines/enterprise_order_system/src/main/resources/hints.json deleted file mode 100644 index eca7c67..0000000 --- a/state_machines/enterprise_order_system/src/main/resources/hints.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "methodFqn": "ExternalTaxService.calculateTax", - "event": "FINALIZE" - } -] diff --git a/state_machines/extended_analysis_sample/src/main/resources/hints.json b/state_machines/extended_analysis_sample/src/main/resources/hints.json deleted file mode 100644 index d51eadb..0000000 --- a/state_machines/extended_analysis_sample/src/main/resources/hints.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "methodFqn": "ExternalNotificationService.sendExternalNotification", - "event": "EXTERNAL_TRIGGER" - } -]