Patze
Patze

Reputation: 307

Remove LineComment in Java AST

I´m trying to remove a LineComment from a java file via AST. I read the document from a source file, create an AST parser (AST.JLS3) and afterwards create a CompilationUnit and an ASTRewrite instance.

doc = new Document( doctext );
parser = ASTParser.newParser( AST.JLS3 );
parser.setSource( doc.get().toCharArray() );
cu = (CompilationUnit) parser.createAST( null );
astRewrite = ASTRewrite.create( cu.getAST() );

Nothing special so far, I´m able to add and remove fields a.s.o. Now I´m trying to remove comments from the unit with the following code:

@SuppressWarnings( "unchecked" )
final List<Comment> comments = (List<Comment>) cu.getCommentList();
final Iterator<Comment> commentIter = comments.iterator();
while ( commentIter.hasNext() ) {
  final Comment curComment = commentIter.next();
  if ( curComment.isLineComment() ) {
    final LineComment lineComment = (LineComment) curComment;
    lineComment.accept( new CommentCopyrightFieldVisitor( cu, document.get(), astRewrite ) );
  }
}

Here´s the visitor that should perform the action and remove the comment.

public class CommentFieldVisitor extends ASTVisitor {

  final CompilationUnit cu;

  final String sourceCode;

  final ASTRewrite astRewrite;

  public CommentFieldVisitor( final CompilationUnit cu, final String sourceCode, final ASTRewrite astRewrite ) {
    this.cu = cu;
    this.sourceCode = sourceCode;
    this.astRewrite = astRewrite;
  }

  @Override
  public boolean visit( final LineComment commentNode ) {
    int start = commentNode.getStartPosition();
    int end = start + commentNode.getLength();
    final String comment = sourceCode.substring( start, end );
    final String fieldComment = Config.INSTANCE.getTargetFieldComment();

    if ( comment != null && comment.equalsIgnoreCase( fieldComment ) ) {
      System.out.println( "REMOVE COMMENT" );
      assert astRewrite != null : "ERROR: AST Rewriter is null";
      astRewrite.remove( commentNode, null );
    }
    return false;
  }
}

I iterate all comments in the compilation unit, I create a visitor for every comment in the list. This visitor checks, if the content of the comment equals a preconfigured string. If it does, it should be removed. Though if I call

astRewrite.remove( commentNode, null );

I always get a NPE from inside the remove method. astRewrite and commentNode are defined (because the remove-code is reached.

Does anyone have an idea, what I might be doing wrong? Or another approach how to remove such a comment via AST?

Upvotes: 2

Views: 727

Answers (2)

Sybuser
Sybuser

Reputation: 1374

A tiny bit late here but I'm sure the answer will help other people.

I can confirm the NPE with remove method when it lacks the required context of a ICompilationUnit to execute. This results in :

REMOVE COMMENT
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.core.dom.StructuralPropertyDescriptor.isChildListProperty()" because "property" is null
    at org.eclipse.jdt.core.dom.rewrite.ASTRewrite.remove(ASTRewrite.java:398)

Here's how I do when I don't have such context:

String sourceCode = "/* block comment */ public class Hello {} // line comment";
Document doc = new Document(sourceCode);
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
parser.setSource(doc.get().toCharArray());
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
ASTRewrite astRewrite = ASTRewrite.create(cu.getAST());
TextEdit edits = astRewrite.rewriteAST(doc, null);
final List<Comment> comments = cu.getCommentList();
List<TextEdit> textEdits = new ArrayList<>();
for (Comment curComment : comments) {
    if (curComment.isLineComment()) {
        final LineComment lineComment = (LineComment) curComment;
        int commentStart = lineComment.getStartPosition();
        int commentLength = lineComment.getLength();
        int commentEnd = commentStart + commentLength;
        String comment = sourceCode.substring(commentStart, commentEnd);
        if (comment != null && comment.equalsIgnoreCase("// line comment")) {
            textEdits.add(new DeleteEdit(commentStart, commentLength));
        }
    }
}
edits.addChildren(textEdits.toArray(TextEdit[]::new));
try {
    edits.apply(doc);
    System.out.println(doc.get());
} catch (MalformedTreeException | BadLocationException e) {
    e.printStackTrace();
}

See also this project https://github.com/JnRouvignac/AutoRefactor and especially these 2 classes below:

  • ASTCommentRewriter.java for the solution above with the list of DeleteEdit
  • CommentsCleanUp.java for the usage of ASTRewrite.remove()

Upvotes: 0

Patze
Patze

Reputation: 307

I managed it via a workaround which uses Comment.getStartPosition() and Comment.getLength(). I use these methods to exract the comments from my source code file and replace them with "". After that I need to re-create the AST tree from the modified source code. This is far away from perfect, but I didn´t find an alternative solution.

Upvotes: 1

Related Questions