This commit is contained in:
2025-07-16 19:05:57 +02:00
parent d8bc3bd069
commit 285b82c251
10 changed files with 0 additions and 382 deletions

View File

@@ -51,111 +51,3 @@ public class DynamicStateMachineExample {
}
```
1. define how java function can be implemented
1.2. for this analyse bytecode manipulation such as project lombok
2. get a diagram
3. get a diagram with slicing, for example, slice by setValue on an ENUM field -> identify saga
1. make a list of all messages on message queues, how they're remapped and then who receives them
NOTE
```java
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;
public class MethodCallGraphGenerator {
static class MethodNode {
String className;
String methodName;
public MethodNode(String className, String methodName) {
this.className = className;
this.methodName = methodName;
}
@Override
public String toString() {
return className + "." + methodName;
}
@Override
public int hashCode() {
return Objects.hash(className, methodName);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MethodNode other) {
return this.className.equals(other.className) && this.methodName.equals(other.methodName);
}
return false;
}
}
static Map<MethodNode, Set<MethodNode>> callGraph = new HashMap<>();
public static void main(String[] args) throws Exception {
File sourceDir = new File("src"); // Change to your Java source directory
parseDirectory(sourceDir);
generateDotFile("callgraph.dot");
System.out.println("Call graph generated to callgraph.dot");
}
private static void parseDirectory(File dir) throws Exception {
for (File file : Objects.requireNonNull(dir.listFiles())) {
if (file.isDirectory()) {
parseDirectory(file);
} else if (file.getName().endsWith(".java")) {
parseFile(file);
}
}
}
private static void parseFile(File file) throws Exception {
CompilationUnit cu = StaticJavaParser.parse(new FileInputStream(file));
cu.findAll(MethodDeclaration.class).forEach(method -> {
String className = cu.getPrimaryTypeName().orElse("Unknown");
MethodNode caller = new MethodNode(className, method.getNameAsString());
callGraph.putIfAbsent(caller, new HashSet<>());
method.accept(new VoidVisitorAdapter<Void>() {
@Override
public void visit(MethodCallExpr call, Void arg) {
super.visit(call, arg);
String calleeName = call.getNameAsString();
MethodNode callee = new MethodNode("Unknown", calleeName); // Could be refined with better context
callGraph.get(caller).add(callee);
}
}, null);
});
}
private static void generateDotFile(String filename) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("digraph G {\n");
for (var caller : callGraph.keySet()) {
for (var callee : callGraph.get(caller)) {
sb.append(" \"").append(caller).append("\" -> \"").append(callee).append("\";\n");
}
}
sb.append("}");
java.nio.file.Files.writeString(new File(filename).toPath(), sb.toString());
}
}
```

View File

@@ -1,165 +0,0 @@
package click.kamil.callgraphexporter;
import click.kamil.callgraphexporter.domain.MethodMetadata;
import click.kamil.callgraphexporter.domain.MethodSignature;
import click.kamil.callgraphexporter.domain.Modifiers;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
public class JdtResolvedCallGraph {
// Use signature as the unique key
static Map<MethodSignature, Set<MethodSignature>> callGraph = new HashMap<>();
// Optionally store metadata
static Map<MethodSignature, MethodMetadata> metadataMap = new HashMap<>();
public static void main(String[] args) throws IOException {
File sourceDir = new File("src/main/java/click/kamil/callgraphexporter/simple");
List<File> javaFiles = new ArrayList<>();
collectJavaFiles(sourceDir, javaFiles);
for (File file : javaFiles) {
parseFileWithBinding(file, sourceDir);
}
generateDotFile("callgraph.dot");
System.out.println("Call graph saved to callgraph.dot");
}
private static void collectJavaFiles(File dir, List<File> javaFiles) {
for (File file : Objects.requireNonNull(dir.listFiles())) {
if (file.isDirectory()) {
collectJavaFiles(file, javaFiles);
} else if (file.getName().endsWith(".java")) {
javaFiles.add(file);
}
}
}
private static void parseFileWithBinding(File file, File sourceRoot) throws IOException {
String source = Files.readString(file.toPath());
ASTParser parser = ASTParser.newParser(AST.JLS21);
parser.setSource(source.toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setEnvironment(
null,
new String[]{sourceRoot.getAbsolutePath()},
null,
true
);
parser.setUnitName(file.getName());
Map<String, String> options = JavaCore.getOptions();
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_17);
parser.setCompilerOptions(options);
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
cu.accept(new ASTVisitor() {
@Override
public boolean visit(MethodDeclaration method) {
IMethodBinding binding = method.resolveBinding();
if (binding == null) return super.visit(method);
MethodSignature callerSig = toSignature(binding);
MethodMetadata callerMeta = toMetadata(binding, method);
metadataMap.put(callerSig, callerMeta);
callGraph.putIfAbsent(callerSig, new HashSet<>());
method.accept(new ASTVisitor() {
@Override
public boolean visit(MethodInvocation node) {
IMethodBinding calleeBinding = node.resolveMethodBinding();
if (calleeBinding != null) {
MethodSignature calleeSig = toSignature(calleeBinding);
callGraph.get(callerSig).add(calleeSig);
// You can collect metadata for callees too if needed
}
return super.visit(node);
}
});
return super.visit(method);
}
});
}
// Fixed: matches your MethodSignature record with only methodName and paramTypes
private static MethodSignature toSignature(IMethodBinding binding) {
String methodName = binding.getName();
List<String> paramTypes = new ArrayList<>();
for (ITypeBinding param : binding.getParameterTypes()) {
paramTypes.add(param.getName());
}
return new MethodSignature(methodName, paramTypes);
}
private static MethodMetadata toMetadata(IMethodBinding binding, MethodDeclaration method) {
String returnType = binding.getReturnType().getName();
Set<String> exceptions = new HashSet<>();
for (ITypeBinding ex : binding.getExceptionTypes()) {
exceptions.add(ex.getName());
}
Set<Modifiers> modifiers = extractModifiers(binding, method);
List<String> annotations = new ArrayList<>();
return new MethodMetadata(returnType, exceptions, modifiers, annotations, null);
}
private static void generateDotFile(String filename) throws IOException {
StringBuilder sb = new StringBuilder("digraph G {\n");
for (MethodSignature caller : callGraph.keySet()) {
String callerLabel = formatSignature(caller);
for (MethodSignature callee : callGraph.get(caller)) {
String calleeLabel = formatSignature(callee);
sb.append(" \"").append(callerLabel).append("\" -> \"").append(calleeLabel).append("\";\n");
}
}
sb.append("}");
Files.writeString(new File(filename).toPath(), sb.toString());
}
private static String formatSignature(MethodSignature sig) {
// Build a readable label (methodName + param list)
return sig.methodName() + sig.parameterTypes();
}
private static Set<Modifiers> extractModifiers(IMethodBinding binding, MethodDeclaration method) {
Set<Modifiers> result = new HashSet<>();
int mods = binding.getModifiers();
if (Modifier.isPublic(mods)) result.add(Modifiers.PUBLIC);
if (Modifier.isPrivate(mods)) result.add(Modifiers.PRIVATE);
if (Modifier.isProtected(mods)) result.add(Modifiers.PROTECTED);
if (Modifier.isStatic(mods)) result.add(Modifiers.STATIC);
if (Modifier.isAbstract(mods)) result.add(Modifiers.ABSTRACT);
if (Modifier.isFinal(mods)) result.add(Modifiers.FINAL);
if (Modifier.isNative(mods)) result.add(Modifiers.NATIVE);
if (Modifier.isSynchronized(mods)) result.add(Modifiers.SYNCHRONIZED);
if (Modifier.isStrictfp(mods)) result.add(Modifiers.STRICTFP);
for (Object mod : method.modifiers()) {
if (mod instanceof Modifier keyword) {
if (keyword.isDefault()) result.add(Modifiers.DEFAULT);
}
}
return result;
}
}

View File

@@ -1,15 +0,0 @@
package click.kamil.callgraphexporter.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Method {
private MethodSignature signature;
private MethodMetadata metadata;
}

View File

@@ -1,12 +0,0 @@
package click.kamil.callgraphexporter.domain;
import java.util.List;
import java.util.Set;
public record MethodMetadata(
String returnType,
Set<String> thrownExceptions,
Set<Modifiers> modifiers,
List<String> annotations,
String documentationComment // Optional: Javadoc
) {}

View File

@@ -1,9 +0,0 @@
package click.kamil.callgraphexporter.domain;
import java.util.List;
public record MethodSignature(
String methodName,
List<String> parameterTypes
) {
}

View File

@@ -1,18 +0,0 @@
package click.kamil.callgraphexporter.domain;
public enum Modifiers {
PUBLIC,
PROTECTED,
PRIVATE,
STATIC,
ABSTRACT,
FINAL,
NATIVE,
SYNCHRONIZED,
TRANSIENT,
VOLATILE,
STRICTFP,
DEFAULT,
SEALED,
NON_SEALED,
}

View File

@@ -1,26 +0,0 @@
package click.kamil.callgraphexporter.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TypeGroup {
private String packageName;
private String typeName;
private TypeKind typeKind;
private EnumSet<Modifiers> modifiers = EnumSet.noneOf(Modifiers.class);
private List<Method> methods = new ArrayList<>();
public void addMethod(Method method) {
methods.add(method);
}
}

View File

@@ -1,9 +0,0 @@
package click.kamil.callgraphexporter.domain;
public enum TypeKind {
CLASS,
INTERFACE,
RECORD,
ENUM,
ANNOTATION
}

View File

@@ -1,12 +0,0 @@
package click.kamil.callgraphexporter.simple;
public class Kamil {
void a(){
b();
}
void b(){
c();
}
void c(){}
}

View File

@@ -1,8 +0,0 @@
package click.kamil.callgraphexporter.simple;
public class Kamil2 {
void ka() {
new Kamil().b();
}
}