Compare commits
3 Commits
24a9be3a4e
...
1b690582d6
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b690582d6 | |||
| 3ca834fa71 | |||
| c6b4a4be1e |
@@ -87,7 +87,14 @@ public class ConstantResolver {
|
|||||||
return sn.getIdentifier();
|
return sn.getIdentifier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("valueOf".equals(mi.getName().getIdentifier()) && mi.getExpression() != null) {
|
||||||
|
java.util.List<String> enumValues = context.getEnumValues(mi.getExpression().toString());
|
||||||
|
if (enumValues != null && !enumValues.isEmpty()) {
|
||||||
|
return "ENUM_SET:" + String.join(",", enumValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IMethodBinding mb = mi.resolveMethodBinding();
|
IMethodBinding mb = mi.resolveMethodBinding();
|
||||||
if (mb != null) {
|
if (mb != null) {
|
||||||
ITypeBinding returnType = mb.getReturnType();
|
ITypeBinding returnType = mb.getReturnType();
|
||||||
|
|||||||
@@ -316,37 +316,66 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (staticTd != null) {
|
if (staticTd != null) {
|
||||||
declaredType = context.getFqn(staticTd);
|
declaredType = context.getFqn(staticTd);
|
||||||
sourceMethod = "static-call";
|
sourceMethod = "static-call";
|
||||||
|
} else if (context.getEnumValues(varName) != null) {
|
||||||
|
declaredType = varName;
|
||||||
|
sourceMethod = "static-call";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaredType == null && methodName != null && !methodName.equals("VariableReference")) {
|
||||||
|
System.out.println("DEBUG fallback triggered for methodName: " + methodName);
|
||||||
|
for (String methodFqn : path) {
|
||||||
|
System.out.println("DEBUG fallback checking path method: " + methodFqn);
|
||||||
|
TypeDeclaration currentTd = context.getTypeDeclaration(methodFqn.substring(0, methodFqn.lastIndexOf('.')));
|
||||||
|
if (currentTd != null && context.findMethodDeclaration(currentTd, methodName, true) != null) {
|
||||||
|
System.out.println("DEBUG fallback found method in " + context.getFqn(currentTd));
|
||||||
|
CompilationUnit cu = currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
|
||||||
|
List<String> values = resolveMethodReturnConstant(context.getFqn(currentTd), methodName, 0, new HashSet<>(), cu);
|
||||||
|
System.out.println("DEBUG fallback resolveMethodReturnConstant returned: " + values);
|
||||||
|
if (values != null && !values.isEmpty()) {
|
||||||
|
polymorphicEvents.addAll(values);
|
||||||
|
sourceMethod = methodFqn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (declaredType != null) {
|
if (declaredType != null) {
|
||||||
List<String> typesToInspect = new ArrayList<>();
|
List<String> enumValues = context.getEnumValues(declaredType);
|
||||||
if ("inline-instantiation".equals(sourceMethod)) {
|
if (enumValues != null && !enumValues.isEmpty()) {
|
||||||
typesToInspect.add(declaredType);
|
for (String ev : enumValues) {
|
||||||
} else {
|
polymorphicEvents.add(parseEnumSetElement(ev));
|
||||||
typesToInspect.add(declaredType);
|
|
||||||
typesToInspect.addAll(context.getImplementations(declaredType));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String type : typesToInspect) {
|
|
||||||
Set<String> visited = new HashSet<>();
|
|
||||||
TypeDeclaration baseTd = context.getTypeDeclaration(type);
|
|
||||||
CompilationUnit cuToUse = null;
|
|
||||||
if (baseTd != null && baseTd.getRoot() instanceof CompilationUnit) {
|
|
||||||
cuToUse = (CompilationUnit) baseTd.getRoot();
|
|
||||||
} else {
|
|
||||||
String firstPathMethod = path.get(0);
|
|
||||||
String entryClassName = firstPathMethod.substring(0, firstPathMethod.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);
|
} else {
|
||||||
for (String constant : constants) {
|
List<String> typesToInspect = new ArrayList<>();
|
||||||
if (!polymorphicEvents.contains(constant)) {
|
if ("inline-instantiation".equals(sourceMethod)) {
|
||||||
polymorphicEvents.add(constant);
|
typesToInspect.add(declaredType);
|
||||||
|
} else {
|
||||||
|
typesToInspect.add(declaredType);
|
||||||
|
typesToInspect.addAll(context.getImplementations(declaredType));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String type : typesToInspect) {
|
||||||
|
Set<String> visited = new HashSet<>();
|
||||||
|
TypeDeclaration baseTd = context.getTypeDeclaration(type);
|
||||||
|
CompilationUnit cuToUse = null;
|
||||||
|
if (baseTd != null && baseTd.getRoot() instanceof CompilationUnit) {
|
||||||
|
cuToUse = (CompilationUnit) baseTd.getRoot();
|
||||||
|
} else {
|
||||||
|
String firstPathMethod = path.get(0);
|
||||||
|
String entryClassName = firstPathMethod.substring(0, firstPathMethod.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);
|
||||||
|
for (String constant : constants) {
|
||||||
|
if (!polymorphicEvents.contains(constant)) {
|
||||||
|
polymorphicEvents.add(constant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,8 +413,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
|
|
||||||
// Clean up any remaining invalid AST fallbacks (like 'type', 'event', or unresolved class names)
|
// Clean up any remaining invalid AST fallbacks (like 'type', 'event', or unresolved class names)
|
||||||
polymorphicEvents.removeIf(e -> {
|
polymorphicEvents.removeIf(e -> {
|
||||||
String val = e.contains(".") ? e.substring(e.lastIndexOf('.') + 1) : e;
|
if (e.contains(".")) return false; // If it's a fully qualified enum/constant, keep it
|
||||||
return !val.equals(val.toUpperCase()) || val.length() <= 2;
|
String val = e;
|
||||||
|
return !val.equals(val.toUpperCase()) || val.length() <= 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
polymorphicEvents = polymorphicEvents.stream().distinct().collect(java.util.stream.Collectors.toList());
|
polymorphicEvents = polymorphicEvents.stream().distinct().collect(java.util.stream.Collectors.toList());
|
||||||
@@ -825,8 +855,23 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (Object argObj : cic.arguments()) {
|
boolean handledByLombok = false;
|
||||||
extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) argObj, constants);
|
if (methodName.startsWith("get") && methodName.length() > 3) {
|
||||||
|
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||||
|
TypeDeclaration typeDecl = context.getTypeDeclaration(typeName);
|
||||||
|
if (typeDecl != null) {
|
||||||
|
String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
|
||||||
|
int fieldIndex = findConstructorArgumentIndexForLombokField(typeDecl, fieldName);
|
||||||
|
if (fieldIndex >= 0 && fieldIndex < cic.arguments().size()) {
|
||||||
|
extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) cic.arguments().get(fieldIndex), constants);
|
||||||
|
handledByLombok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handledByLombok) {
|
||||||
|
for (Object argObj : cic.arguments()) {
|
||||||
|
extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) argObj, constants);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,6 +897,61 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int findConstructorArgumentIndexForLombokField(TypeDeclaration td, String fieldName) {
|
||||||
|
boolean isAllArgsConstructor = false;
|
||||||
|
boolean isRequiredArgsConstructor = false;
|
||||||
|
boolean isData = false;
|
||||||
|
boolean isValue = false;
|
||||||
|
|
||||||
|
for (Object modObj : td.modifiers()) {
|
||||||
|
if (modObj instanceof org.eclipse.jdt.core.dom.MarkerAnnotation ma) {
|
||||||
|
String name = ma.getTypeName().getFullyQualifiedName();
|
||||||
|
if (name.equals("AllArgsConstructor") || name.equals("lombok.AllArgsConstructor")) isAllArgsConstructor = true;
|
||||||
|
if (name.equals("RequiredArgsConstructor") || name.equals("lombok.RequiredArgsConstructor")) isRequiredArgsConstructor = true;
|
||||||
|
if (name.equals("Data") || name.equals("lombok.Data")) isData = true;
|
||||||
|
if (name.equals("Value") || name.equals("lombok.Value")) isValue = true;
|
||||||
|
} else if (modObj instanceof org.eclipse.jdt.core.dom.NormalAnnotation na) {
|
||||||
|
String name = na.getTypeName().getFullyQualifiedName();
|
||||||
|
if (name.equals("AllArgsConstructor") || name.equals("lombok.AllArgsConstructor")) isAllArgsConstructor = true;
|
||||||
|
if (name.equals("RequiredArgsConstructor") || name.equals("lombok.RequiredArgsConstructor")) isRequiredArgsConstructor = true;
|
||||||
|
if (name.equals("Data") || name.equals("lombok.Data")) isData = true;
|
||||||
|
if (name.equals("Value") || name.equals("lombok.Value")) isValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAllArgsConstructor && !isRequiredArgsConstructor && !isData && !isValue) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (org.eclipse.jdt.core.dom.FieldDeclaration fd : td.getFields()) {
|
||||||
|
boolean isStatic = false;
|
||||||
|
boolean isFinal = false;
|
||||||
|
boolean hasNonNull = false;
|
||||||
|
for (Object modObj : fd.modifiers()) {
|
||||||
|
if (modObj instanceof org.eclipse.jdt.core.dom.Modifier mod) {
|
||||||
|
if (mod.isStatic()) isStatic = true;
|
||||||
|
if (mod.isFinal()) isFinal = true;
|
||||||
|
} else if (modObj instanceof org.eclipse.jdt.core.dom.MarkerAnnotation ma) {
|
||||||
|
if (ma.getTypeName().getFullyQualifiedName().endsWith("NonNull")) hasNonNull = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isStatic) continue;
|
||||||
|
|
||||||
|
if (isRequiredArgsConstructor && !isAllArgsConstructor && !isFinal && !hasNonNull) continue;
|
||||||
|
if (isData && !isAllArgsConstructor && !isFinal && !hasNonNull) continue;
|
||||||
|
|
||||||
|
for (Object fragObj : fd.fragments()) {
|
||||||
|
org.eclipse.jdt.core.dom.VariableDeclarationFragment frag = (org.eclipse.jdt.core.dom.VariableDeclarationFragment) fragObj;
|
||||||
|
if (frag.getName().getIdentifier().equals(fieldName)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
private void extractConstantsFromArgument(org.eclipse.jdt.core.dom.Expression argObj, List<String> constants) {
|
private void extractConstantsFromArgument(org.eclipse.jdt.core.dom.Expression argObj, List<String> constants) {
|
||||||
if (argObj instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation innerCic) {
|
if (argObj instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation innerCic) {
|
||||||
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(innerCic.getType());
|
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(innerCic.getType());
|
||||||
@@ -1031,14 +1131,26 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
protected Expression unwrapMethodInvocation(org.eclipse.jdt.core.dom.MethodInvocation mi, int depth) {
|
protected Expression unwrapMethodInvocation(org.eclipse.jdt.core.dom.MethodInvocation mi, int depth) {
|
||||||
if (depth > 5) return mi;
|
if (depth > 5) return mi;
|
||||||
// Do not unwrap if called on an object (e.g. mapper.toEvent)
|
// Do not unwrap if called on an object (e.g. mapper.toEvent)
|
||||||
// Exceptions: Optional.of, Optional.ofNullable, etc.
|
// Exceptions: Optional.of, Optional.ofNullable, Stream, etc.
|
||||||
if (mi.getExpression() != null) {
|
if (mi.getExpression() != null) {
|
||||||
String exprStr = mi.getExpression().toString();
|
String exprStr = mi.getExpression().toString();
|
||||||
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays")) {
|
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects")) {
|
||||||
return mi;
|
return mi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mi.arguments().isEmpty()) {
|
if (!mi.arguments().isEmpty()) {
|
||||||
|
String mName = mi.getName().getIdentifier();
|
||||||
|
String lowerName = mName.toLowerCase();
|
||||||
|
|
||||||
|
// Do not unwrap methods that explicitly convert, map, parse, or create
|
||||||
|
if (lowerName.startsWith("map") || lowerName.startsWith("parse") ||
|
||||||
|
lowerName.startsWith("convert") || lowerName.startsWith("to") ||
|
||||||
|
lowerName.startsWith("resolve") || lowerName.startsWith("build") ||
|
||||||
|
lowerName.startsWith("create") || lowerName.startsWith("from")) {
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
Expression arg = (Expression) mi.arguments().get(0);
|
Expression arg = (Expression) mi.arguments().get(0);
|
||||||
if (arg instanceof org.eclipse.jdt.core.dom.MethodInvocation innerMi) {
|
if (arg instanceof org.eclipse.jdt.core.dom.MethodInvocation innerMi) {
|
||||||
return unwrapMethodInvocation(innerMi, depth + 1);
|
return unwrapMethodInvocation(innerMi, depth + 1);
|
||||||
@@ -1211,8 +1323,14 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
public boolean visit(org.eclipse.jdt.core.dom.ExpressionMethodReference node) {
|
public boolean visit(org.eclipse.jdt.core.dom.ExpressionMethodReference node) {
|
||||||
if (node.getName().getIdentifier().equals(mName)) {
|
if (node.getName().getIdentifier().equals(mName)) {
|
||||||
if (node.getParent() instanceof org.eclipse.jdt.core.dom.MethodInvocation parentMi) {
|
if (node.getParent() instanceof org.eclipse.jdt.core.dom.MethodInvocation parentMi) {
|
||||||
if (parentMi.getExpression() != null) {
|
if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isFunctionalCollectionMethod(parentMi)) {
|
||||||
callerExprs.addAll(traceVariableAll(parentMi.getExpression(), visitedVariables));
|
callerExprs.addAll(traceVariableAll(parentMi.getExpression(), visitedVariables));
|
||||||
|
} else {
|
||||||
|
for (Object arg : parentMi.arguments()) {
|
||||||
|
if (arg != node) {
|
||||||
|
callerExprs.addAll(traceVariableAll((Expression) arg, visitedVariables));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,13 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
if (td2 != null) {
|
if (td2 != null) {
|
||||||
String refMethod = resolveMethodInType(td2, emr.getName().getIdentifier());
|
String refMethod = resolveMethodInType(td2, emr.getName().getIdentifier());
|
||||||
if (refMethod != null) {
|
if (refMethod != null) {
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, args));
|
List<String> implicitArgs = new ArrayList<>();
|
||||||
|
if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isFunctionalCollectionMethod(node)) {
|
||||||
|
implicitArgs.add(node.getExpression().toString());
|
||||||
|
} else {
|
||||||
|
implicitArgs.addAll(args);
|
||||||
|
}
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, implicitArgs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -58,7 +64,13 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
}
|
}
|
||||||
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));
|
List<String> implicitArgs = new ArrayList<>();
|
||||||
|
if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isFunctionalCollectionMethod(node)) {
|
||||||
|
implicitArgs.add(node.getExpression().toString());
|
||||||
|
} else {
|
||||||
|
implicitArgs.addAll(args);
|
||||||
|
}
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, implicitArgs));
|
||||||
|
|
||||||
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
||||||
for (String impl : impls) {
|
for (String impl : impls) {
|
||||||
|
|||||||
@@ -49,7 +49,13 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
if (td2 != null) {
|
if (td2 != null) {
|
||||||
String refMethod = resolveMethodInType(td2, emr.getName().getIdentifier());
|
String refMethod = resolveMethodInType(td2, emr.getName().getIdentifier());
|
||||||
if (refMethod != null) {
|
if (refMethod != null) {
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, args));
|
List<String> implicitArgs = new ArrayList<>();
|
||||||
|
if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isFunctionalCollectionMethod(node)) {
|
||||||
|
implicitArgs.add(node.getExpression().toString());
|
||||||
|
} else {
|
||||||
|
implicitArgs.addAll(args);
|
||||||
|
}
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(refMethod, implicitArgs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -62,7 +68,13 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
}
|
}
|
||||||
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));
|
List<String> implicitArgs = new ArrayList<>();
|
||||||
|
if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isFunctionalCollectionMethod(node)) {
|
||||||
|
implicitArgs.add(node.getExpression().toString());
|
||||||
|
} else {
|
||||||
|
implicitArgs.addAll(args);
|
||||||
|
}
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, implicitArgs));
|
||||||
|
|
||||||
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
||||||
for (String impl : impls) {
|
for (String impl : impls) {
|
||||||
|
|||||||
@@ -36,4 +36,62 @@ public final class AstUtils {
|
|||||||
return type.toString(); // fallback
|
return type.toString(); // fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFunctionalCollectionMethod(org.eclipse.jdt.core.dom.MethodInvocation node) {
|
||||||
|
if (node.getExpression() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
org.eclipse.jdt.core.dom.ITypeBinding typeBinding = node.getExpression().resolveTypeBinding();
|
||||||
|
if (typeBinding != null) {
|
||||||
|
if (isCollectionOrStreamType(typeBinding)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String methodName = node.getName().getIdentifier();
|
||||||
|
if (methodName.equals("forEach") || methodName.equals("map") ||
|
||||||
|
methodName.equals("filter") || methodName.equals("flatMap") ||
|
||||||
|
methodName.equals("anyMatch") || methodName.equals("allMatch") ||
|
||||||
|
methodName.equals("noneMatch") || methodName.equals("reduce") ||
|
||||||
|
methodName.equals("collect") || methodName.equals("removeIf") ||
|
||||||
|
methodName.equals("ifPresent") || methodName.equals("ifPresentOrElse") ||
|
||||||
|
methodName.equals("compute") || methodName.equals("computeIfAbsent") ||
|
||||||
|
methodName.equals("computeIfPresent") || methodName.equals("replaceAll") ||
|
||||||
|
methodName.equals("merge") || methodName.equals("peek") ||
|
||||||
|
methodName.equals("doOnNext") || methodName.equals("doOnSuccess") ||
|
||||||
|
methodName.equals("doOnError") || methodName.equals("concatMap") ||
|
||||||
|
methodName.equals("switchMap")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCollectionOrStreamType(org.eclipse.jdt.core.dom.ITypeBinding typeBinding) {
|
||||||
|
String fqn = typeBinding.getErasure().getQualifiedName();
|
||||||
|
if (fqn.startsWith("java.util.stream.") ||
|
||||||
|
fqn.equals("java.lang.Iterable") ||
|
||||||
|
fqn.equals("java.util.Collection") ||
|
||||||
|
fqn.equals("java.util.List") ||
|
||||||
|
fqn.equals("java.util.Set") ||
|
||||||
|
fqn.equals("java.util.Map") ||
|
||||||
|
fqn.equals("java.util.Iterator") ||
|
||||||
|
fqn.equals("java.util.Optional") ||
|
||||||
|
fqn.equals("reactor.core.publisher.Flux") ||
|
||||||
|
fqn.equals("reactor.core.publisher.Mono")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (org.eclipse.jdt.core.dom.ITypeBinding intf : typeBinding.getInterfaces()) {
|
||||||
|
if (isCollectionOrStreamType(intf)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeBinding.getSuperclass() != null) {
|
||||||
|
return isCollectionOrStreamType(typeBinding.getSuperclass());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2704,4 +2704,53 @@ class HeuristicCallGraphEngineTest {
|
|||||||
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
|
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
|
||||||
.containsExactly("CORRECT_EVENT");
|
.containsExactly("CORRECT_EVENT");
|
||||||
}
|
}
|
||||||
}
|
@Test
|
||||||
|
void shouldResolveEnumValueOfFromString(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class Endpoint {
|
||||||
|
private OrderService service;
|
||||||
|
public void trigger(String eventString) {
|
||||||
|
service.doSomething(eventString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void doSomething(String str) {
|
||||||
|
MyEvents event = mapToEnum(str);
|
||||||
|
sendEvent(event);
|
||||||
|
}
|
||||||
|
private MyEvents mapToEnum(String str) {
|
||||||
|
return MyEvents.valueOf(str);
|
||||||
|
}
|
||||||
|
public void sendEvent(MyEvents e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyEvents { A, B }
|
||||||
|
""";
|
||||||
|
Files.writeString(tempDir.resolve("Endpoint.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.Endpoint")
|
||||||
|
.methodName("trigger")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.OrderService")
|
||||||
|
.methodName("sendEvent")
|
||||||
|
.event("e")
|
||||||
|
.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("MyEvents.A", "MyEvents.B");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user