Achtung! Nur gültig in für JDK 6, in JDK 7 gibt es Umbenennungen.
Die langtools von Java sind die bekannten Kommandozeilenprogramme wie javac, jar, javap, … Die Quellen kann man sich zum Beispiel aus dem Mercurial Repository holen. Für Java 6 liefert http://hg.openjdk.java.net/jdk6/jdk6/langtools/ den Zugriff auf die Sourcen. Sie lassen sich auch in einem Rutsch (zip) beziehen. Im Verzeichis src/share/classes beginnen die Pakete:
- com.sun.javadoc. Die Java Doclet-API, also nur Schnittstellen etwa um Pakete, Variablen oder Typen zu beschreiben
- com.sun.mirror. Mirror API repräsentiert Konstrukte eines Java-Programms wie Klassen, Modifizierer. Anweisungen und Ausdrücke werden nicht repräsentiert. Wird in Java 7 verschwinden, denn dort gibt es mit javax.lang.model (seit Java 6) einen Ersatz. Die Mirror API wurde in Java 5 für das APT eingeführt, aber nie standardisiert
- com.sun.source.tree. Repräsentiert den AST (abstract syntax trees) eines Java-Programms vom Java-Compiler. com.sun.source.util. Utility-Klassen für den AST
- javax.annotation.processing. Um Annotation-Prozessoren zu beschreiben
- javax.lang.model.Element, javax.lang.model.type, javax.lang.model.util. Deklarationen, für Typen, die Meta-Daten aus einem Java-Programm beschreiben, etwa Modifizierer oder Generics-Typen. In erster Linie für APT
- com.sun.tools.apt.*. Annotation Processing Tool (apt). Greift auf javax.lang.model zurück
- com.sun.tools.doclets.internal.toolkit. Implementierung des Standard-Doclets
- com.sun.tools.javadoc. Implementierung des Schnittstellen aus com.sun.javadoc
- com.sun.tools.javac.*. Java-Compiler
- com.sun.tools.javah. C Header and Stub File Generator
- sun.tools.javap. Der Java Class File Disassembler.
Wenn man ein Beispiel programmieren möchte, muss man tools.jar in den Klassenpfad aufnehmen. Dann kann man schon loslegen. So nutzt folgendes Programm die Datenstrukturen von javap, und ist somit der Java-Quellcode-Gegenspieler von Reflection.
import java.io.*; import sun.tools.javap.*; public class MyJavap { static String filename = "bin/MyJavap.class"; public static void main( String[] args ) throws FileNotFoundException { ClassData classData = new ClassData( new FileInputStream( filename ) ); for ( FieldData field : classData.getFields() ) System.out.println( field.getType() + " " + field.getName() ); for ( MethodData method : classData.getMethods() ) System.out.println( method.getName() + method.getParameters() ); } }
Ausgabe:
java.lang.String filename <clinit>() <init>() main(java.lang.String[])
Die API um Programmcode zu beschreiben ist komplizierter. Folgendes Programm baut einen internen Baum auf und lässt ihn über die komfortablen print()-Funktionen ausgeben:
import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.TypeTags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Name.Table; public class BuildSomeCode { public static void main( String[] args ) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); StandardJavaFileManager fm = compiler.getStandardFileManager( diagnostics, null, null ); Context context = new Context(); context.put( JavaFileManager.class, fm ); TreeMaker maker = TreeMaker.instance( context ); Table table = new Table(); ListBuffer<JCTree> methods = new ListBuffer<JCTree>(); JCModifiers mmods = maker.Modifiers( Flags.PUBLIC | Flags.STATIC ); Name mname = Name.fromString( table, "test" ); JCExpression retType = maker.TypeIdent( TypeTags.VOID ); ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); ListBuffer<JCStatement> stmts = new ListBuffer<JCStatement>(); JCBlock methodBody = maker.Block( 0, stmts.toList() ); methods.append( maker.MethodDef( mmods, mname, retType, List.<JCTypeParameter> nil(), params.toList(), List.<JCExpression> nil(), methodBody, null ) ); JCModifiers cmods = maker.Modifiers( Flags.PUBLIC ); Name cname = Name.fromString( table, "Test" ); JCClassDecl classDef = maker.ClassDef( cmods, cname, List.<JCTypeParameter> nil(), null, List.<JCExpression> nil(), methods.toList() ); ListBuffer<JCTree> classDefs = new ListBuffer<JCTree>(); classDefs.append( classDef ); JCCompilationUnit compilationUnit = maker.TopLevel( List.<JCAnnotation> nil(), null, classDefs.toList() ); System.out.println( compilationUnit ); } }
Das ergibt:
public class Test { public static void test() { } }
Zum Weiterlesen:
- http://javadots.blogspot.com/2006/11/javac-internals.html
- http://www.ahristov.com/tutorial/java-compiler.html
- http://www.cs.ucla.edu/~smarkstr/javacop/docs/ASTQuickReference.pdf
- http://blogs.sun.com/yj/entry/yet_another_way_to_hack