stage 2 update loop
This commit is contained in:
@@ -76,13 +76,16 @@ public class CfgBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node instanceof DoStatement doStmt) {
|
if (node instanceof DoStatement doStmt) {
|
||||||
List<ControlFlowGraph.CfgNode> bodyExits = buildFlow(doStmt.getBody(), sources, cfg);
|
|
||||||
ControlFlowGraph.CfgNode condNode = new ControlFlowGraph.CfgNode(doStmt.getExpression(), ControlFlowGraph.CfgNodeType.STATEMENT);
|
ControlFlowGraph.CfgNode condNode = new ControlFlowGraph.CfgNode(doStmt.getExpression(), ControlFlowGraph.CfgNodeType.STATEMENT);
|
||||||
cfg.addNode(condNode);
|
cfg.addNode(condNode);
|
||||||
|
|
||||||
|
List<ControlFlowGraph.CfgNode> bodySources = new ArrayList<>(sources);
|
||||||
|
bodySources.add(condNode);
|
||||||
|
|
||||||
|
List<ControlFlowGraph.CfgNode> bodyExits = buildFlow(doStmt.getBody(), bodySources, cfg);
|
||||||
for (ControlFlowGraph.CfgNode exit : bodyExits) {
|
for (ControlFlowGraph.CfgNode exit : bodyExits) {
|
||||||
exit.addSuccessor(condNode);
|
exit.addSuccessor(condNode);
|
||||||
}
|
}
|
||||||
condNode.addSuccessor(condNode); // Loop back
|
|
||||||
return List.of(condNode);
|
return List.of(condNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,57 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Expression> getReachingDefinitions(Expression expr) {
|
public List<Expression> getReachingDefinitions(Expression expr) {
|
||||||
return getReachingDefinitions(expr, new HashSet<>());
|
return getReachingDefinitions(expr, new HashSet<>(), new HashMap<>(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Expression> getReachingDefinitions(Expression expr, Set<ASTNode> visited) {
|
private List<Expression> getReachingDefinitions(
|
||||||
if (expr == null || !visited.add(expr)) return List.of();
|
Expression expr,
|
||||||
|
Set<ASTNode> visited,
|
||||||
|
Map<IVariableBinding, Expression> paramBindings,
|
||||||
|
int depth) {
|
||||||
|
if (expr == null || depth > 50 || !visited.add(expr)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Handle Ternary / Conditional Expression
|
||||||
|
if (expr instanceof ConditionalExpression ce) {
|
||||||
|
String condVal = resolveValue(ce.getExpression(), context);
|
||||||
|
if ("true".equals(condVal)) {
|
||||||
|
List<Expression> resolved = getReachingDefinitions(ce.getThenExpression(), visited, paramBindings, depth + 1);
|
||||||
|
visited.remove(expr);
|
||||||
|
return resolved;
|
||||||
|
} else if ("false".equals(condVal)) {
|
||||||
|
List<Expression> resolved = getReachingDefinitions(ce.getElseExpression(), visited, paramBindings, depth + 1);
|
||||||
|
visited.remove(expr);
|
||||||
|
return resolved;
|
||||||
|
} else {
|
||||||
|
visited.remove(expr);
|
||||||
|
return List.of(ce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Handle Field constant / field initializer propagation
|
||||||
|
IVariableBinding vb = getVariableBinding(expr);
|
||||||
|
if (vb != null && vb.isField()) {
|
||||||
|
Expression fieldInit = findFieldInitializer(vb, expr);
|
||||||
|
if (fieldInit != null) {
|
||||||
|
List<Expression> resolved = getReachingDefinitions(fieldInit, visited, paramBindings, depth + 1);
|
||||||
|
visited.remove(expr);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Handle Parameter mapping for SimpleName
|
||||||
if (expr instanceof SimpleName sn) {
|
if (expr instanceof SimpleName sn) {
|
||||||
|
IBinding b = sn.resolveBinding();
|
||||||
|
if (b instanceof IVariableBinding paramVb && paramBindings.containsKey(paramVb)) {
|
||||||
|
Expression argExpr = paramBindings.get(paramVb);
|
||||||
|
List<Expression> resolved = getReachingDefinitions(argExpr, visited, paramBindings, depth + 1);
|
||||||
|
visited.remove(expr);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Local Reaching Definitions
|
||||||
MethodDeclaration md = findEnclosingMethod(sn);
|
MethodDeclaration md = findEnclosingMethod(sn);
|
||||||
ReachingDefinitions rd = getReachingDefinitionsForMethod(md);
|
ReachingDefinitions rd = getReachingDefinitionsForMethod(md);
|
||||||
if (rd != null) {
|
if (rd != null) {
|
||||||
@@ -49,7 +93,7 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
for (ASTNode def : defs) {
|
for (ASTNode def : defs) {
|
||||||
Expression defExpr = ReachingDefinitions.getDefinitionExpression(def);
|
Expression defExpr = ReachingDefinitions.getDefinitionExpression(def);
|
||||||
if (defExpr != null) {
|
if (defExpr != null) {
|
||||||
exprs.addAll(getReachingDefinitions(defExpr, visited));
|
exprs.addAll(getReachingDefinitions(defExpr, visited, paramBindings, depth + 1));
|
||||||
} else {
|
} else {
|
||||||
exprs.add(expr);
|
exprs.add(expr);
|
||||||
}
|
}
|
||||||
@@ -59,11 +103,138 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Handle Method Invocations (Static Factory methods / Transforming Getters)
|
||||||
|
if (expr instanceof MethodInvocation mi) {
|
||||||
|
IMethodBinding mb = mi.resolveMethodBinding();
|
||||||
|
if (mb != null) {
|
||||||
|
MethodDeclaration md = findMethodDeclaration(mb);
|
||||||
|
if (md != null && md.getBody() != null) {
|
||||||
|
Map<IVariableBinding, Expression> newParamBindings = new HashMap<>(paramBindings);
|
||||||
|
List<?> parameters = md.parameters();
|
||||||
|
List<?> arguments = mi.arguments();
|
||||||
|
int count = Math.min(parameters.size(), arguments.size());
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) parameters.get(i);
|
||||||
|
IVariableBinding paramVb = svd.resolveBinding();
|
||||||
|
if (paramVb != null) {
|
||||||
|
newParamBindings.put(paramVb, (Expression) arguments.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReturnStatement> returns = findReturnStatements(md.getBody());
|
||||||
|
if (!returns.isEmpty()) {
|
||||||
|
List<Expression> results = new ArrayList<>();
|
||||||
|
for (ReturnStatement rs : returns) {
|
||||||
|
if (rs.getExpression() != null) {
|
||||||
|
results.addAll(getReachingDefinitions(rs.getExpression(), visited, newParamBindings, depth + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited.remove(expr);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
visited.remove(expr);
|
visited.remove(expr);
|
||||||
return List.of(expr);
|
return List.of(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IVariableBinding getVariableBinding(Expression expr) {
|
||||||
|
if (expr instanceof SimpleName sn) {
|
||||||
|
IBinding b = sn.resolveBinding();
|
||||||
|
if (b instanceof IVariableBinding varBinding) return varBinding;
|
||||||
|
} else if (expr instanceof QualifiedName qn) {
|
||||||
|
IBinding b = qn.resolveBinding();
|
||||||
|
if (b instanceof IVariableBinding varBinding) return varBinding;
|
||||||
|
} else if (expr instanceof FieldAccess fa) {
|
||||||
|
return fa.resolveFieldBinding();
|
||||||
|
} else if (expr instanceof SuperFieldAccess sfa) {
|
||||||
|
return sfa.resolveFieldBinding();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression findFieldInitializer(IVariableBinding vb, ASTNode contextNode) {
|
||||||
|
if (vb == null) return null;
|
||||||
|
ITypeBinding declaringClass = vb.getDeclaringClass();
|
||||||
|
if (declaringClass == null) return null;
|
||||||
|
String classFqn = declaringClass.getErasure().getQualifiedName();
|
||||||
|
if (classFqn == null || classFqn.isEmpty()) return null;
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(classFqn);
|
||||||
|
if (td == null) {
|
||||||
|
CompilationUnit cu = null;
|
||||||
|
ASTNode root = contextNode.getRoot();
|
||||||
|
if (root instanceof CompilationUnit) {
|
||||||
|
cu = (CompilationUnit) root;
|
||||||
|
}
|
||||||
|
td = context.getTypeDeclaration(classFqn, cu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (td != null) {
|
||||||
|
for (FieldDeclaration fd : td.getFields()) {
|
||||||
|
for (Object fragObj : fd.fragments()) {
|
||||||
|
if (fragObj instanceof VariableDeclarationFragment fragment) {
|
||||||
|
if (fragment.getName().getIdentifier().equals(vb.getName())) {
|
||||||
|
return fragment.getInitializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDeclaration findMethodDeclaration(IMethodBinding mb) {
|
||||||
|
if (mb == null) return null;
|
||||||
|
ITypeBinding declaringClass = mb.getDeclaringClass();
|
||||||
|
if (declaringClass == null) return null;
|
||||||
|
String classFqn = declaringClass.getErasure().getQualifiedName();
|
||||||
|
if (classFqn == null || classFqn.isEmpty()) return null;
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(classFqn);
|
||||||
|
if (td != null) {
|
||||||
|
for (MethodDeclaration md : td.getMethods()) {
|
||||||
|
IMethodBinding candidateMb = md.resolveBinding();
|
||||||
|
if (candidateMb != null && candidateMb.getKey().equals(mb.getKey())) {
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (MethodDeclaration md : td.getMethods()) {
|
||||||
|
if (md.getName().getIdentifier().equals(mb.getName()) && md.parameters().size() == mb.getParameterTypes().length) {
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ReturnStatement> findReturnStatements(ASTNode node) {
|
||||||
|
List<ReturnStatement> returns = new ArrayList<>();
|
||||||
|
node.accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(ReturnStatement rs) {
|
||||||
|
returns.add(rs);
|
||||||
|
return super.visit(rs);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(LambdaExpression le) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(AnonymousClassDeclaration acd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(TypeDeclarationStatement tds) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return returns;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resolveValue(Expression expr, CodebaseContext context) {
|
public String resolveValue(Expression expr, CodebaseContext context) {
|
||||||
if (expr == null) return null;
|
if (expr == null) return null;
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ public class ReachingDefinitions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean changed = true;
|
boolean changed = true;
|
||||||
while (changed) {
|
int iterations = 0;
|
||||||
|
int maxIterations = 5000;
|
||||||
|
while (changed && iterations < maxIterations) {
|
||||||
|
iterations++;
|
||||||
changed = false;
|
changed = false;
|
||||||
for (ControlFlowGraph.CfgNode node : cfgNodes) {
|
for (ControlFlowGraph.CfgNode node : cfgNodes) {
|
||||||
Set<ASTNode> newIn = new HashSet<>();
|
Set<ASTNode> newIn = new HashSet<>();
|
||||||
@@ -67,6 +70,10 @@ public class ReachingDefinitions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (iterations >= maxIterations) {
|
||||||
|
// Log warning or print to stderr
|
||||||
|
System.err.println("Warning: ReachingDefinitions analysis reached max iterations (" + maxIterations + ") and terminated early.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ASTNode> getReachingDefinitions(ASTNode usageNode, String varName) {
|
public Set<ASTNode> getReachingDefinitions(ASTNode usageNode, String varName) {
|
||||||
|
|||||||
@@ -88,4 +88,166 @@ class JdtDataFlowModelTest {
|
|||||||
// Both branches assign "SAME", so it converges to "SAME"
|
// Both branches assign "SAME", so it converges to "SAME"
|
||||||
assertThat(val).isEqualTo("SAME");
|
assertThat(val).isEqualTo("SAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFieldConstantPropagation(@TempDir Path tempDir) throws IOException {
|
||||||
|
Path comFoo = tempDir.resolve("com/foo");
|
||||||
|
Files.createDirectories(comFoo);
|
||||||
|
Files.writeString(comFoo.resolve("Test.java"),
|
||||||
|
"package com.foo;\n" +
|
||||||
|
"public class Test {\n" +
|
||||||
|
" private static final String MY_CONST = \"CONST_VAL\";\n" +
|
||||||
|
" public void run() {\n" +
|
||||||
|
" String a = MY_CONST;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||||
|
assertThat(td).isNotNull();
|
||||||
|
|
||||||
|
MethodDeclaration md = td.getMethods()[0];
|
||||||
|
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) md.getBody().statements().get(0);
|
||||||
|
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||||
|
Expression initializer = aFrag.getInitializer();
|
||||||
|
|
||||||
|
DataFlowModel model = new JdtDataFlowModel(context);
|
||||||
|
String val = model.resolveValue(initializer, context);
|
||||||
|
assertThat(val).isEqualTo("CONST_VAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTernaryExpressionResolution(@TempDir Path tempDir) throws IOException {
|
||||||
|
Path comFoo = tempDir.resolve("com/foo");
|
||||||
|
Files.createDirectories(comFoo);
|
||||||
|
Files.writeString(comFoo.resolve("Test.java"),
|
||||||
|
"package com.foo;\n" +
|
||||||
|
"public class Test {\n" +
|
||||||
|
" public void run(boolean flag) {\n" +
|
||||||
|
" String a = flag ? \"TRUE_VAL\" : \"FALSE_VAL\";\n" +
|
||||||
|
" String b = true ? \"TRUE_VAL\" : \"FALSE_VAL\";\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||||
|
assertThat(td).isNotNull();
|
||||||
|
|
||||||
|
MethodDeclaration md = td.getMethods()[0];
|
||||||
|
|
||||||
|
// 1. Unresolvable condition (flag)
|
||||||
|
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) md.getBody().statements().get(0);
|
||||||
|
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||||
|
Expression initA = aFrag.getInitializer();
|
||||||
|
|
||||||
|
DataFlowModel model = new JdtDataFlowModel(context);
|
||||||
|
List<Expression> defsA = model.getReachingDefinitions(initA);
|
||||||
|
assertThat(defsA).hasSize(1);
|
||||||
|
assertThat(defsA.get(0)).isInstanceOf(ConditionalExpression.class);
|
||||||
|
|
||||||
|
// 2. Statically resolvable condition (true)
|
||||||
|
VariableDeclarationStatement bStmt = (VariableDeclarationStatement) md.getBody().statements().get(1);
|
||||||
|
VariableDeclarationFragment bFrag = (VariableDeclarationFragment) bStmt.fragments().get(0);
|
||||||
|
Expression initB = bFrag.getInitializer();
|
||||||
|
|
||||||
|
List<Expression> defsB = model.getReachingDefinitions(initB);
|
||||||
|
assertThat(defsB).hasSize(1);
|
||||||
|
assertThat(defsB.get(0)).isInstanceOf(StringLiteral.class);
|
||||||
|
assertThat(((StringLiteral) defsB.get(0)).getLiteralValue()).isEqualTo("TRUE_VAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStaticFactoryMethodTracking(@TempDir Path tempDir) throws IOException {
|
||||||
|
Path comFoo = tempDir.resolve("com/foo");
|
||||||
|
Files.createDirectories(comFoo);
|
||||||
|
Files.writeString(comFoo.resolve("Test.java"),
|
||||||
|
"package com.foo;\n" +
|
||||||
|
"public class Test {\n" +
|
||||||
|
" public static String create(String value) {\n" +
|
||||||
|
" return value;\n" +
|
||||||
|
" }\n" +
|
||||||
|
" public void run() {\n" +
|
||||||
|
" String a = create(\"FACTORY_VAL\");\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||||
|
assertThat(td).isNotNull();
|
||||||
|
|
||||||
|
MethodDeclaration runMd = null;
|
||||||
|
for (MethodDeclaration md : td.getMethods()) {
|
||||||
|
if (md.getName().getIdentifier().equals("run")) {
|
||||||
|
runMd = md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(runMd).isNotNull();
|
||||||
|
|
||||||
|
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) runMd.getBody().statements().get(0);
|
||||||
|
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||||
|
Expression initializer = aFrag.getInitializer();
|
||||||
|
|
||||||
|
DataFlowModel model = new JdtDataFlowModel(context);
|
||||||
|
String val = model.resolveValue(initializer, context);
|
||||||
|
assertThat(val).isEqualTo("FACTORY_VAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoWhileLoopFlow(@TempDir Path tempDir) throws IOException {
|
||||||
|
Path comFoo = tempDir.resolve("com/foo");
|
||||||
|
Files.createDirectories(comFoo);
|
||||||
|
Files.writeString(comFoo.resolve("Test.java"),
|
||||||
|
"package com.foo;\n" +
|
||||||
|
"public class Test {\n" +
|
||||||
|
" public void run(boolean flag) {\n" +
|
||||||
|
" int a = 1;\n" +
|
||||||
|
" do {\n" +
|
||||||
|
" a = 2;\n" +
|
||||||
|
" } while (flag);\n" +
|
||||||
|
" int b = a;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||||
|
assertThat(td).isNotNull();
|
||||||
|
|
||||||
|
MethodDeclaration md = td.getMethods()[0];
|
||||||
|
ControlFlowGraph cfg = CfgBuilder.build(md);
|
||||||
|
|
||||||
|
// Find the condition node (flag)
|
||||||
|
ControlFlowGraph.CfgNode condNode = null;
|
||||||
|
for (ControlFlowGraph.CfgNode node : cfg.getNodes()) {
|
||||||
|
if (node.getAstNode() instanceof SimpleName sn && sn.getIdentifier().equals("flag")) {
|
||||||
|
condNode = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(condNode).isNotNull();
|
||||||
|
|
||||||
|
// The condition node should loop back to the statement inside the body (a = 2;)
|
||||||
|
boolean hasLoopBackToBody = false;
|
||||||
|
for (ControlFlowGraph.CfgNode succ : condNode.getSuccessors()) {
|
||||||
|
if (succ.getAstNode() instanceof ExpressionStatement es) {
|
||||||
|
if (es.getExpression() instanceof Assignment assign) {
|
||||||
|
if (assign.getLeftHandSide() instanceof SimpleName sn && sn.getIdentifier().equals("a")) {
|
||||||
|
hasLoopBackToBody = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(hasLoopBackToBody).isTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -439,7 +439,7 @@
|
|||||||
},
|
},
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderStatic", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderStatic", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||||
"triggerPoint" : {
|
"triggerPoint" : {
|
||||||
"event" : "EventBuilder.buildEvent()",
|
"event" : "new FulfillEvent()",
|
||||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||||
"methodName" : "processEvent",
|
"methodName" : "processEvent",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||||
@@ -447,10 +447,14 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 13,
|
"lineNumber" : 13,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ "OrderEvents.FULFILL" ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : [ {
|
||||||
|
"sourceState" : "OrderStates.PAID",
|
||||||
|
"targetState" : "OrderStates.FULFILLED",
|
||||||
|
"event" : "OrderEvents.FULFILL"
|
||||||
|
} ]
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -466,7 +470,7 @@
|
|||||||
},
|
},
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderInstance", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderInstance", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||||
"triggerPoint" : {
|
"triggerPoint" : {
|
||||||
"event" : "new EventBuilder().buildInstanceEvent()",
|
"event" : "new CancelEvent()",
|
||||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||||
"methodName" : "processEvent",
|
"methodName" : "processEvent",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||||
@@ -474,10 +478,14 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 13,
|
"lineNumber" : 13,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ "OrderEvents.CANCEL" ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : [ {
|
||||||
|
"sourceState" : "OrderStates.SUBMITTED",
|
||||||
|
"targetState" : "OrderStates.CANCELED",
|
||||||
|
"event" : "OrderEvents.CANCEL"
|
||||||
|
} ]
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
|
|||||||
Reference in New Issue
Block a user