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.
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);
}
}
}
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.
maybe you can throw an Exception, catch it, extract the corresponding stacktrace element to get the method name.