Como obter o método circundante no arquivo de origem Java para um determinado número de linha
-
20-09-2019 - |
Pergunta
Eu tenho um número de linha de um arquivo de origem Java e quero obter o método azedo para esse número de linha programaticamente.
Eu olhei para Antlr o que não me ajudou muito.
Janino (http://www.janino.net) parece promissor, eu escaneava e analisava (e, se necessário, compilaria) o código. Então eu poderia usar JDI e
ReferenceType.locationsOfLine(int lineNumber)
Ainda não sei como usar o JDI para fazer isso e não encontrei um tutorial que vai a lugar nenhum nessa direção.
Talvez haja alguma outra maneira que estou completamente perdendo.
Solução
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);
}
}
}
Outras dicas
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.