23.3 Programmieren mit der Tools-API
Tools wie der Java-Compiler javac, aber auch jarsigner, javadoc, apt, xjc, javap und andere Kommandozeilenwerkzeuge, sind rein in Java geschrieben. Einige Tools bieten Entwicklern eine API, sodass sie erweitert und von Programmen angesprochen werden können. Das ist interessant für Tool-Anbieter, die zum Beispiel den Compiler erweitern oder die Ausgabe vom JavaDoc-Tool anpassen möchten.
Alle in Java geschriebenen Programme befinden sich in einem Extra-Java-Archiv mit dem Namens tools.jar, das sich im lib-Verzeichnis des JDK befindet. Die normalen ausführbaren Programme wie javadoc oder javah haben (unter Windows) alle die gleiche Größe um 15 KiB, da sie nichts anderes machen, als die Java-Laufzeitumgebung mit tools.jar im Klassenpfad aufzurufen und dann an die entsprechende main()-Methode des Programms weiterzuleiten. Statt javadoc aufzurufen, kommt com.sun.tools.javadoc.Main.main() zum gleichen Ziel.
Oracle definiert nicht für jedes Tool eine API. So ist es zum Beispiel nicht vorgesehen, in den Erzeugungsprozess von javah einzugreifen. Wohl ist es aber möglich, eigene Doclets zu schreiben – Doclets sind kleine Programme, die aus der Java-API-Dokumentation zum Beispiel verlinkte HTML-Dokumente aufbauen.
23.3.1 Eigene Doclets
Standardmäßig sind die Programme – und die API der Programme – nicht im Klassenpfad eingebunden. Um eigene Doclets zu schreiben, muss zunächst tools.jar in den Klassenpfad aufgenommen werden. Das macht neue Pakete wie com.sun.javadoc (Metadateien) und com.sun.tools.javadoc (Doclet-Tool selbst) verfügbar.
Im folgenden Beispiel wollen wir ein kleines Doclet schreiben, das Klassen, Methoden und Konstruktoren ausgibt, die das Tag @since 1.7 tragen. So lässt sich leicht ermitteln, was in der Version Java 7 alles hinzugekommen ist. Doclets werden normalerweise von der Kommandozeile aufgerufen und dem javadoc-Tool übergeben. Unser Programm vereinfacht das, indem es direkt das Tool über Java mit dem passenden Parameter aufruft.
Listing 23.2: com/tutego/tools/javadoc/SinceJava7FinderDoclet.java
package com.tutego.tools.javadoc;
import java.io.*;
import java.util.Formatter;
import com.sun.javadoc.*;
import com.sun.tools.javadoc.Main;
public class SinceJava7FinderDoclet
{
private final static Formatter formatter = new Formatter();
public static boolean start( RootDoc root )
{
for ( ClassDoc clazz : root.classes() )
processClass( clazz );
return true;
}
private static void processClass( ClassDoc clazz )
{
for ( Tag tag : clazz.tags( "since" ) )
if ( "1.7".equals( tag.text() ) )
formatter.format( "Neuer Typ %s%n", clazz );
for ( MethodDoc method : clazz.methods() )
for ( Tag tag : method.tags( "since" ) )
if ( "1.7".equals( tag.text() ) )
formatter.format( "Neue Methode %s%n", method );
for ( ConstructorDoc constructor : clazz.constructors() )
for ( Tag tag : constructor.tags( "since" ) )
if ( "1.7".equals( tag.text() ) )
formatter.format( "Neuer Konstruktor %s%n", constructor );
for ( FieldDoc field : clazz.fields() )
for ( Tag tag : field.tags( "since" ) )
if ( "1.7".equals( tag.text() ) )
formatter.format( "Neues Attribut %s%n", field );
}
public static void main( String[] args )
{
PrintStream err = System.err, out = System.out;
System.setErr( new PrintStream( new OutputStream() {
@Override public void write( int b ) { }
} ) );
System.setOut( System.err );
String[] params = { "-quiet", // ignored!?
"-doclet", SinceJava7FinderDoclet.class.getName(),
"-sourcepath", "C:/Program Files/Java/jdk1.7.0/src/",
// "java.lang"
"-subpackages", "java:javax"
};
Main.execute( params );
System.setErr( err );
System.setOut( out );
System.out.println( formatter );
}
}
Unsere main()-Methode ruft das JDK-Doclet-Programm über Main.execute() auf und übergibt die eigene Doclet-Klasse per Parameter – die Argumente von execute() erinnern an die Kommandozeilenparameter. Das Doclet-Hauptprogramm wiederum ruft unsere start(RootDoc root)-Methode auf – das Gleiche würde auch passieren, wenn das Doclet von außen über javadoc aufgerufen würde. start() läuft über alle ermittelten Typen und übergibt zum Abarbeiten der Innereien die Verantwortung an processClass(). Die Metadaten kommen dabei über diverse XXXDoc-Typen.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.