diff --git a/pom.xml b/pom.xml
index 2c02afe..c224ab0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,8 +24,65 @@
4.13.1
test
+
+
+
+ fr.inria.gforge.spoon
+ spoon-core
+ 10.4.1
+
+
+
+
+
+ org.soot-oss
+ sootup.core
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.java.core
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.java.sourcecode
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.java.bytecode
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.jimple.parser
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.callgraph
+ 1.2.0
+
+
+ org.soot-oss
+ sootup.analysis
+ 1.2.0
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+ /maven.google.com
+ https://maven.google.com
+
+
+
diff --git a/src/main/java/tum/dpid/App.java b/src/main/java/tum/dpid/App.java
deleted file mode 100644
index cf88980..0000000
--- a/src/main/java/tum/dpid/App.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package tum.dpid;
-
-/**
- * Hello world!
- *
- */
-public class App
-{
- public static void main( String[] args )
- {
- System.out.println( "Hello World!" );
- }
-}
diff --git a/src/main/java/tum/dpid/Runner.java b/src/main/java/tum/dpid/Runner.java
new file mode 100644
index 0000000..a3bebca
--- /dev/null
+++ b/src/main/java/tum/dpid/Runner.java
@@ -0,0 +1,81 @@
+package tum.dpid;
+
+import spoon.Launcher;
+import spoon.reflect.CtModel;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.reference.CtTypeReference;
+import spoon.reflect.visitor.filter.TypeFilter;
+import tum.dpid.services.DatabaseMethodFinder;
+import tum.dpid.services.processors.ClassHierarchyOrder;
+import tum.dpid.services.processors.MethodCallChain;
+import tum.dpid.services.processors.MethodOrder;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Runner class of method call chain processor and analyzer by utilizing spoon
+ *
+ */
+public class Runner
+{
+
+ public static void main( String[] args )
+ {
+ Launcher launcher = new Launcher();
+ launcher.getEnvironment().setNoClasspath(true);
+
+ //String testDirectory = "src/main/resources/tester"; //"../../../resources/tester";
+ String directoryPath = "../../fromItestra/LoopAntiPattern";
+ launcher.addInputResource(directoryPath);
+ launcher.buildModel();
+ CtModel model = launcher.getModel();
+
+ //All Java Classes in the project
+ List> allClasses = model.getElements(new TypeFilter<>(CtClass.class));
+
+ //All Methods in the project
+ List> allMethods = model.getElements(new TypeFilter<>(CtMethod.class));
+
+ // Methods which makes request to database in project
+ Set> databaseMethods = DatabaseMethodFinder.findDatabaseMethods(model, allClasses);
+ for (CtMethod> method : databaseMethods) {
+ System.out.println("Database Method is: " + method.getSignature() + " (" + method.getDeclaringType().getQualifiedName() + ")");
+ }
+
+ //Initialize class hierarchy order processor and start processing it
+ ClassHierarchyOrder classHierarchyOrder = new ClassHierarchyOrder();
+ launcher.addProcessor(classHierarchyOrder);
+ launcher.process();
+
+ //Initialize method execution order processor (call chain of method) and start processing it
+ MethodOrder methodOrder = new MethodOrder();
+ launcher.addProcessor(methodOrder);
+ launcher.process();
+
+ Map, List>> callList = methodOrder.getCallList();
+ Map, Set>> classHierarchy = classHierarchyOrder.getClassImplementors() ;
+
+ //Process each method in the project and print out their call chain
+ for (CtMethod> ctMethod: allMethods) {
+ List methodCallHierarchies = MethodCallChain.processMethod(ctMethod, callList, classHierarchy);
+ if (methodCallHierarchies.isEmpty()) {
+ System.out.println("No method `" + ctMethod.getDeclaringType() + "` found. \n");
+ }
+ if (methodCallHierarchies.size() > 1) {
+ System.out.println("Found " + methodCallHierarchies.size() + " matching methods...\n");
+ }
+ for (MethodCallChain methodCallHierarchy : methodCallHierarchies) {
+ methodCallHierarchy.printCallChain();
+ System.out.println();
+ }
+ }
+ }
+
+
+}
+
diff --git a/src/main/java/tum/dpid/cfg/CfgExtractor.java b/src/main/java/tum/dpid/cfg/CfgExtractor.java
new file mode 100644
index 0000000..bacd985
--- /dev/null
+++ b/src/main/java/tum/dpid/cfg/CfgExtractor.java
@@ -0,0 +1,88 @@
+package tum.dpid.cfg;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Optional;
+
+import sootup.callgraph.CallGraph;
+import sootup.callgraph.CallGraphAlgorithm;
+import sootup.callgraph.ClassHierarchyAnalysisAlgorithm;
+import sootup.core.inputlocation.AnalysisInputLocation;
+import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
+import sootup.core.jimple.common.stmt.JInvokeStmt;
+import sootup.core.model.SootClass;
+import sootup.core.model.SootMethod;
+import sootup.core.signatures.MethodSignature;
+import sootup.core.typehierarchy.ViewTypeHierarchy;
+import sootup.core.types.ClassType;
+import sootup.core.types.VoidType;
+import sootup.core.util.DotExporter;
+import sootup.core.views.View;
+import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation;
+import sootup.java.bytecode.inputlocation.PathBasedAnalysisInputLocation;
+import sootup.java.core.JavaIdentifierFactory;
+import sootup.java.core.JavaSootMethod;
+import sootup.java.core.language.JavaJimple;
+import sootup.java.core.types.JavaClassType;
+import sootup.java.core.views.JavaView;
+import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation;
+
+/**
+ * Experimental class to extract control flow graph of project by using SootUp
+ */
+
+public class CfgExtractor {
+
+ public void CfgExtractorFunc(String inputPath){
+
+ AnalysisInputLocation inputLocation =
+ new JavaClassPathAnalysisInputLocation("target/classes");
+
+ JavaView view = new JavaView(inputLocation);
+
+ System.out.println("All Class: " + view.getClasses());
+
+ JavaClassType classType =
+ view.getIdentifierFactory().getClassType("tum.dpid.App");
+
+ if (!view.getClass(classType).isPresent()) {
+ System.out.println("Class not found!");
+ return;
+ }
+
+ // Retrieve the specified class from the project.
+ SootClass sootClass = view.getClass(classType).get();
+
+
+ MethodSignature methodSignature = view.getIdentifierFactory().getMethodSignature(
+ classType, "main", "void", Collections.singletonList("java.lang.String[]"));
+
+ Optional opt = view.getMethod(methodSignature);
+
+ // Create type hierarchy and CHA
+ final ViewTypeHierarchy typeHierarchy = new ViewTypeHierarchy(view);
+
+ CallGraphAlgorithm cha = new ClassHierarchyAnalysisAlgorithm(view);
+
+ // Create CG by initializing CHA with entry method(s)
+ CallGraph cg = cha.initialize(Collections.singletonList(methodSignature));
+
+ cg.callsFrom(methodSignature).forEach(System.out::println);
+
+ var x = DotExporter.createUrlToWebeditor( opt.get().getBody().getStmtGraph());
+
+ System.out.println("Dot Graph is " + x);
+
+ //ystem.out.println(cg);
+ }
+
+ /* //CFG with sootup
+ String sourcePath = "../../fromItestra/LoopAntiPattern";
+ String binaryPath = "../../fromItestra/LoopAntiPattern/build/classes/java/main/com/example/LoopAntiPattern" ; //LoopAntiPattern-0.0.1-SNAPSHOT.jar"
+ CfgExtractor cfgExtractor = new CfgExtractor();
+ cfgExtractor.CfgExtractorFunc(binaryPath);
+ System.out.println("*************************************************************************************************************************************");
+
+ */
+}
diff --git a/src/main/java/tum/dpid/services/DatabaseMethodFinder.java b/src/main/java/tum/dpid/services/DatabaseMethodFinder.java
new file mode 100644
index 0000000..e88d105
--- /dev/null
+++ b/src/main/java/tum/dpid/services/DatabaseMethodFinder.java
@@ -0,0 +1,49 @@
+package tum.dpid.services;
+
+import spoon.reflect.CtModel;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.declaration.ModifierKind;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DatabaseMethodFinder {
+
+ public static Set> findDatabaseMethods(CtModel model, List> allClasses) {
+ Set> databaseMethods = new HashSet<>();
+
+ for (CtClass> ctClass : allClasses) {
+ if (isDatabaseClass(ctClass)) {
+ for(CtMethod> method : ctClass.getMethods()) {
+ if (!isGetterOrSetter(method)) {
+ databaseMethods.add(method);
+ }
+ }
+ }
+ }
+ return databaseMethods;
+ }
+
+ private static boolean isDatabaseClass(CtClass> ctClass) {
+ // Define the package name where database-related classes are located
+ String databasePackage = "com.example.LoopAntiPattern.data.repository";
+ return ctClass.getPackage().getQualifiedName().startsWith(databasePackage);
+ }
+
+ private static boolean isGetterOrSetter(CtMethod> method) {
+ String methodName = method.getSimpleName();
+ boolean isStatic = method.getModifiers().contains(ModifierKind.STATIC);
+
+ // Check for getter method
+ boolean isGetter = !isStatic && method.getParameters().isEmpty() &&
+ (methodName.startsWith("get") || methodName.startsWith("is"));
+
+ // Check for setter method
+ boolean isSetter = !isStatic && method.getParameters().size() == 1 &&
+ methodName.startsWith("set");
+
+ return isGetter || isSetter;
+ }
+}
diff --git a/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java b/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java
new file mode 100644
index 0000000..e4a3111
--- /dev/null
+++ b/src/main/java/tum/dpid/services/processors/ClassHierarchyOrder.java
@@ -0,0 +1,39 @@
+package tum.dpid.services.processors;
+
+import spoon.processing.AbstractProcessor;
+import spoon.reflect.reference.CtTypeReference;
+import spoon.support.reflect.declaration.CtClassImpl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassHierarchyOrder extends AbstractProcessor> {
+
+ private final Map, Set>> classImplementors = new HashMap<>();
+
+ public void findInheritance(CtTypeReference> classRef, CtTypeReference> superClass) {
+ Set> subclasses = classImplementors.computeIfAbsent(superClass, k -> new HashSet<>());
+ subclasses.add(classRef);
+ }
+
+ @Override
+ public void process(CtClassImpl ctClass) {
+ if (ctClass.getReference().isAnonymous()) {
+ return;
+ }
+ if (ctClass.getSuperclass() != null) {
+ findInheritance(ctClass.getReference(), ctClass.getSuperclass());
+ }
+ for (Object o : ctClass.getSuperInterfaces()) {
+ CtTypeReference> superclass = (CtTypeReference>) o;
+ findInheritance(ctClass.getReference(), superclass);
+ }
+ }
+
+ public Map, Set>> getClassImplementors() {
+ return classImplementors;
+ }
+
+}
diff --git a/src/main/java/tum/dpid/services/processors/MethodCallChain.java b/src/main/java/tum/dpid/services/processors/MethodCallChain.java
new file mode 100644
index 0000000..660a8bf
--- /dev/null
+++ b/src/main/java/tum/dpid/services/processors/MethodCallChain.java
@@ -0,0 +1,73 @@
+package tum.dpid.services.processors;
+
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.reference.CtTypeReference;
+
+import java.util.*;
+
+public class MethodCallChain {
+
+ private final CtExecutableReference> executableReference;
+ private final Map , List >> callList;
+ private final Map , Set >> classHierarchy;
+
+ private MethodCallChain(CtExecutableReference> executableReference,
+ Map , List >> callList,
+ Map , Set >> classHierarchy) {
+ this.executableReference = executableReference;
+ this.callList = callList;
+ this.classHierarchy = classHierarchy;
+ }
+
+ public static List processMethod(CtMethod> methodName,
+ Map , List>> callList,
+ Map , Set >> classHierarchy) {
+ ArrayList result = new ArrayList<>();
+ for (CtExecutableReference> executableReference : findExecutablesForMethod(methodName, callList)) {
+ result.add(new MethodCallChain(executableReference, callList, classHierarchy));
+ }
+ return result;
+ }
+
+ static List > findExecutablesForMethod(CtMethod> methodName, Map , List >> callList) {
+ ArrayList > result = new ArrayList<>();
+ for (CtExecutableReference> executableReference : callList.keySet()) {
+ if (executableReference.equals(methodName.getReference())){
+ result.add(executableReference);
+ }
+ }
+ return result;
+ }
+
+ public void printCallChain() {
+ System.out.println("Method call hierarchy of " + executableReference + "");
+ printCallChain(executableReference, "\t", new HashSet >());
+ }
+
+ private void printCallChain(CtExecutableReference> method, String indents, Set > alreadyVisited) {
+ if (alreadyVisited.contains(method)) {
+ return;
+ }
+ alreadyVisited.add(method);
+ List > callListForMethod = callList.get(method);
+ if (callListForMethod == null) {
+ return;
+ }
+ for (CtExecutableReference> eachReference : callListForMethod) {
+ System.out.println(indents + eachReference.toString());
+
+ printCallChain(eachReference, indents.concat("\t"), alreadyVisited);
+ Set > subclasses = classHierarchy.get(eachReference.getDeclaringType());
+ if (subclasses != null) {
+ for (CtTypeReference> subclass : subclasses) {
+ CtExecutableReference> ctExecutableReference = eachReference.getOverridingExecutable(subclass);
+ if (ctExecutableReference != null) {
+ System.out.println(indents + "* " + ctExecutableReference.toString());
+ printCallChain( ctExecutableReference, indents.concat("\t"), alreadyVisited);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/tum/dpid/services/processors/MethodOrder.java b/src/main/java/tum/dpid/services/processors/MethodOrder.java
new file mode 100644
index 0000000..c6042a8
--- /dev/null
+++ b/src/main/java/tum/dpid/services/processors/MethodOrder.java
@@ -0,0 +1,38 @@
+package tum.dpid.services.processors;
+
+import spoon.processing.AbstractProcessor;
+import spoon.reflect.code.CtAbstractInvocation;
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.visitor.filter.AbstractFilter;
+import spoon.support.reflect.declaration.CtMethodImpl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MethodOrder extends AbstractProcessor> {
+ private Map, List>> callList = new HashMap<>();
+
+ @Override
+ public void process(CtMethodImpl ctMethod) {
+ List elements = ctMethod.getElements(new AbstractFilter(CtElement.class) {
+ @Override
+ public boolean matches(CtElement ctElement) {
+ return ctElement instanceof CtAbstractInvocation;
+ }
+ });
+ List> calls = new ArrayList<>();
+ for (CtElement element : elements) {
+ CtAbstractInvocation> invocation = (CtAbstractInvocation>) element;
+ calls.add(invocation.getExecutable());
+
+ }
+ callList.put(ctMethod.getReference(), calls);
+ }
+
+ public Map, List>> getCallList() {
+ return callList;
+ }
+}
diff --git a/src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java b/src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java
new file mode 100644
index 0000000..26f4771
--- /dev/null
+++ b/src/main/java/tum/dpid/services/v1/CallHierarchyProcessor.java
@@ -0,0 +1,39 @@
+package tum.dpid.services.v1;
+
+import spoon.reflect.code.CtInvocation;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.visitor.CtAbstractVisitor;
+
+import java.util.*;
+
+/*
+ Previous version of method call chain processor but it lacks of all. It only prints first degree of method call chain
+ */
+public class CallHierarchyProcessor extends CtAbstractVisitor {
+ private Map> callHierarchy = new HashMap<>();
+
+ /*This prints one degree of children*/
+ @Override
+ public void visitCtMethod(CtMethod method) {
+ String methodSignature = method.getDeclaringType().getQualifiedName() + "#" + method.getSignature();
+ Set calledMethods = new HashSet<>();
+ method.getElements(e -> e instanceof CtInvocation)
+ .forEach(e -> {
+ CtInvocation> invocation = (CtInvocation>) e;
+ CtExecutableReference> executable = invocation.getExecutable();
+ if (executable.getDeclaringType() != null) {
+ //System.out.println("CtExecutableReference is " + executable);
+ String calledMethodSignature = executable.getDeclaringType().getQualifiedName() + "#" + executable.getSignature();
+ calledMethods.add(calledMethodSignature);
+ }
+ });
+ callHierarchy.put(methodSignature, calledMethods);
+
+ }
+
+ public Map> getCallHierarchy() {
+ return callHierarchy;
+ }
+
+}
diff --git a/src/main/java/tum/dpid/services/v1/MethodCallTracer.java b/src/main/java/tum/dpid/services/v1/MethodCallTracer.java
new file mode 100644
index 0000000..ca9c582
--- /dev/null
+++ b/src/main/java/tum/dpid/services/v1/MethodCallTracer.java
@@ -0,0 +1,92 @@
+package tum.dpid.services.v1;
+
+import spoon.reflect.CtModel;
+import spoon.reflect.code.CtInvocation;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.visitor.filter.TypeFilter;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/*
+ Previous version of method call chain processor.
+ */
+public class MethodCallTracer {
+
+ public static Set> findMethodsCallingDatabaseMethods(CtModel model, List> allMethods, Set> databaseMethods) {
+ Set> callingMethods = new HashSet<>();
+
+ for (CtMethod> method : allMethods) {
+ for (CtExecutableReference> calledMethodRef : method.getElements(new TypeFilter<>(CtExecutableReference.class))) {
+ for (CtMethod> dbMethod : databaseMethods) {
+ if (calledMethodRef.getSimpleName().equals(dbMethod.getSimpleName()) &&
+ calledMethodRef.getDeclaringType().equals(dbMethod.getDeclaringType().getReference())) {
+ callingMethods.add(method);
+ }
+ }
+ }
+ }
+ return callingMethods;
+ }
+
+
+ public static Set traceMethodCalls(Set> databaseMethods, List> allMethods) {
+
+ Set callChains = new HashSet<>();
+ Set> visitedMethods = new HashSet<>();
+
+ for (CtMethod> dbMethod : databaseMethods) {
+ traceMethod(dbMethod, allMethods, "", callChains, visitedMethods);
+ }
+
+ return callChains;
+ }
+
+ private static void traceMethod(CtMethod> method, List> allMethods, String callChain, Set callChains, Set> visitedMethods) {
+ if (visitedMethods.contains(method)) {
+ return;
+ }
+ visitedMethods.add(method);
+
+ callChain = method.getSimpleName() + " #" + method.getDeclaringType().getQualifiedName() + " -> " + callChain;
+
+ for (CtMethod> caller : findCallingMethods(method, allMethods)) {
+ traceMethod(caller, allMethods, callChain, callChains, visitedMethods);
+ }
+ callChains.add(callChain);
+
+ }
+
+
+ private static Set> findCallingMethods(CtMethod> method, List> allMethods) {
+ Set> callingMethods = new HashSet<>();
+
+ for (CtMethod> candidateMethod : allMethods) {
+ for (CtInvocation> invocation : candidateMethod.getElements(new TypeFilter<>(CtInvocation.class))) {
+ CtExecutableReference> executableRef = invocation.getExecutable();
+ if (executableRef.getDeclaration() != null && executableRef.getDeclaration().equals(method)) {
+ callingMethods.add(candidateMethod);
+ }
+ }
+ }
+
+ return callingMethods;
+ }
+
+}
+
+//Runner Code Snippet
+// Set> callingMethods = MethodCallTracer.findMethodsCallingDatabaseMethods(model,allMethods, databaseMethods);
+// for (CtMethod> method : callingMethods) {
+// System.out.println("Method calling database: " + method.getSignature());
+// }
+
+//
+// //Method Call Tracer File
+// Set callChains = MethodCallTracer.traceMethodCalls(databaseMethods, allMethods);
+// // Print results
+// for (String callChain : callChains) {
+// System.out.println("Call chain: " + callChain);
+// }