Codegenerierung mit Javadoc

Die automatische Codegenerierung wird in der Softwareentwicklung immer häufiger eingesetzt, da die Komplexität vor dem Softwareentwickler verborgen bleiben muss und verschiedene Standard- und De-facto-Standard-Anwendungsprogrammierschnittstellen akzeptiert werden. Das Ausblenden der Komplexität vor dem Entwickler kann demonstriert werden, indem in CORBA Stub- und Skeleton-Klassen aus ihren Sprachbeschreibungen für die Schnittstellendefinition erstellt werden und indem einige objektorientierte Datenbanken den erforderlichen Adaptercode erstellen, um Objekte aus der Datenbank beizubehalten und abzurufen.

Java enthält viele APIs, die Java-Entwickler als De-facto-Standards betrachten. Die Komplexität dieser APIs reicht von denen, die den "Kern" der Java-Sprache bilden, bis zu denen, die in der Java 2 Platform, Enterprise Edition, enthalten sind. Beispielsweise bietet die Java Database Connectivity API eine einheitliche Schnittstelle für die Interaktion mit Datenbanken verschiedener Unternehmen. Angenommen, Sie möchten, dass ein Java-Objekt in einer Datenbank bestehen bleibt, indem Sie eine einfache save()Methode implementieren , mit der die Attribute des Objekts einer Datenbanktabelle zugeordnet werden. Diese Methode würde die Attribute aus dem Objekt extrahieren und die JDBC-API verwenden, um eine JDBC-Anweisung zu erstellen, die für die Datenbank ausgeführt wird. Nach der Implementierung dersave()Methode für einige Klassen sehen Sie die Ähnlichkeiten in der Codestruktur und die Wiederholung der Implementierung dieser Methode. Oft müssen die grundlegenden Attribute eines Objekts transliteriert und in die entsprechende Java-API "eingesteckt" werden. In diesem Fall kann ein Codegenerator ein nützliches Werkzeug in Ihrer Programmier-Toolbox sein.

Mithilfe eines Codegenerators können Sie den Prozess einiger langwieriger, sich wiederholender und fehleranfälliger Codierungsaufgaben automatisieren. Die Tatsache, dass Sie sich an bekannte APIs anschließen, erhöht den Nutzen eines solchen Tools, da es für ein breites Publikum von Entwicklern anwendbar ist. Darüber hinaus können einige typische "interne" domänenspezifische Frameworks auch als feste API-Ziele für Codegeneratoren betrachtet werden.

Ein Codegenerator kann ein zeitsparendes Werkzeug sein, das die Codequalität erhöht und einen formaleren und automatisierten Ansatz für einen Teil des Entwicklungszyklus einführt. Ein weiterer Vorteil der automatisierten Codegenerierung ist die Synchronisation von Objektdefinitionen über verschiedene Programmiersprachen hinweg. In vielen eng gebundenen Anwendungen muss dasselbe Geschäftsobjekt (z. B. eine Bestellung zum Kauf einer Aktie) in C ++, Java und SQL konsistent dargestellt werden. Die Möglichkeit, verschiedene Darstellungen aus einem gemeinsamen Modell auszugeben, ist in verschiedenen Modellierungswerkzeugen verfügbar. Ich fand es jedoch umständlich, diese Tools zu verwenden, um den erforderlichen Anpassungsgrad zu erreichen. Ein dedizierter benutzerdefinierter Codegenerator ist einfach zu erstellen und bindet Sie nicht an ein bestimmtes Modellierungswerkzeug.

Der Weg nach Javadoc

Der Weg, den mein Team zur Auswahl von Javadoc für die Codegenerierung eingeschlagen hat, war etwas lang und wahrscheinlich üblich. In frühen Implementierungen haben wir Perl-Skripte verwendet, um benutzerdefinierte Metadatengrammatik in einer Textdatei zu analysieren. Dies war eine Ad-hoc-Lösung, und das Hinzufügen zusätzlicher Ausgabeformate war schwierig. Unser zweiter, kurzlebiger Versuch bestand darin, einen vorhandenen Java-basierten IDL-Compiler zu ändern. Wir stellten schnell fest, dass zusätzliche IDL-Schlüsselwörter eingeführt werden müssen, um Hinweise an den Codegenerator zu senden. Eine Erweiterung von IDL vorzunehmen oder sogar mit Tools wie lex und yacc von vorne zu beginnen (die eine Quelldatei in Token aufteilen und Code definieren, der für jedes erkannte Token aufgerufen wird), war nicht persönlich schmackhaft. (Weitere Informationen finden Sie unter Ressourcen.)

Eine dritte vielversprechendere Lösung bestand darin, die Klassenmetadaten mithilfe von XML zu beschreiben. Das Definieren eines XML-DTD-Schemas und das Erstellen von XML-Dokumenten zur Beschreibung von Klassen schien eine natürliche Übereinstimmung zu sein. Die Datei konnte dann überprüft und leicht analysiert werden. Um nicht von vorne anzufangen, stellte ich fest, dass jemand versucht haben muss, eine ähnliche XML-DTD zu erstellen, und stieß bald auf XMI. XMI ist eine vollständige Beschreibung von UML mithilfe von XML und wird jetzt als Austauschformat zwischen UML-Tools verwendet. (Weitere Informationen finden Sie unter Ressourcen.)

Die XML-Dokumente, in denen Klassen beschrieben wurden, waren jedoch äußerst ausführlich und schwer manuell zu bearbeiten. Es gibt einfach zu viele scheinbar überflüssige Tags und Beschreibungen, um sie zu entfernen, als dass Sie ein Klassenattribut ändern könnten. Das Bearbeiten von XML-Dateien auf der Ebene der Anwendungsdomäne kann auch recht mühsam sein. IBM alphaWorks erstellt ein XMI-Toolkit, das die Verarbeitung von XMI-basierten XML-Dokumenten erheblich vereinfacht. Die XMI-Toolkit-API zum Bearbeiten von Klassenbeschreibungen ist jedoch der Java Reflection- oder Doclet-API sehr ähnlich. Vor diesem Hintergrund entschied sich meine Organisation für den erfolgreichen Doclet-Ansatz.

Wir stellen Javadoc vor

Javadoc ist das Programm, mit dem die Java-API-Dokumentation im HTML-Format erstellt wird. Es wird als Teil des Java SDK verteilt und seine Ausgabestufe ist so konzipiert, dass es durch die Erstellung von Doclets erweiterbar ist. Die Doclet-API bietet die Infrastruktur für den Zugriff auf alle Aspekte einer Java-Quellcodedatei, die von Javadoc analysiert wurde. Mithilfe der Doclet-API, die der Reflection-API ähnelt, können Sie eine Java-Klassenbeschreibung durchgehen, auf benutzerdefinierte Javadoc-Tags zugreifen und die Ausgabe in eine Datei schreiben. Das Standard-Doclet zur Erstellung der HTML-Dokumentation macht genau das; Es schreibt HTML-Dateien aus, während es den gesamten Java-Quellcode durchläuft. Weitere Informationen zu Javadoc finden Sie unter Ressourcen.

Durch das Erstellen einfacher Java-Klassen, die Attribute und einige benutzerdefinierte Javadoc-Tags enthalten, können diese Klassen als einfache Metadatenbeschreibung für die Codegenerierung dienen. Javadoc analysiert diese Metadatenklassen, und benutzerdefinierte Doclets greifen auf die Metadatenklasseninformationen zu, um konkrete Implementierungen der Metadatenklasse in bestimmten Programmiersprachen wie Java, C ++ oder SQL zu erstellen. Sie können auch Variationen des Standard-Doclets erstellen, das einfache HTML-Tabellen erstellt, die die Metadatenklasse beschreiben und in ein Textverarbeitungsdokument aufgenommen werden sollen. Diese Metadaten-Java-Klassen dienen demselben Zweck wie eine IDL-Beschreibung, deren Syntax C ++ ähnelt.

Die Verwendung von Javadoc als Tool zur Codegenerierung bietet mehrere Vorteile:

  • Sie müssen keinen Parsing-Code schreiben. Das Parsen der Metadatenklassen wird von Javadoc durchgeführt und in einer benutzerfreundlichen API dargestellt.
  • Durch die Verwendung benutzerdefinierter Javadoc-Tags bieten Sie gerade genug Flexibilität, um spezielle Hooks während der Codegenerierung zu definieren.
  • Da Java-Typen gut definiert sind, beträgt ein int 32 Bit. Daher müssen Sie keine zusätzlichen Schlüsselwörter vom primitiven Typ einführen, um diese Klarheit zu erreichen.
  • Sie können die Java-Metadatenklassen durch Kompilierung auf Syntax und andere Fehler überprüfen.

Einführung in die Dokumente

Bevor ich mich mit dem für die Codegenerierung verwendeten Doclet befasse, werde ich ein einfaches "Hello World" -Beispiel vorstellen, in dem die relevanten Teile zum Erstellen, Ausführen und Spielen mit der Doclet-API erläutert werden. Der Beispielcode für SimpleDocletist unten angegeben. (Den Quellcode für diesen Artikel finden Sie unter Ressourcen.) Wenn Sie diesen Code für ein echtes "Hello World" -Programm als etwas langwierig betrachten, bietet die Sun-Website ein noch einfacheres Dokument, das Ihnen den Einstieg erleichtert. (Siehe Ressourcen.)

Paket codegen.samples; import com.sun.javadoc. *; import java.text. *; public static boolean start (RootDoc ​​root) {// über alle Klassen iterieren. ClassDoc [] classes = root.classes (); for (int i = 0; i <classes.length; i ++) {// iteriere über alle Methoden und drucke ihre Namen. MethodDoc [] Methoden = Klassen [i] .methods (); out ("Methoden"); aus("-------"); für (int j = 0; j
   
    

Das obige Dokument druckt beschreibende Informationen zu den Klassen, Methoden, Feldern und einigen Javadoc-Tag-Informationen der SimpleOrder.javaunten aufgeführten Klasse aus :

öffentliche Klasse SimpleOrder {public SimpleOrder () {} public String getSymbol () {return Symbol; } public int getQuantity () {{escriptive return Quantity; } / ** * Ein gültiges Aktiensymbol. * * @see Ein großes Buch mit gültigen Symbolen für weitere Informationen. * / privates String-Symbol; / ** * Das gesamte Bestellvolumen. * * @mytag Mein benutzerdefiniertes Tag. * / private int Menge; private String OrderType; privater Float Preis; private String Dauer; private int AccountType; private int TransactionType; }}

Nach dem Kompilieren dieser Dateien rufen Sie das Javadoc-Tool mit dem folgenden Befehl auf:

javadoc -private -doclet codegen.samples.SimpleDoclet SimpleOrder.java 

Die -privateOption weist Javadoc an, private Feld- und Methodeninformationen -docletverfügbar zu machen , und die Option teilt Javadoc mit, welches Doclet aufgerufen werden soll. Der letzte Parameter ist die zu analysierende Datei. Die Ausgabe des Programms ist die folgende:

Laden der Quelldatei SimpleOrder.java ... Erstellen von Javadoc-Informationen ... Methoden ------- Methode: name = getSymbol Methode: name = getQuantity Felder ------ Feld: name = Symbol, Kommentar = A gültig Aktiensymbol., Typ = java.lang.String; Feld-Tag-Name = @see Feld-Tag-Wert = Ein großes Buch mit gültigen Symbolen für weitere Informationen. Feld: Name = Menge, Kommentar = Das gesamte Auftragsvolumen., Typ = int; Feld-Tag-Name = @mytag Feld-Tag-Wert = Mein benutzerdefiniertes Tag. Feld: name = OrderType, comment =, type = java.lang.String; Feld: Name = Preis, Kommentar =, Typ = Float; Feld: name = Dauer, Kommentar =, Typ = java.lang.String; Feld: name = AccountType, comment =, type = int; Feld: name = TransactionType, comment =, type = int;

Der Beispielcode zeigt, dass die Doclet-API im Paket enthalten ist com.sun.javadoc. Da Sie sich an das Javadoc-Tool anschließen und keine eigenständige Anwendung erstellen, ruft Javadoc Ihr Doclet über die Methode auf public static boolean start(RootDoc root).

Enthält nach Ausführung der startMethode RootDocalle von Javadoc analysierten Informationen. Sie können dann alle analysierten Klassen beginnen zu Fuß durch , indem die Methode aufgerufen classes()auf RootDoc. Diese Methode gibt ein ClassDocArray zurück, das alle analysierten Klassen beschreibt. ClassDocenthält wiederum Methoden wie fields()und methods(). Diese Methoden geben FieldDocund MethodDocArrays zurück, die alle Felder und Methoden der analysierten Klasse beschreiben. Alle "Doc" -Klassen enthalten die Methode tags, die ein TagArray zurückgibt , das sowohl benutzerdefinierte als auch Standard-Javadoc-Tags beschreibt. Das in diesem Beispiel verwendete Standard-Tag ist @see.

Die out()Methode umschließt einfach die Standardausgabe, und die MessageFormatKlasse hilft beim Formatieren der Ausgabe gemäß einer festen Vorlage.

Reusable classes for code generation

In light of the above example, I hope you agree that creating your own doclets and extracting the class information using the Doclet API is easy. The next step to parsing the Java classes and generating code to a file is relatively straightforward. To make creating code-generation doclets easier, I developed a small set of interfaces and abstract base classes. The class diagram of these utility classes is shown below.

The interface Maker defines the method signature public void make(ClassDoc classdoc) that you will use to interact with your code generators. The abstract class CodeMaker provides default implementations for manipulating files and indention, which are common to all code generators. Specific code generators inherit from the abstract base class and provide an implementation of the make method. The make method has the class ClassDoc as an argument, not RootDoc. That causes the Maker to enter the code generation logic at the class level.

All classes parsed by Javadoc are looped over in the doclets plug-in method start. An example of how that is done (described in the file SimpleMakerDoclet.java) is shown below:

öffentlicher statischer boolescher Start (RootDoc-Stamm) {ClassDoc [] classes = root.classes (); // CodeMakers so einrichten, dass Maker ausgeführt wird simplemaker = new SimpleCodeMaker ("Description Maker"); // Durchlaufe alle Klassen und führe die "make" -Methode aus, für die der Maker arbeitet (int i = 0; i <classes.length; i ++) {ClassDoc classdoc = classes [i]; simplemaker.make (classdoc); } return true; }}

Im Folgenden finden Sie Teile des Codes eines einfachen Codegenerators SimpleCodeMaker, der dieselbe Aufgabe wie der SimpleDocletzuvor aufgeführte ausführt . Anstatt die Ausgabe an den Bildschirm zu senden, wird SimpleCodeMakersie in einer Datei im Unterverzeichnis gespeichert genclasses. Die Implementierung der makeMethode wird auch mit separaten Methoden zur Verarbeitung von Feldern und Methoden strukturierter. Nur die Methoden makeund processMethodssind hier der Kürze halber aufgeführt.