Search code examples
javaparsingline-numbers

How to get surrounding method in Java source file for a given line number


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.


Solution

  • 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);
            }
        }
    }