I have read the JavaParser manual and started to build my own examples. What I intend to achieve, is to read Java code and insert new lines of code on it. Specifically, I want to initialise a counter before every if
and while
statement, and inside the body of the statement to increment the counter. My purpose for doing this, is to run the new code for a specified set of runs and observe how many times each branch is executed. I am using JavaParser to parse and add code, because I want to generate and run everything automatically.
For example, we have the following simple piece of code:
if (x>4) {
x=4;
}
else {
x=4;
}
while (i<x) {
i++;
}
And after the parsing I would like to have something like the following:
int countA=0;
int countB=0;
if (x>4) {
x=4;
countA++;
}
else {
x=4;
countB++;
}
int countC=0;
while (i<x) {
i++;
countC++;
}
My code so far:
public static void main (String[] args) throws Exception {
CompilationUnit cu = StaticJavaParser.parse("class X{void x(){" +
" if (x>4) { " +
" x=4; " +
" } " +
" else { " +
" x=4; " +
" } " +
" while (i<x) { " +
" i++; " +
" } " +
" }}");
cu.findAll(Statement.class).forEach(statement -> {
if (statement.isIfStmt()) {
//System.out.println(statement.getChildNodes().get(0));
// System.out.println(statement.getParentNodeForChildren().toString());
// statement.getChildNodes().add(statement.getChildNodes().get(0));
// System.out.println(statement.toString());
}
if (statement.isWhileStmt()) {
// System.out.println();
}
});
System.out.println(cu);
}
}
I have tried some things (the commended out lines) but unsuccessfully. My main problem is that I cannot find a way to inject any line of code at the end of the childNodes
of the given statement
which supposedly would be the increment of the counter or at the end of the parentNode
which would be the initialisation. Is there any way to achieve what I am describing?
The problem in your code is the following:
if (statement.isIfStmt()) {
The condition clearly state that you are dealing with an if statement and thus you cannot actually add an instruction. What you need to do is to get the n+1 element (where n is the index of the if statement) and then use:
cu.findAll(Statement.class).get(2).asBlockStmt().addStatement("countA++;")
There are several ways to define the expression to be added, here I'm using a plain string for clarity but check the working example for actual syntax.
For the while you can use the same technique but it's going to be more complicated. For simplicity the declarations of your counters will be added at the beginning of the method using the following approach:
((BlockStmt)cu.findAll(Statement.class).get(6).getParentNode().get()).addStatement(0, new StringLiteralExpr("int b = 1;"));
Exaplanation
I reproduced your code making a working JUnit 5 and this is the result I got
public class ParserTest {
String counterName = "count";
Integer counterIndex = 1;
@Test
public void main() {
CompilationUnit cu = StaticJavaParser.parse("class X{void x(){" + " if (x>4) { " + " x=4; "
+ " } " + " else { " + " x=4; " + " } " + " while (i<x) { " + " i++; "
+ " } " + " }}");
cu.findAll(Statement.class).forEach(statement -> {
if (statement.isIfStmt()) {
// Add counter declaration in main block for the if
String counterNameAndIndexIf = counterName + counterIndex++;
// Create expression using StaticJavaParser
Expression ifCounterExpression = StaticJavaParser
.parseVariableDeclarationExpr(" int " + counterNameAndIndexIf + " = 0");
((BlockStmt) statement.getParentNode().get()).addStatement(0, ifCounterExpression);
// Add counter declaration in main block for the else
String counterNameAndIndexElse = counterName + counterIndex++;
// Create expression using StaticJavaParser
Expression elseCounterExpression = StaticJavaParser
.parseVariableDeclarationExpr("int " + counterNameAndIndexElse + " = 0");
((BlockStmt) statement.getParentNode().get()).addStatement(0, elseCounterExpression);
// Add if increment
Expression ifIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexIf + "++");
((BlockStmt) statement.getChildNodes().get(1)).addStatement(ifIncrementExpression);
// Add else increment
Expression elseIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexElse + "++");
((BlockStmt) statement.getChildNodes().get(2)).addStatement(elseIncrementExpression);
}
if (statement.isWhileStmt()) {
String counterNameAndIndexWhile = counterName + counterIndex++;
Expression whileCounterExpression = StaticJavaParser
.parseVariableDeclarationExpr(" int " + counterNameAndIndexWhile + " = 0");
((BlockStmt) statement.getParentNode().get()).addStatement(0, whileCounterExpression);
// Add while increment
Expression whileIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexWhile + "++");
((BlockStmt) statement.getChildNodes().get(1)).addStatement(whileIncrementExpression);
}
});
System.out.println(cu);
}
And the result is
class X {
void x() {
int count3 = 0;
int count2 = 0;
int count1 = 0;
if (x > 4) {
x = 4;
count1++;
} else {
x = 4;
count2++;
}
while (i < x) {
i++;
count3++;
}
}
Hope this helps. Let me know if you need clarifications on how to build any of these objects. There are for sure smarter ways and code can be obviously refactored but I wanted to have it laid down as plainly as possible for you.
Cheers
EDIT: to support statement nesting and declaring the all the counter variables in the main block of the method the following can be used
statement.findRootNode().getChildNodes().get(0).getChildNodes().get(1).getChildNodes().get(2)
This approach will start top down from the root and will always point to the main block. Use it to add your counter variables.