2009-12-18 8 views
8

Tengo un número de línea de un archivo fuente Java y quiero obtener el método sourounding para ese número de línea programáticamente.Cómo obtener el método circundante en el archivo fuente Java para un número de línea dado

Miré en ANTLR que no me ayudó mucho.

Janino (http://www.janino.net) parece prometedor, podría escanear y analizar (y si es necesario compilar) el código. Entonces podría utilizar JDI y

ReferenceType.locationsOfLine(int lineNumber) 

Aún no sé cómo utilizar JDI para hacer esto y no encontrar un tutorial que va a ninguna parte en esta dirección.

Quizás haya alguna otra manera que me falta completamente.

+0

¿Estás hablando de la misma manera que dif -U will? p.ej. @@ -3452,6 +3452,59 @@ void MyMethod (int a, int b) – Pod

+0

¿Desea el nombre del método o el texto del método? –

+0

El nombre del método sería suficiente, pero el problema se resolvió durante mucho tiempo. Gracias de todos modos :-) – roesslerj

Respuesta

11

Si está utilizando Java 6, y si no le molesta usar las API de Sun, puede usar the javac API. Tendrá que agregar tools.jar a su 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); 
     } 
    } 
} 
3

Puede usar ASM's CodeVisitor a retrieve the debugging line information de una clase compilada. Esto le ahorra el análisis de los archivos fuente de Java.

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

Para la clase 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 } 

Esto produce:

<init> 
11 
x 
14 
15 
16 
y 
19 
20 

Si necesita esta información en tiempo de ejecución. Puede crear una excepción y echar un vistazo al stack trace elements.

+0

¡Hola! Gracias por esta buena respuesta. Esto es casi lo que estaba buscando. ¡Realmente aprecio tu ayuda! – roesslerj

0

quizás puedas lanzar una excepción, atraparla, extraer el elemento stacktrace correspondiente para obtener el nombre del método.

+1

No es necesario analizar el seguimiento de la pila. El elemento traza de pila (http://java.sun.com/javase/6/docs/api/java/lang/StackTraceElement.html) contiene la información. –

+0

gracias por la información – chburd

+0

Lo siento por ser impreciso. Tengo que obtener el método de una clase que no estoy usando. – roesslerj

Cuestiones relacionadas