views:

297

answers:

3

I have a line number of a Java source file and want to get the sourounding method for that line number programatically.

I looked into ANTLR which didn't help me much.

Janino (http://www.janino.net) seems promising, I would scan and parse (and if necessary compile) the code. Then I could use JDI and

ReferenceType.locationsOfLine(int lineNumber)

Still I don't know how to use JDI for doing this and didn't find a tutorial that goes anywhere in this direction.

Maybe there is some other way that I am completely missing.

+3  A: 

You can use ASM's CodeVisitor to retrieve the debugging line information from a compiled class. This saves you the parsing of Java source files.

ClassReader reader = new ClassReader(A.class.getName());
reader.accept(new ClassVisitor() {
    public CodeVisitor visitMethod(int access, String name, String desc,
            String[] exceptions, Attribute attrs) {
        System.out.println(name);
        return new CodeVisitor() {
            public void visitLineNumber(int line, Label start) {
                System.out.println(line);
            }
        }
    }
}, false);

For class A:

11  class A {
12  
13    public void x() {
14        int x = 1;
15        System.out.println("Hello");
16    }
17
18    public void y() {
19        System.out.println("World!");
20    }
21 }

This produces:

<init>
11
x
14
15
16
y
19
20

If you need this information at runtime. You can create an exception and have a look at the stack trace elements.

Thomas Jung
Hello! Thank you for this good answer. This is almost what I was looking for. I really appreciate your help!
roesslerj
A: 

maybe you can throw an Exception, catch it, extract the corresponding stacktrace element to get the method name.

chburd
There's not need to parse the stack trace. The stack trace element (http://java.sun.com/javase/6/docs/api/java/lang/StackTraceElement.html) contains the information.
Thomas Jung
thanks for the information
chburd
Sorry for being imprecise. I have to get the method of a class that I am not using.
roesslerj
+3  A: 

If you're using Java 6, and if you don't mind using Sun's APIs, then you can use the javac API. You'll need to add tools.jar to your classpath.

import java.io.IOException;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LineMap;
import com.sun.source.tree.MethodTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;

public class MethodFinder {

    public static void main(String[] args) {
     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
     DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<JavaFileObject>();
     StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
     Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects("path/to/Source.java");
     CompilationTask task = compiler.getTask(null, fileManager, diagnosticsCollector, null, null, fileObjects);

     // Here we switch to Sun-specific APIs
     JavacTask javacTask = (JavacTask) task;
     SourcePositions sourcePositions = Trees.instance(javacTask).getSourcePositions();
     Iterable<? extends CompilationUnitTree> parseResult = null;
     try {
      parseResult = javacTask.parse();
     } catch (IOException e) {

      // Parsing failed
      e.printStackTrace();
      System.exit(0);
     }
     for (CompilationUnitTree compilationUnitTree : parseResult) {
      compilationUnitTree.accept(new MethodLineLogger(compilationUnitTree, sourcePositions), null);
     }
    }

    private static class MethodLineLogger extends TreeScanner<Void, Void> {
     private final CompilationUnitTree compilationUnitTree;
     private final SourcePositions sourcePositions;
     private final LineMap lineMap;

     private MethodLineLogger(CompilationUnitTree compilationUnitTree, SourcePositions sourcePositions) {
      this.compilationUnitTree = compilationUnitTree;
      this.sourcePositions = sourcePositions;
      this.lineMap = compilationUnitTree.getLineMap();
     }

     @Override
     public Void visitMethod(MethodTree arg0, Void arg1) {
      long startPosition = sourcePositions.getStartPosition(compilationUnitTree, arg0);
      long startLine = lineMap.getLineNumber(startPosition);
      long endPosition = sourcePositions.getEndPosition(compilationUnitTree, arg0);
      long endLine = lineMap.getLineNumber(endPosition);

      // Voila!
      System.out.println("Found method " + arg0.getName() + " from line " + startLine + " to line "  + endLine + ".");

      return super.visitMethod(arg0, arg1);
     }
    }
}
Eli Acherkan
Sorry for answering late - no Internet connection over the weekend...THIS IS ONE HELL OF A SOLUTION! Exactly what I was looking for!Thank you so much, you just made my day!
roesslerj
My pleasure! It was a challenging question, I had a lot of fun helping you out.
Eli Acherkan