8

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
  • Are you talking about the same way diff -U will? e.g. @@ -3452,6 +3452,59 @@ void MyMethod(int a, int b) Commented Dec 18, 2009 at 14:11
  • You want the method name, or the text of the method? Commented Jul 23, 2015 at 21:27
  • Method name would suffice, but problem is long solved. Thanks anyway :-) Commented Jul 23, 2015 at 21:34

3 Answers 3

12

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);
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

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.

1 Comment

Hello! Thank you for this good answer. This is almost what I was looking for. I really appreciate your help!
0

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

2 Comments

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.