Search code examples
javaabstract-syntax-treeeclipse-jdt

How to get all JDT AST Statement's at a given line number?


How can I get the org.eclipse.jdt.core.dom.Statement at a given line number?

Assume I have this class:

 1 package foo;
 2 public class Operations {
 3  public static int calc(int a, int b) {
 4      if (a >= b)
 5          return a - b;
 6      if (a<b || b==2)
 7          return -1;
 8      return 0;
 9  }
10 }

This is what i expect:

  • line 3 -> BlockStatement
  • line 4 -> IfStatement
  • line 5 -> ReturnStatement

This code works but it is a bit bulky because every body and statement must be visited in the worsed case. Is there a better/simpler way to do it?

List<Statement> getStatementsAtLine(final CompilationUnit cu, final int lineNumber) {
        final List<Statement> result = new ArrayList<Statement>();
        cu.accept(new ASTVisitor() {
            @Override
            public boolean visit(MethodDeclaration node) {
                Block body = node.getBody();

                for (Statement s : (List<Statement>) body.statements()) {
                    int l=cu.getLineNumber(s.getStartPosition());
                    if (l==lineNumber){
                        result.add(s);
                    }
                }
                return true;
            }
        });

        return result;
    }

Solution

  • You may want to convert the line number into a (character-) position using

    org.eclipse.jdt.core.dom.CompilationUnit.getPosition(int, int)
    

    With that offset you can use the org.eclipse.jdt.core.dom.NodeFinder to retrieve the ASTNode at that position, s.t. like:

    NodeFinder finder= new NodeFinder(cu, offset, 0);
    ASTNode node= finder.getCoveringNode();
    while (node != null && !(node instanceof Statement)) {
        node= node.getParent();
    }
    return node;
    

    EDIT: This approach, however, has a problem with white space, because leading or trailing white space characters are not covered by the statement being sought.

    I see two possible strategies to avoid this problem:

    1. Increment the offset until you are sure it is "within" the significant text of the line, or
    2. Apply the trick from a comment below: extend the region up-to the end-of-file and ask for the largest ASTNode completely covered by this region (using getCoveredNode() instead of getCoveringNode()).

    Strategy (1) requires to retrieve the text of the line in question via the IDocument, which may not be readily available. Strategy (2) may have the effect that a statement spanning several lines may be selected, not sure if this is against any requirement. If more precision is needed, calculate the length argument for getPosition() as the difference between the current line start and the next line start.