diff --git a/TestHeuristic.class b/TestHeuristic.class new file mode 100644 index 0000000..b70767a Binary files /dev/null and b/TestHeuristic.class differ diff --git a/TestHeuristic.java b/TestHeuristic.java new file mode 100644 index 0000000..34a7512 --- /dev/null +++ b/TestHeuristic.java @@ -0,0 +1,17 @@ +import java.util.*; + +public class TestHeuristic { + public static void main(String[] args) { + System.out.println(isFullyQualifiedMethod("TransformerImpl.transform")); + System.out.println(isFullyQualifiedMethod("Transformer.transform")); + } + + private static boolean isFullyQualifiedMethod(String methodFqn) { + if (methodFqn == null || !methodFqn.contains(".")) return false; + int lastDot = methodFqn.lastIndexOf('.'); + String classPart = methodFqn.substring(0, lastDot); + if (classPart.contains(".")) return true; + if (!classPart.isEmpty() && Character.isUpperCase(classPart.charAt(0))) return true; + return false; + } +} diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java index b59a98e..2eddf6d 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java @@ -179,6 +179,7 @@ public class CallGraphPathFinder { } private boolean calculateHeuristicMatch(String neighbor, String target) { + System.out.println("calculateHeuristicMatch: neighbor=" + neighbor + ", target=" + target); if (neighbor.equals(target)) return true; if (isFullyQualifiedMethod(neighbor) && isFullyQualifiedMethod(target)) { @@ -202,7 +203,6 @@ public class CallGraphPathFinder { String simpleClassTarget = classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget; if (simpleClassNeighbor.equals(simpleClassTarget)) return true; - if (simpleClassNeighbor.contains(simpleClassTarget) || simpleClassTarget.contains(simpleClassNeighbor)) return true; List impls = context.getImplementations(classTarget); if (impls != null) { @@ -222,7 +222,21 @@ public class CallGraphPathFinder { String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target; String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor; - return simpleNeighbor.equals(simpleTarget); + if (!simpleNeighbor.equals(simpleTarget)) return false; + + String classNeighbor = neighbor.contains(".") ? neighbor.substring(0, neighbor.lastIndexOf('.')) : null; + String classTarget = target.contains(".") ? target.substring(0, target.lastIndexOf('.')) : null; + String simpleClassTarget = classTarget != null && classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget; + String simpleClassNeighbor = classNeighbor != null && classNeighbor.contains(".") ? classNeighbor.substring(classNeighbor.lastIndexOf('.') + 1) : classNeighbor; + + if (simpleClassNeighbor != null && simpleClassTarget != null) { + if (simpleClassNeighbor.equalsIgnoreCase(simpleClassTarget)) return true; + // e.g. "this" vs "com.example.Machine" + if (simpleClassNeighbor.equals("this") || simpleClassNeighbor.equals("super")) return true; + return false; + } + + return true; } private boolean isFullyQualifiedMethod(String methodFqn) { diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java index 07d8dea..6cb13f2 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java @@ -525,12 +525,37 @@ public class VariableTracer { if (simpleClassNeighbor.equals(simpleClassTarget)) return true; List impls = context.getImplementations(classTarget); - if (impls != null && impls.contains(classNeighbor)) return true; + if (impls != null) { + for (String impl : impls) { + if (impl.equals(classNeighbor) || impl.endsWith("." + classNeighbor)) return true; + } + } List implsN = context.getImplementations(classNeighbor); - if (implsN != null && implsN.contains(classTarget)) return true; + if (implsN != null) { + for (String impl : implsN) { + if (impl.equals(classTarget) || impl.endsWith("." + classTarget)) return true; + } + } + return false; } - return false; + + String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target; + String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor; + if (!simpleNeighbor.equals(simpleTarget)) return false; + + String classNeighbor = neighbor.contains(".") ? neighbor.substring(0, neighbor.lastIndexOf('.')) : null; + String classTarget = target.contains(".") ? target.substring(0, target.lastIndexOf('.')) : null; + String simpleClassTarget = classTarget != null && classTarget.contains(".") ? classTarget.substring(classTarget.lastIndexOf('.') + 1) : classTarget; + String simpleClassNeighbor = classNeighbor != null && classNeighbor.contains(".") ? classNeighbor.substring(classNeighbor.lastIndexOf('.') + 1) : classNeighbor; + + if (simpleClassNeighbor != null && simpleClassTarget != null) { + if (simpleClassNeighbor.equalsIgnoreCase(simpleClassTarget)) return true; + if (simpleClassNeighbor.equals("this") || simpleClassNeighbor.equals("super")) return true; + return false; + } + + return true; } private MethodDeclaration findEnclosingMethod(ASTNode node) {