How to write new methods body while using LexicalPreservingPrinter?

78 Views Asked by At

I want to add a new method to an existing class. I want to preserve the formatting of the existing code.

I use com.github.javaparser:javaparser-core:3.25.5. But if I use LexicalPreservingPrinter, there is no method body for the new method.

The following code is inspired by How to use JavaParser to add new methods to a parsing file?.

Example (see the comments at the end):

    final CompilationUnit cu = StaticJavaParser.parse(Paths.get("path-of-an-existing-class.java"));

    LexicalPreservingPrinter.setup(cu);

    final ClassOrInterfaceDeclaration type = cu.addClass("GeneratedClass");
    final MethodDeclaration method = type.addMethod("main", Keyword.PUBLIC, Keyword.STATIC);
    final BlockStmt block = new BlockStmt();
    method.setBody(block);
    final NameExpr clazz = new NameExpr("System");
    final FieldAccessExpr field = new FieldAccessExpr(clazz, "out");
    final MethodCallExpr call = new MethodCallExpr(field, "println");
    call.addArgument(new StringLiteralExpr("Hello World!"));
    block.addStatement(call);

    // If the line "LexicalPreservingPrinter.setup(...)" is deactivated: Boths numbers are equal.
    // If it is activated: The numbers differ.

    System.out.println(StringUtils.countMatches(new DefaultPrettyPrinter().print(cu), "Hello"));
    System.out.println(StringUtils.countMatches(LexicalPreservingPrinter.print(cu), "Hello"));

Is there a bug in my code?

2

There are 2 best solutions below

0
Shear Plane On

Not exactly the same, but it works for me.

    final CompilationUnit cu = StaticJavaParser.parse(Paths.get("path-of-an-existing-class.java"));

    LexicalPreservingPrinter.setup(cu);

    final TypeDeclaration<?> parseTypeDeclaration =
        StaticJavaParser.parseTypeDeclaration(
            "public static class GeneratedClass { "
                + "public static void main() {System.out.println(\"Hello World!\");}"
                + "}");
    cu.addType(parseTypeDeclaration);

    // If the line "LexicalPreservingPrinter.setup(...)" is deactivated: Boths numbers are equal.
    // If it is activated: The numbers are equal too :)

    System.out.println(StringUtils.countMatches(new DefaultPrettyPrinter().print(cu), "Hello"));
    System.out.println(StringUtils.countMatches(LexicalPreservingPrinter.print(cu), "Hello"));

It seems that StaticJavaParser.parse... sets the range fields in all the created objects to some "good" value. The other approachs sets null.

Thanks for reading :)

0
jpl On

What you see is linked to the functioning of the LPP. Generally speaking, each time you modify an AST node, the LPP is triggered to adjust the rendering of the node and its children. But when the node is not attached to the AST the LPP cannot prepare the rendering. To resolve your issue simply attach the new block to the AST when it is fully initialized.

final ClassOrInterfaceDeclaration type = cu.addClass("GeneratedClass");
final MethodDeclaration method = type.addMethod("main", Keyword.PUBLIC, Keyword.STATIC);
final BlockStmt block = new BlockStmt();
final NameExpr clazz = new NameExpr("System");
final FieldAccessExpr field = new FieldAccessExpr(clazz, "out");
final MethodCallExpr call = new MethodCallExpr(field, "println");
call.addArgument(new StringLiteralExpr("Hello World!"));
block.addStatement(call);
method.setBody(block); // <-- here the block is fully initialized