transition enricher

This commit is contained in:
2026-06-19 09:06:33 +02:00
parent e894566112
commit 0a23daae05
27 changed files with 1949 additions and 36 deletions

View File

@@ -58,47 +58,73 @@ public class CallGraphBuilder {
String event = tp.getEvent();
if (event == null || event.isEmpty() || event.contains("\"")) return tp;
TypeDeclaration td = context.getTypeDeclaration(tp.getClassName());
if (td != null) {
MethodDeclaration md = context.findMethodDeclaration(td, tp.getMethodName(), true);
if (md != null) {
int paramIndex = -1;
for (int i = 0; i < md.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(i);
if (svd.getName().getIdentifier().equals(event)) {
paramIndex = i;
break;
}
}
if (paramIndex >= 0) {
String caller = path.get(path.size() - 2);
String target = path.get(path.size() - 1);
List<CallEdge> edges = callGraph.get(caller);
if (edges != null) {
for (CallEdge edge : edges) {
if (edge.getTargetMethod().equals(target) || isHeuristicMatch(edge.getTargetMethod(), target)) {
if (paramIndex < edge.getArguments().size()) {
String resolvedValue = edge.getArguments().get(paramIndex);
if (resolvedValue != null) {
return TriggerPoint.builder()
.event(resolvedValue)
.className(tp.getClassName())
.methodName(tp.getMethodName())
.sourceFile(tp.getSourceFile())
.lineNumber(tp.getLineNumber())
.build();
}
}
String currentParamName = event;
String resolvedValue = event;
// Walk backwards up the call chain
for (int i = path.size() - 1; i > 0; i--) {
String target = path.get(i);
String caller = path.get(i - 1);
// Find parameter index in target method
int paramIndex = getParameterIndex(target, currentParamName);
if (paramIndex < 0) {
break; // Parameter name changed or not found, stop tracing
}
// Find the edge from caller to target to get the argument passed
List<CallEdge> edges = callGraph.get(caller);
boolean found = false;
if (edges != null) {
for (CallEdge edge : edges) {
if (edge.getTargetMethod().equals(target) || isHeuristicMatch(edge.getTargetMethod(), target)) {
if (paramIndex < edge.getArguments().size()) {
String arg = edge.getArguments().get(paramIndex);
if (arg != null) {
currentParamName = arg;
resolvedValue = arg;
found = true;
break;
}
}
}
}
}
if (!found) break; // Could not map argument
}
if (!resolvedValue.equals(event)) {
return TriggerPoint.builder()
.event(resolvedValue)
.className(tp.getClassName())
.methodName(tp.getMethodName())
.sourceFile(tp.getSourceFile())
.lineNumber(tp.getLineNumber())
.build();
}
return tp;
}
private int getParameterIndex(String methodFqn, String paramName) {
if (methodFqn == null || !methodFqn.contains(".")) return -1;
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 (int i = 0; i < md.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(i);
if (svd.getName().getIdentifier().equals(paramName)) {
return i;
}
}
}
}
return -1;
}
private String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) {
for (String node : path) {
List<CallEdge> edges = callGraph.get(node);
@@ -174,6 +200,22 @@ public class CallGraphBuilder {
List<String> args = new ArrayList<>();
for (Object argObj : astArguments) {
Expression expr = (Expression) argObj;
// Extract from lambda
if (expr instanceof LambdaExpression le) {
ASTNode body = le.getBody();
if (body instanceof Expression bodyExpr) {
expr = bodyExpr;
} else if (body instanceof Block block) {
for (Object stmtObj : block.statements()) {
if (stmtObj instanceof ReturnStatement rs && rs.getExpression() != null) {
expr = rs.getExpression();
break;
}
}
}
}
String val = constantResolver.resolve(expr, context);
if (val == null) {
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)

View File

@@ -240,10 +240,22 @@ public class GenericEventDetector {
if (resolved != null) return resolved;
// If not a constant, it might be a parameter like 'event'
if (payloadExpr instanceof CastExpression ce) {
payloadExpr = ce.getExpression();
}
if (payloadExpr instanceof SimpleName sn) {
return sn.getIdentifier(); // Return the parameter name so the call graph can fill it
String extracted = extractEventFromMessageBuilder(payloadExpr);
if (extracted != null) {
return extracted;
}
return sn.getIdentifier(); // Fall back to returning the parameter name
}
return payloadExpr.toString();
} else if (current.arguments().isEmpty() && current.getExpression() instanceof SimpleName sn) {
// If the event is obtained by calling a method on a provider/supplier parameter,
// return the provider's name so CallGraphBuilder can trace the lambda argument
return sn.getIdentifier();
}
Expression receiver = current.getExpression();

View File

@@ -71,6 +71,7 @@ public class MessagingDetector {
.name(protocolName + ": " + destination)
.className(context.getFqn((TypeDeclaration) method.getParent()))
.methodName(method.getName().getIdentifier())
.sourceFile(context.getRelativePath(context.getFqn((TypeDeclaration) method.getParent())))
.metadata(metadata)
.parameters(extractParameters(method))
.build());

View File

@@ -230,7 +230,20 @@ public class CodebaseContext {
// Try FQN match if input was simple name
String fqn = simpleNameToFqn.get(interfaceName);
if (fqn != null) return interfaceToImpls.getOrDefault(fqn, Collections.emptyList());
if (fqn != null && interfaceToImpls.containsKey(fqn)) {
return interfaceToImpls.get(fqn);
}
// Try simple name match if input was FQN
if (interfaceName.contains(".")) {
String simpleName = interfaceName.substring(interfaceName.lastIndexOf('.') + 1);
List<String> simpleImpls = interfaceToImpls.get(simpleName);
if (simpleImpls != null) {
// To be perfectly safe, we could check if simpleNameToFqn matches,
// but for call graphs, returning potential implementations is fine.
return simpleImpls;
}
}
return Collections.emptyList();
}

View File

@@ -32,6 +32,9 @@ public class FileUtils {
return paths
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(extension))
.filter(p -> matchers.stream().noneMatch(m -> m.matches(p)))
.map(Path::toAbsolutePath)
.map(Path::normalize)
.distinct()
.collect(Collectors.toList());
}
}