I want to print all field access list for each method of a class in Java with JavaParser Library (3.25.8).
I try this:
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.FieldAccessExpr;
import java.io.File;
import java.io.IOException;
public class FieldAccessList {
public static void main(String[] args) throws IOException {
File sourceFile = new File("Example.java");
CompilationUnit cu = StaticJavaParser.parse(sourceFile);
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(classDeclaration -> {
System.out.println("Class: " + classDeclaration.getNameAsString());
classDeclaration.findAll(MethodDeclaration.class).forEach(methodDeclaration -> {
System.out.println(" Method: " + methodDeclaration.getNameAsString());
methodDeclaration.findAll(FieldAccessExpr.class).forEach(fieldAccessExpr -> {
System.out.println(" Field Access: " + fieldAccessExpr.getNameAsString());
});
});
});
}
}
and my pom.xml is:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Sahand</groupId>
<artifactId>Importance</artifactId>
<version>2.0</version>
<name>Sahand Project Extension</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.25.8</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-symbol-solver-core</artifactId>
<version>3.25.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
for Example.java:
public class Example {
private int field1;
private String field2;
public void method1() {
field1 = 10;
System.out.println(field2);
}
public void method2() {
field2 = "Hello";
}
}
The output I expected should be:
Class: Example
Method: method1
Field Access: field1
Field Access: field2
Method: method2
Field Access: field2
But the output is:
Class: Example
Method: method1
Field Access: out
Method: method2
The javadoc of FieldAccessExpr
says it is meant for detecting accesses of the type person.name
, which is probably why it detected System.out
. Through some trial and error, I figured out that the expression field1 = 10;
is of type AssignExpr
and that System.out.println(field2);
is of type MethodCallExpr
. A ++
or --
expression is of type UnaryExpr
. Also to filter out expressions that only use local variables, I collected all of the class level fields at the start and included only expressions that involved any of those fields. Combining all of that together, for the Example
class, I'm able to get the expected output using below code:
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.UnaryExpr;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class TestUtil {
public static void listFieldAccess() throws FileNotFoundException {
File sourceFile = new File("Example.java");
CompilationUnit cu = StaticJavaParser.parse(sourceFile);
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(classDeclaration -> {
System.out.println("Class: " + classDeclaration.getNameAsString());
List<String> fields = new ArrayList<>();
// Find all field names
classDeclaration.findAll(FieldDeclaration.class).forEach(fieldDeclaration -> {
fieldDeclaration.getVariables().forEach(variable -> {
fields.add(variable.getNameAsString());
});
});
classDeclaration.findAll(MethodDeclaration.class).forEach(methodDeclaration -> {
System.out.println(" Method: " + methodDeclaration.getNameAsString());
methodDeclaration.findAll(Expression.class).forEach(expression -> {
// Process only specific types of expressions
if (expression instanceof MethodCallExpr || expression instanceof AssignExpr ||
expression instanceof UnaryExpr) {
// Check if any of the expression fields match the class level fields
List<String> matchedFields = fields.stream().filter(field -> {
return expression.getChildNodes().stream().anyMatch((node) -> node.toString().contains(field));
}).collect(Collectors.toList());
System.out.println("Field access: " + matchedFields);
}
});
});
});
}
}
Couldn't figure out exactly how to differentiate between a read and write access as something like a method call could read or write internally. Also you might still need to include more expression types and add some filtering, to include only fields within the desired class.