call graph fix + tests
This commit is contained in:
@@ -57,7 +57,20 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
// rather than completely hiding the endpoint.
|
// rather than completely hiding the endpoint.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWildcard || smEvent.equals(triggerEvent) || triggerEvent.toLowerCase().contains(smEvent.toLowerCase()) || smEvent.toLowerCase().contains(triggerEvent.toLowerCase())) {
|
List<String> polyEvents = tp.getPolymorphicEvents() != null ? tp.getPolymorphicEvents() : java.util.Collections.emptyList();
|
||||||
|
boolean hasPolyMatch = false;
|
||||||
|
for (String pe : polyEvents) {
|
||||||
|
String simplePe = pe;
|
||||||
|
if (pe.contains(".")) {
|
||||||
|
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
||||||
|
hasPolyMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPolyMatch || (polyEvents.isEmpty() && (isWildcard || smEvent.equals(triggerEvent) || triggerEvent.toLowerCase().contains(smEvent.toLowerCase()) || smEvent.toLowerCase().contains(triggerEvent.toLowerCase())))) {
|
||||||
// Event matches or is a wildcard
|
// Event matches or is a wildcard
|
||||||
for (State smSourceState : t.getSourceStates()) {
|
for (State smSourceState : t.getSourceStates()) {
|
||||||
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ public class TriggerPoint {
|
|||||||
private final String stateMachineId; // Optional: to link to a specific SM instance
|
private final String stateMachineId; // Optional: to link to a specific SM instance
|
||||||
private final String sourceState; // Optional: if we can determine the expected current state
|
private final String sourceState; // Optional: if we can determine the expected current state
|
||||||
private final int lineNumber;
|
private final int lineNumber;
|
||||||
|
private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ public class ConstantResolver {
|
|||||||
return resolveInfix(infix, context, visited);
|
return resolveInfix(infix, context, visited);
|
||||||
}
|
}
|
||||||
if (expr instanceof QualifiedName qn) {
|
if (expr instanceof QualifiedName qn) {
|
||||||
return resolveManual(qn, context, visited);
|
String val = resolveManual(qn, context, visited);
|
||||||
|
return val != null ? val : qn.toString();
|
||||||
}
|
}
|
||||||
if (expr instanceof SimpleName sn) {
|
if (expr instanceof SimpleName sn) {
|
||||||
return resolveManual(sn, context, visited);
|
return resolveManual(sn, context, visited);
|
||||||
|
|||||||
@@ -88,8 +88,14 @@ public class CallGraphBuilder {
|
|||||||
// Not a parameter. Maybe it's a local variable initialized from a parameter or method call?
|
// Not a parameter. Maybe it's a local variable initialized from a parameter or method call?
|
||||||
String tracedVar = traceLocalVariable(target, currentParamName);
|
String tracedVar = traceLocalVariable(target, currentParamName);
|
||||||
if (tracedVar != null && !tracedVar.equals(currentParamName)) {
|
if (tracedVar != null && !tracedVar.equals(currentParamName)) {
|
||||||
|
// Extract method calls like .getType() from the traced variable
|
||||||
|
int dotIdx = tracedVar.indexOf('.');
|
||||||
|
if (dotIdx > 0 && dotIdx + 1 < tracedVar.length() && Character.isLowerCase(tracedVar.charAt(dotIdx + 1))) {
|
||||||
|
methodSuffix = tracedVar.substring(dotIdx) + methodSuffix;
|
||||||
|
tracedVar = tracedVar.substring(0, dotIdx);
|
||||||
|
}
|
||||||
currentParamName = tracedVar;
|
currentParamName = tracedVar;
|
||||||
// We must try to find paramIndex again, because the traced variable might be a parameter of this same method
|
resolvedValue = tracedVar + methodSuffix;
|
||||||
paramIndex = getParameterIndex(target, currentParamName);
|
paramIndex = getParameterIndex(target, currentParamName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,6 +112,12 @@ public class CallGraphBuilder {
|
|||||||
if (paramIndex < edge.getArguments().size()) {
|
if (paramIndex < edge.getArguments().size()) {
|
||||||
String arg = edge.getArguments().get(paramIndex);
|
String arg = edge.getArguments().get(paramIndex);
|
||||||
if (arg != null) {
|
if (arg != null) {
|
||||||
|
// If the argument passed has a method call, extract it
|
||||||
|
int dotIdx = arg.indexOf('.');
|
||||||
|
if (dotIdx > 0 && dotIdx + 1 < arg.length() && Character.isLowerCase(arg.charAt(dotIdx + 1))) {
|
||||||
|
methodSuffix = arg.substring(dotIdx) + methodSuffix;
|
||||||
|
arg = arg.substring(0, dotIdx);
|
||||||
|
}
|
||||||
currentParamName = arg;
|
currentParamName = arg;
|
||||||
resolvedValue = arg + methodSuffix;
|
resolvedValue = arg + methodSuffix;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -118,13 +130,73 @@ public class CallGraphBuilder {
|
|||||||
if (!found) break; // Could not map argument
|
if (!found) break; // Could not map argument
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resolvedValue.equals(event)) {
|
// Final check on the entry method
|
||||||
|
String entryMethod = path.get(0);
|
||||||
|
int entryParamIndex = getParameterIndex(entryMethod, currentParamName);
|
||||||
|
if (entryParamIndex < 0) {
|
||||||
|
String tracedVar = traceLocalVariable(entryMethod, currentParamName);
|
||||||
|
if (tracedVar != null && !tracedVar.equals(currentParamName)) {
|
||||||
|
int dotIdx = tracedVar.indexOf('.');
|
||||||
|
if (dotIdx > 0 && dotIdx + 1 < tracedVar.length() && Character.isLowerCase(tracedVar.charAt(dotIdx + 1))) {
|
||||||
|
methodSuffix = tracedVar.substring(dotIdx) + methodSuffix;
|
||||||
|
tracedVar = tracedVar.substring(0, dotIdx);
|
||||||
|
}
|
||||||
|
currentParamName = tracedVar;
|
||||||
|
resolvedValue = tracedVar + methodSuffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> polymorphicEvents = new ArrayList<>();
|
||||||
|
if (resolvedValue.matches(".*\\.get[A-Z].*\\(\\)")) {
|
||||||
|
String varName = resolvedValue.substring(0, resolvedValue.indexOf('.'));
|
||||||
|
String methodName = resolvedValue.substring(resolvedValue.indexOf('.') + 1, resolvedValue.indexOf('('));
|
||||||
|
|
||||||
|
// Resolve in the first method in the path where the variable might be declared
|
||||||
|
for (String methodFqn : path) {
|
||||||
|
String declaredType = getVariableDeclaredType(methodFqn, varName);
|
||||||
|
if (declaredType != null) {
|
||||||
|
System.out.println("DEEP TRACE: " + methodFqn + " " + varName + " -> " + declaredType);
|
||||||
|
List<String> typesToInspect = new ArrayList<>();
|
||||||
|
typesToInspect.add(declaredType);
|
||||||
|
typesToInspect.addAll(context.getImplementations(declaredType));
|
||||||
|
System.out.println("DEEP TRACE IMPLS: " + typesToInspect);
|
||||||
|
|
||||||
|
for (String type : typesToInspect) {
|
||||||
|
Set<String> visited = new HashSet<>();
|
||||||
|
// We must find the compilation unit to pass for simple names!
|
||||||
|
// Let's pass the first available CU for this class
|
||||||
|
TypeDeclaration baseTd = context.getTypeDeclaration(type);
|
||||||
|
CompilationUnit cuToUse = null;
|
||||||
|
if (baseTd != null && baseTd.getRoot() instanceof CompilationUnit) {
|
||||||
|
cuToUse = (CompilationUnit) baseTd.getRoot();
|
||||||
|
} else {
|
||||||
|
String entryClassName = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
|
TypeDeclaration entryTd = context.getTypeDeclaration(entryClassName);
|
||||||
|
if (entryTd != null && entryTd.getRoot() instanceof CompilationUnit) {
|
||||||
|
cuToUse = (CompilationUnit) entryTd.getRoot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<String> constants = resolveMethodReturnConstant(type, methodName, 0, visited, cuToUse);
|
||||||
|
System.out.println("DEEP TRACE RETURN: " + type + " -> " + constants);
|
||||||
|
for (String constant : constants) {
|
||||||
|
if (!polymorphicEvents.contains(constant)) {
|
||||||
|
polymorphicEvents.add(constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolvedValue.equals(event) || !polymorphicEvents.isEmpty()) {
|
||||||
return TriggerPoint.builder()
|
return TriggerPoint.builder()
|
||||||
.event(resolvedValue)
|
.event(resolvedValue)
|
||||||
.className(tp.getClassName())
|
.className(tp.getClassName())
|
||||||
.methodName(tp.getMethodName())
|
.methodName(tp.getMethodName())
|
||||||
.sourceFile(tp.getSourceFile())
|
.sourceFile(tp.getSourceFile())
|
||||||
.lineNumber(tp.getLineNumber())
|
.lineNumber(tp.getLineNumber())
|
||||||
|
.polymorphicEvents(polymorphicEvents)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +222,108 @@ public class CallGraphBuilder {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getVariableDeclaredType(String methodFqn, String varName) {
|
||||||
|
if (methodFqn == null || !methodFqn.contains(".")) 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) {
|
||||||
|
for (Object pObj : md.parameters()) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) pObj;
|
||||||
|
if (svd.getName().getIdentifier().equals(varName)) {
|
||||||
|
return svd.getType().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String[] foundType = new String[1];
|
||||||
|
if (md.getBody() != null) {
|
||||||
|
md.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(VariableDeclarationStatement node) {
|
||||||
|
for (Object fragObj : node.fragments()) {
|
||||||
|
VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
|
||||||
|
if (frag.getName().getIdentifier().equals(varName)) {
|
||||||
|
foundType[0] = node.getType().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return foundType[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveMethodReturnConstant(String className, String methodName, int depth, Set<String> visited, CompilationUnit contextCu) {
|
||||||
|
if (depth > 20) return Collections.emptyList();
|
||||||
|
String fqn = className + "." + methodName;
|
||||||
|
if (!visited.add(fqn)) return Collections.emptyList();
|
||||||
|
|
||||||
|
List<String> constants = new ArrayList<>();
|
||||||
|
TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(className, contextCu) : context.getTypeDeclaration(className);
|
||||||
|
if (td == null) {
|
||||||
|
System.out.println("DEEP TRACE FAILED TO FIND TD: " + className);
|
||||||
|
}
|
||||||
|
if (td != null) {
|
||||||
|
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
|
||||||
|
if (md != null && md.getBody() != null) {
|
||||||
|
md.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(ReturnStatement node) {
|
||||||
|
Expression retExpr = node.getExpression();
|
||||||
|
if (retExpr != null) {
|
||||||
|
boolean handled = false;
|
||||||
|
if (retExpr instanceof MethodInvocation mi) {
|
||||||
|
// Follow delegation first
|
||||||
|
String called = resolveCalledMethod(mi);
|
||||||
|
System.out.println("DEEP TRACE RESOLVED CALLED: " + called);
|
||||||
|
if (called != null && called.contains(".")) {
|
||||||
|
if (visited.contains(called)) {
|
||||||
|
handled = true;
|
||||||
|
} else {
|
||||||
|
String cName = called.substring(0, called.lastIndexOf('.'));
|
||||||
|
String mName = called.substring(called.lastIndexOf('.') + 1);
|
||||||
|
|
||||||
|
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
||||||
|
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
||||||
|
|
||||||
|
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
||||||
|
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
||||||
|
constants.addAll(delegationResult);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handled) {
|
||||||
|
String val = constantResolver.resolve(retExpr, context);
|
||||||
|
if (val != null) {
|
||||||
|
if (val.startsWith("ENUM_SET:")) {
|
||||||
|
for (String eVal : val.substring(9).split(",")) {
|
||||||
|
constants.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
constants.add(val);
|
||||||
|
}
|
||||||
|
} else if (retExpr instanceof QualifiedName qn) {
|
||||||
|
constants.add(qn.toString());
|
||||||
|
} else if (retExpr instanceof SimpleName sn) {
|
||||||
|
constants.add(sn.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited.remove(fqn);
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
|
||||||
private String traceLocalVariable(String methodFqn, String varName) {
|
private String traceLocalVariable(String methodFqn, String varName) {
|
||||||
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
@@ -179,11 +353,18 @@ public class CallGraphBuilder {
|
|||||||
if (initializer[0] != null) {
|
if (initializer[0] != null) {
|
||||||
Expression expr = traceVariable(initializer[0]);
|
Expression expr = traceVariable(initializer[0]);
|
||||||
if (expr instanceof MethodInvocation mi) {
|
if (expr instanceof MethodInvocation mi) {
|
||||||
// e.g. event.getType() -> return "event.getType()" or just "event"
|
// Unwrapper logic: If wrapper method is called, extract its arguments recursively
|
||||||
if (mi.getExpression() instanceof SimpleName sn) {
|
Expression innerMost = unwrapMethodInvocation(mi, 0);
|
||||||
return sn.getIdentifier() + "." + mi.getName().getIdentifier() + "()";
|
if (innerMost instanceof MethodInvocation innerMi) {
|
||||||
|
if (innerMi.getExpression() instanceof SimpleName sn) {
|
||||||
|
return sn.getIdentifier() + "." + innerMi.getName().getIdentifier() + "()";
|
||||||
|
}
|
||||||
|
return innerMi.getName().getIdentifier() + "()";
|
||||||
}
|
}
|
||||||
return mi.getName().getIdentifier() + "()";
|
if (innerMost instanceof SimpleName sn) {
|
||||||
|
return sn.getIdentifier();
|
||||||
|
}
|
||||||
|
return innerMost.toString();
|
||||||
}
|
}
|
||||||
if (expr instanceof SimpleName sn) {
|
if (expr instanceof SimpleName sn) {
|
||||||
return sn.getIdentifier();
|
return sn.getIdentifier();
|
||||||
@@ -195,6 +376,18 @@ public class CallGraphBuilder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Expression unwrapMethodInvocation(MethodInvocation mi, int depth) {
|
||||||
|
if (depth > 5) return mi;
|
||||||
|
if (!mi.arguments().isEmpty()) {
|
||||||
|
Expression arg = (Expression) mi.arguments().get(0);
|
||||||
|
if (arg instanceof MethodInvocation innerMi) {
|
||||||
|
return unwrapMethodInvocation(innerMi, depth + 1);
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
private String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) {
|
private 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);
|
||||||
@@ -213,53 +406,58 @@ public class CallGraphBuilder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
||||||
|
ASTNode parent = node.getParent();
|
||||||
|
while (parent != null && !(parent instanceof MethodDeclaration)) {
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
return (MethodDeclaration) parent;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, List<CallEdge>> buildCallGraph() {
|
private Map<String, List<CallEdge>> buildCallGraph() {
|
||||||
graph = new HashMap<>();
|
graph = new HashMap<>();
|
||||||
for (CompilationUnit cu : context.getCompilationUnits()) {
|
for (CompilationUnit cu : context.getCompilationUnits()) {
|
||||||
cu.accept(new ASTVisitor() {
|
cu.accept(new ASTVisitor() {
|
||||||
@Override
|
|
||||||
public boolean visit(MethodDeclaration node) {
|
|
||||||
TypeDeclaration td = findEnclosingType(node);
|
|
||||||
if (td != null) {
|
|
||||||
currentMethodFqn = context.getFqn(td) + "." + node.getName().getIdentifier();
|
|
||||||
}
|
|
||||||
return super.visit(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(MethodInvocation node) {
|
public boolean visit(MethodInvocation node) {
|
||||||
if (currentMethodFqn != null) {
|
MethodDeclaration md = findEnclosingMethod(node);
|
||||||
List<String> calledMethods = resolveCalledMethodsPolymorphic(node);
|
if (md != null) {
|
||||||
List<String> args = resolveArguments(node.arguments());
|
TypeDeclaration td = findEnclosingType(md);
|
||||||
for (String calledMethod : calledMethods) {
|
if (td != null) {
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
String currentMethodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
|
||||||
}
|
List<String> calledMethods = resolveCalledMethodsPolymorphic(node);
|
||||||
for (Object argObj : node.arguments()) {
|
List<String> args = resolveArguments(node.arguments());
|
||||||
if (argObj instanceof ExpressionMethodReference emr) {
|
for (String calledMethod : calledMethods) {
|
||||||
String typeName = emr.getExpression().toString();
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
||||||
if ("this".equals(typeName) || "super".equals(typeName)) {
|
}
|
||||||
TypeDeclaration td = findEnclosingType(node);
|
for (Object argObj : node.arguments()) {
|
||||||
if (td != null) {
|
if (argObj instanceof ExpressionMethodReference emr) {
|
||||||
String refMethod = resolveMethodInType(td, emr.getName().getIdentifier());
|
String typeName = emr.getExpression().toString();
|
||||||
if (refMethod != null) {
|
if ("this".equals(typeName) || "super".equals(typeName)) {
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, args));
|
TypeDeclaration td2 = findEnclosingType(node);
|
||||||
|
if (td2 != null) {
|
||||||
|
String refMethod = resolveMethodInType(td2, emr.getName().getIdentifier());
|
||||||
|
if (refMethod != null) {
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
String fallbackTypeFqn = null;
|
||||||
String fallbackTypeFqn = null;
|
ITypeBinding binding = emr.getExpression().resolveTypeBinding();
|
||||||
ITypeBinding binding = emr.getExpression().resolveTypeBinding();
|
if (binding != null) {
|
||||||
if (binding != null) {
|
fallbackTypeFqn = binding.getQualifiedName();
|
||||||
fallbackTypeFqn = binding.getQualifiedName();
|
} else if (emr.getExpression() instanceof SimpleName sn) {
|
||||||
} else if (emr.getExpression() instanceof SimpleName sn) {
|
fallbackTypeFqn = resolveReceiverTypeFallback(sn);
|
||||||
fallbackTypeFqn = resolveReceiverTypeFallback(sn);
|
}
|
||||||
}
|
if (fallbackTypeFqn != null) {
|
||||||
if (fallbackTypeFqn != null) {
|
String calledMethod = fallbackTypeFqn + "." + emr.getName().getIdentifier();
|
||||||
String calledMethod = fallbackTypeFqn + "." + emr.getName().getIdentifier();
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
|
||||||
|
|
||||||
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));
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(impl + "." + emr.getName().getIdentifier(), args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,22 +469,27 @@ public class CallGraphBuilder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(SuperMethodInvocation node) {
|
public boolean visit(SuperMethodInvocation node) {
|
||||||
if (currentMethodFqn != null) {
|
MethodDeclaration md = findEnclosingMethod(node);
|
||||||
String methodName = node.getName().getIdentifier();
|
if (md != null) {
|
||||||
TypeDeclaration td = findEnclosingType(node);
|
TypeDeclaration tdOuter = findEnclosingType(md);
|
||||||
if (td != null) {
|
if (tdOuter != null) {
|
||||||
String superFqn = context.getSuperclassFqn(td);
|
String currentMethodFqn = context.getFqn(tdOuter) + "." + md.getName().getIdentifier();
|
||||||
if (superFqn != null) {
|
String methodName = node.getName().getIdentifier();
|
||||||
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
TypeDeclaration td = findEnclosingType(node);
|
||||||
String calledMethod = null;
|
if (td != null) {
|
||||||
if (superTd != null) {
|
String superFqn = context.getSuperclassFqn(td);
|
||||||
calledMethod = resolveMethodInType(superTd, methodName);
|
if (superFqn != null) {
|
||||||
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
|
String calledMethod = null;
|
||||||
|
if (superTd != null) {
|
||||||
|
calledMethod = resolveMethodInType(superTd, methodName);
|
||||||
|
}
|
||||||
|
if (calledMethod == null) {
|
||||||
|
calledMethod = superFqn + "." + methodName;
|
||||||
|
}
|
||||||
|
List<String> args = resolveArguments(node.arguments());
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
||||||
}
|
}
|
||||||
if (calledMethod == null) {
|
|
||||||
calledMethod = superFqn + "." + methodName;
|
|
||||||
}
|
|
||||||
List<String> args = resolveArguments(node.arguments());
|
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,7 +536,11 @@ public class CallGraphBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract from constructor args (e.g., new CustomMessage(OrderEvent.PROCESS, ...))
|
// Extract from constructor args (e.g., new CustomMessage(OrderEvent.PROCESS, ...))
|
||||||
expr = traceVariable(expr);
|
Expression tracedExpr = traceVariable(expr);
|
||||||
|
if (tracedExpr instanceof QualifiedName || tracedExpr instanceof ClassInstanceCreation || tracedExpr instanceof StringLiteral || tracedExpr instanceof NumberLiteral) {
|
||||||
|
expr = tracedExpr; // Only accept trace if it resolves to a pure constant/primitive/constructor
|
||||||
|
}
|
||||||
|
|
||||||
if (expr instanceof ClassInstanceCreation cic) {
|
if (expr instanceof ClassInstanceCreation cic) {
|
||||||
if (!cic.arguments().isEmpty()) {
|
if (!cic.arguments().isEmpty()) {
|
||||||
expr = (Expression) cic.arguments().get(0);
|
expr = (Expression) cic.arguments().get(0);
|
||||||
@@ -386,6 +593,13 @@ public class CallGraphBuilder {
|
|||||||
return binding.getQualifiedName() + "." + methodName;
|
return binding.getQualifiedName() + "." + methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (receiver instanceof ThisExpression) {
|
||||||
|
TypeDeclaration td = findEnclosingType(node);
|
||||||
|
if (td != null) {
|
||||||
|
return context.getFqn(td) + "." + methodName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (receiver instanceof SimpleName sn) {
|
if (receiver instanceof SimpleName sn) {
|
||||||
String fallbackTypeFqn = resolveReceiverTypeFallback(sn);
|
String fallbackTypeFqn = resolveReceiverTypeFallback(sn);
|
||||||
if (fallbackTypeFqn != null) {
|
if (fallbackTypeFqn != null) {
|
||||||
@@ -558,12 +772,4 @@ public class CallGraphBuilder {
|
|||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
|
||||||
ASTNode parent = node.getParent();
|
|
||||||
while (parent != null && !(parent instanceof MethodDeclaration)) {
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
return (MethodDeclaration) parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,8 +215,743 @@ class CallGraphBuilderTest {
|
|||||||
|
|
||||||
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
|
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
CallChain chain = chains.get(0);
|
||||||
|
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
|
||||||
|
.as("Resolved event was: " + chain.getTriggerPoint().getEvent())
|
||||||
|
.containsExactlyInAnyOrder("OrderEvents.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolvePolymorphicInheritanceAcrossChain() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
doProcessOrderEvent(domainEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doProcessOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
OrderEvents eventType = assertSupportedOrderEvent(domainEvent.getType());
|
||||||
|
service.updateOrderState(eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderEvents assertSupportedOrderEvent(OrderEvents e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {
|
||||||
|
// sendEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RichOrderEvent {
|
||||||
|
OrderEvents getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CancelOrderEvent implements RichOrderEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.CANCELLED; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceiveOrderEvent implements RichOrderEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.RECEIVED; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { CREATE, PAY, CANCELLED, RECEIVED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_poly");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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())
|
||||||
|
.as("Resolved event was: " + chain.getTriggerPoint().getEvent())
|
||||||
|
.containsExactlyInAnyOrder("OrderEvents.CANCELLED", "OrderEvents.RECEIVED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUnwrapDeepMethodWrappers() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
OrderEvents eventType = wrap3(wrap2(wrap1(domainEvent.getType())));
|
||||||
|
service.updateOrderState(eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderEvents wrap1(OrderEvents e) { return e; }
|
||||||
|
private OrderEvents wrap2(OrderEvents e) { return e; }
|
||||||
|
private OrderEvents wrap3(OrderEvents e) { return e; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.CREATE; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { CREATE }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_deep_wrap");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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())
|
||||||
|
.as("Resolved event was: " + chain.getTriggerPoint().getEvent())
|
||||||
|
.containsExactlyInAnyOrder("OrderEvents.CREATE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolvePolymorphicInheritanceThroughDelegation() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(BaseEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BaseEvent {
|
||||||
|
OrderEvents getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractEvent implements BaseEvent {
|
||||||
|
protected OrderEvents internalGetType() {
|
||||||
|
return OrderEvents.PAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteEvent extends AbstractEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return internalGetType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnotherConcreteEvent implements BaseEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return delegateToHelper();
|
||||||
|
}
|
||||||
|
private OrderEvents delegateToHelper() {
|
||||||
|
return OrderEvents.CANCELLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY, CANCELLED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_poly_delegation");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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", "OrderEvents.CANCELLED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleMissingImplementationsGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(UnknownEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnknownEvent is not defined in the source (e.g. from a third-party library)
|
||||||
|
|
||||||
|
enum OrderEvents { CREATE }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_missing_impl");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
assertThat(chains).hasSize(1);
|
||||||
CallChain chain = chains.get(0);
|
CallChain chain = chains.get(0);
|
||||||
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("domainEvent.getType()");
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("domainEvent.getType()");
|
||||||
|
assertThat(chain.getTriggerPoint().getPolymorphicEvents()).isNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTraceMultipleLocalVariableAssignments() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
OrderEvents type1 = domainEvent.getType();
|
||||||
|
OrderEvents type2 = type1;
|
||||||
|
OrderEvents type3 = type2;
|
||||||
|
service.updateOrderState(type3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.RECEIVED; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { RECEIVED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_multiple_assign");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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.RECEIVED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveMethodInheritedFromAbstractBaseClass() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(BaseEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BaseEvent {
|
||||||
|
OrderEvents getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractEvent implements BaseEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return OrderEvents.PAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete subclass inherits getType() but doesn't override it!
|
||||||
|
class InheritingEvent extends AbstractEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_inherited");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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 shouldStopTracingRecursiveMethodsGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RecursiveEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecursiveEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return this.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_recursive");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// Should safely complete without stack overflow and return an empty result
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("domainEvent.getType()");
|
||||||
|
assertThat(chain.getTriggerPoint().getPolymorphicEvents()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleChainedMethodCallsGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(EventWrapper wrapper) {
|
||||||
|
service.updateOrderState(wrapper.getEvent().getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventWrapper {
|
||||||
|
public RichEvent getEvent() { return new RichEvent(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.PAY; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_chained");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// It might not fully resolve chained method calls, but it must not crash!
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("wrapper.getEvent().getType()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTraceStaticFieldReferences() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private static final OrderEvents CONSTANT_EVENT = OrderEvents.CANCELLED;
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent() {
|
||||||
|
service.updateOrderState(CONSTANT_EVENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { CANCELLED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_static_field");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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().getEvent()).isEqualTo("OrderEvents.CANCELLED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleAnonymousInnerClassesGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent() {
|
||||||
|
RichEvent event = new RichEvent() {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return OrderEvents.CREATE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
service.updateOrderState(event.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RichEvent {
|
||||||
|
OrderEvents getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { CREATE }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_anonymous");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// It successfully traces the local variable 'event' to its anonymous class initializer
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).startsWith("new RichEvent(){");
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).endsWith(".getType()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldLinkCallsInsideLambdasToEnclosingMethod() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent() {
|
||||||
|
Runnable r = () -> service.updateOrderState(OrderEvents.PAY);
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_lambda_scope");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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().getEvent()).isEqualTo("OrderEvents.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleTernaryOperatorsGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(boolean flag) {
|
||||||
|
OrderEvents event = flag ? OrderEvents.PAY : OrderEvents.CANCELLED;
|
||||||
|
service.updateOrderState(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY, CANCELLED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_ternary");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// It should gracefully fallback to the string representation of the ternary operator
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("flag ? OrderEvents.PAY : OrderEvents.CANCELLED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTraceLastTextualAssignmentInTryCatchBlocks() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent() {
|
||||||
|
OrderEvents event;
|
||||||
|
try {
|
||||||
|
event = OrderEvents.PAY;
|
||||||
|
} catch (Exception e) {
|
||||||
|
event = OrderEvents.CANCELLED;
|
||||||
|
}
|
||||||
|
service.updateOrderState(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY, CANCELLED }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_try_catch");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// The ASTVisitor picks up Assignments in textual order.
|
||||||
|
// CANCELLED is the last one textually in the method.
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("OrderEvents.CANCELLED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTraceArrayAndCollectionAccessGracefully() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent() {
|
||||||
|
OrderEvents[] events = new OrderEvents[]{OrderEvents.PAY};
|
||||||
|
service.updateOrderState(events[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_array");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(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);
|
||||||
|
// We don't dynamically resolve array indexes, but it shouldn't crash.
|
||||||
|
// It returns the array access expression as a string.
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("events[0]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "PLACE_ORDER",
|
"event" : "PLACE_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_ORDER",
|
"event" : "CANCEL_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||||
@@ -26,7 +28,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 21
|
"lineNumber" : 21,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "PAY_ORDER",
|
"event" : "PAY_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
||||||
@@ -35,7 +38,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "SHIP_ORDER",
|
"event" : "SHIP_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||||
@@ -44,7 +48,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "RETURN_ORDER",
|
"event" : "RETURN_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||||
@@ -53,7 +58,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -185,7 +191,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -219,7 +226,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 21
|
"lineNumber" : 21,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -248,7 +256,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -278,7 +287,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -312,7 +322,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -346,7 +357,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17
|
"lineNumber" : 17,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34
|
"lineNumber" : 34,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "AUDIT_EVENT",
|
"event" : "AUDIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "EXTERNAL_TRIGGER",
|
"event" : "EXTERNAL_TRIGGER",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -26,7 +28,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19
|
"lineNumber" : 19,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "SUBMIT_EVENT",
|
"event" : "SUBMIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -35,7 +38,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20
|
"lineNumber" : 20,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_EVENT",
|
"event" : "CANCEL_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -44,7 +48,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "[LIFECYCLE:RESTORE]",
|
"event" : "[LIFECYCLE:RESTORE]",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -53,7 +58,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29
|
"lineNumber" : 29,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "REACTIVE_EVENT",
|
"event" : "REACTIVE_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||||
@@ -62,7 +68,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -160,7 +167,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19
|
"lineNumber" : 19,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -190,7 +198,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20
|
"lineNumber" : 20,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -220,7 +229,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -250,7 +260,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34
|
"lineNumber" : 34,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -284,7 +295,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29
|
"lineNumber" : 29,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : "orderId",
|
"contextMachineId" : "orderId",
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -314,7 +326,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -343,7 +356,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34
|
"lineNumber" : 34,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "AUDIT_EVENT",
|
"event" : "AUDIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "EXTERNAL_TRIGGER",
|
"event" : "EXTERNAL_TRIGGER",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -26,7 +28,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19
|
"lineNumber" : 19,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "SUBMIT_EVENT",
|
"event" : "SUBMIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -35,7 +38,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20
|
"lineNumber" : 20,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_EVENT",
|
"event" : "CANCEL_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -44,7 +48,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "[LIFECYCLE:RESTORE]",
|
"event" : "[LIFECYCLE:RESTORE]",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -53,7 +58,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29
|
"lineNumber" : 29,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "REACTIVE_EVENT",
|
"event" : "REACTIVE_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||||
@@ -62,7 +68,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -160,7 +167,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19
|
"lineNumber" : 19,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -190,7 +198,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20
|
"lineNumber" : 20,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -220,7 +229,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -250,7 +260,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34
|
"lineNumber" : 34,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -284,7 +295,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29
|
"lineNumber" : 29,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : "orderId",
|
"contextMachineId" : "orderId",
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -314,7 +326,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -343,7 +356,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16
|
"lineNumber" : 16,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 15
|
"lineNumber" : 15,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -55,7 +56,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 15
|
"lineNumber" : 15,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 40
|
"lineNumber" : 40,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "ORDER_EVENT",
|
"event" : "ORDER_EVENT",
|
||||||
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
|
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 52
|
"lineNumber" : 52,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -103,7 +105,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 40
|
"lineNumber" : 40,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -67,7 +68,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18
|
"lineNumber" : 18,
|
||||||
|
"polymorphicEvents" : null
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "eventProvider",
|
"event" : "eventProvider",
|
||||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50
|
"lineNumber" : 50,
|
||||||
|
"polymorphicEvents" : null
|
||||||
}, {
|
}, {
|
||||||
"event" : "customMessage",
|
"event" : "customMessage",
|
||||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||||
@@ -26,7 +28,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 78
|
"lineNumber" : 78,
|
||||||
|
"polymorphicEvents" : null
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -140,7 +143,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -170,7 +174,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -200,7 +205,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -234,7 +240,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -268,7 +275,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 78
|
"lineNumber" : 78,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -302,7 +310,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25
|
"lineNumber" : 25,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -336,7 +345,8 @@
|
|||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50
|
"lineNumber" : 50,
|
||||||
|
"polymorphicEvents" : [ ]
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
Reference in New Issue
Block a user