Documentare Groovy con Groovydoc

Groovydoc è stato introdotto nel 2007 per fornire a Groovy ciò che Javadoc fornisce per Java. Groovydoc viene utilizzato per generare la documentazione API per le classi Groovy e Java che compongono il linguaggio Groovy. In questo post, vedo come invocare Groovydoc tramite la riga di comando e tramite l'attività Ant personalizzata fornita da Groovy.

Groovy e codice sorgente Java con commenti Groovydoc / Javadoc

Userò versioni adattate dello script Groovy e delle classi introdotte per la prima volta nel mio post sul blog Easy Groovy Logger Injection e Log Guarding per dimostrare Groovydoc. Lo script Groovy principale e le classi Groovy di quel post sono state modificate per includere più commenti in stile Javadoc per dimostrare meglio Groovydoc in azione. Lo script rivisto e le classi associate vengono visualizzati negli elenchi di codice successivi.

demoGroovyLogTransformation.groovy

#!/usr/bin/env groovy /** * demoGroovyLogTransformation.groovy * * Grab SLF4J, Log4j, and Apache Commons Logging dependencies using @Grab and * demonstrate Groovy 1.8's injected logging handles. * * //marxsoftware.blogspot.com/2011/05/easy-groovy-logger-injection-an... */ // No need to "grab" java.util.logging: it's part of the JDK! /* * Specifying 'slf4j-simple' rather than 'slf4j-api' to avoid the error * "Failed to load class "org.slf4j.impl.StaticLoggerBinder" that is caused by * specifying no or more than one of the actual logging binding libraries to * be used (see //www.slf4j.org/codes.html#StaticLoggerBinder). One should * be selected from 'slf4j-nop', 'slf4j-simple', 'slf4j-log4j12.jar', * 'slf4j-jdk14', or 'logback-classic'. An example of specifying the SLF4J * dependency via @Grab is available at * //mvnrepository.com/artifact/org.slf4j/slf4j-api/1.6.1. */ @Grab(group='org.slf4j', module="slf4j-simple", version="1.6.1") /* * An example of specifying the Log4j dependency via @Grab is at * //mvnrepository.com/artifact/log4j/log4j/1.2.16. */ @Grab(group='log4j', module="log4j", version="1.2.16") /* * An example of specifying the Apache Commons Logging dependency via @Grab is at * //mvnrepository.com/artifact/commons-logging/commons-logging-api/1..... */ @Grab(group='commons-logging', module="commons-logging-api", version="1.1") /* * Run the tests... */ int headerSize = 79 printHeader("java.util.logger", headerSize) def javaUtilLogger = new JavaUtilLoggerClass() printHeader("Log4j", headerSize) def log4jLogger = new Log4jLoggerClass() printHeader("SLF4j", headerSize) def slf4jLogger = new Slf4jLoggerClass() printHeader("Apache Commons", headerSize) def commonsLogger = new ApacheCommonsLoggerClass() /** * Print header with provided text. * * @param textForHeader Text to be included in the header. * @param sizeOfHeader Number of characters in each row of header. */ def printHeader(final String textForHeader, final int sizeOfHeader) { println "=".multiply(sizeOfHeader) println "= ${textForHeader}${' '.multiply(sizeOfHeader-textForHeader.size()-4)}=".multiply(sizeOfHeader) } 

JavaUtilLoggerClass.groovy

import groovy.util.logging.Log /** * Sample Groovy class using {@code @Log} to inject java.util.logging logger * into the class. */ @Log class JavaUtilLoggerClass { /** * Constructor. */ public JavaUtilLoggerClass() { println "\njava.util.logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.finer "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of JDK's java.util.logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and JDK for java.util.logging. */ public String printAndReturnValue(int newValue) { println "JDK: Print method invoked for ${newValue}" return "JDK: ${newValue}" } } 

Log4jLoggerClass.groovy

import groovy.util.logging.Log4j import org.apache.log4j.Level /** * Sample Groovy class using {@code @Log4j} to inject Log4j logger * into the class. */ @Log4j class Log4jLoggerClass { /** * Constructor. */ Log4jLoggerClass() { // It is necessary to set logging level here because default is FATAL and // we are not using a Log4j external configuration file in this example log.setLevel(Level.INFO) println "\nLog4j Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Log4j. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Log4j. */ public String printAndReturnValue(int newValue) { println "Log4j: Print method invoked for ${newValue}" return "Log4j: ${newValue}" } } 

Slf4jLoggerClass.groovy

import groovy.util.logging.Slf4j /** * Sample Groovy class using {@code @Slf4j} to inject Simple Logging Facade for * Java (SLF4J) logger into the class. */ @Slf4j class Slf4jLoggerClass { /** * Constructor. */ public Slf4jLoggerClass() { println "\nSLF4J Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of SLF4J logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and SLF4J. */ public String printAndReturnValue(int newValue) { println "SLF4J: Print method invoked for ${newValue}" return "SLF4J: ${newValue}" } } 

ApacheCommonsLoggerClass.groovy

import groovy.util.logging.Commons /** * Sample Groovy class using {@code @Commons} to inject Apache Commons logger * into the class. */ @Commons class ApacheCommonsLoggerClass { /** * Constructor. */ public ApacheCommonsLoggerClass() { println "\nApache Commons Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Apache Commons Logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Apache Commons Logging. */ public String printAndReturnValue(int newValue) { println "Commons: Print method invoked for ${newValue}" return "Commons: ${newValue}" } } 

Oltre allo script e alle classi Groovy sopra, utilizzo anche una nuova classe Java per illustrare che Groovydoc funziona su classi Java e classi Groovy. La classe Java non fa molto oltre a fornire i commenti Javadoc che devono essere elaborati da Groovydoc.

DoNothingClass.java

/** * Class that does not do anything, but is here to be a Java class run through * groovydoc. */ public class DoNothingClass { /** * Simple method that returns literal "Hello _addressee_!" string where * _addressee_ is the name provided to this method. * * @param addressee Name for returned salutation to be addressed to. * @return "Hello!" */ public String sayHello(final String addressee) { return "Hello, " + addressee; } /** * Main executable function. */ public static void main(final String[] arguments) { final DoNothingClass me = new DoNothingClass(); me.sayHello("Dustin"); } /** * Provide String representation of this object. * * @return String representation of me. */ @Override public String toString() { return "Hello!"; } } 

Esecuzione di Groovydoc dalla riga di comando

Con lo script Groovy, le classi Groovy e la classe Java mostrate sopra pronte per l'uso, è tempo di rivolgere l'attenzione all'esecuzione di Groovydoc su queste classi e script. Come nel caso di Javadoc, Groovydoc può essere eseguito dalla riga di comando. Il comando per eseguire Groovydoc contro le classi e gli script precedenti (supponendo che si trovino tutti nella stessa directory in cui viene eseguito il comando) assomiglia a questo:

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

Il comando precedente viene eseguito tutto su una riga. Tuttavia, per una migliore leggibilità, ho aggiunto interruzioni di riga per suddividere il comando.

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

I parametri del comando groovydoc sembrano familiari a chiunque abbia utilizzato javadoc dalla riga di comando. L'ultima parte del comando specifica che groovydoc deve essere eseguito su Groovy e codice Java.

Eseguire Groovydoc da Ant

È inoltre possibile accedere facilmente a Groovydoc tramite un'attività Ant personalizzata come descritto nella Guida dell'utente di Groovy. È abbastanza facile applicare il task groovydoc Ant impostando prima il taskdef appropriato e quindi utilizzando quel tag definito. Ciò è dimostrato nel seguente frammento XML da un build.xmlfile pertinente .

Parti di un file Ant build.xml che dimostrano l'attività groovydoc


    
    

La porzione di Ant build.xmlmostrata sopra è approssimativamente equivalente a quella utilizzata sulla riga di comando. Avere Groovydoc disponibile tramite Ant è importante perché rende più facile integrare la creazione di documentazione Groovy da sistemi di compilazione basati su Ant.

Documentazione generata da Groovydoc

Poiché ogni approccio alla generazione della documentazione di Groovy tramite Groovydoc (riga di comando o basato su Ant) funziona più o meno allo stesso modo dell'altro, ora mi concentrerò sull'output HTML che potrebbe derivare da entrambi gli approcci. La serie successiva di istantanee dello schermo mostra la documentazione generata a partire dalla pagina principale, seguita dalla pagina DefaultPackage (ho lasciato pigramente lo script, le classi Groovy e la classe Java nella directory corrente e senza alcuna dichiarazione di pacchetto), seguita rispettivamente dall'output per lo script Groovy, per un esempio di classe Groovy, e per la classe Java inventata. Le ultime tre immagini aiutano a distinguere tra l'output di uno script Groovy e una classe Groovy rispetto a una classe Java.

Esempio di pagina principale di Groovydoc

Output di Groovydoc per pacchetto di esempio (DefaultPackage)

Output di Groovydoc per esempio di script Groovy

Output di Groovydoc per esempio di classe Groovy

Output di Groovydoc per una classe Java di esempio

Diverse osservazioni possono essere fatte dall'output di Groovydoc mostrato sopra. Innanzitutto, la documentazione generata per lo script Groovy ha documentato solo i metodi definiti nello script (incluso il mainmetodo implicito ). Ciò che non è così ovvio dalle immagini statiche sopra è che, in effetti, nessun output di Groovydoc viene creato per uno script a meno che almeno un metodo non sia definito esplicitamente nello script. Se un metodo è definito nello script, l'output di Groovydoc viene generato per tutti i metodi definiti e per il metodo principale implicito. L'opzione -nomainforscriptspuò essere passata a Groovydoc per non avere alcun Groovydoc generato per il mainmetodo implicito . Il risultato dell'aggiunta di questa opzione è mostrato di seguito (notare che la maindocumentazione di non è più visualizzata).

L' -nommainforscriptsopzione è utile perché spesso non vogliamo che la mainfunzione venga documentata in modo implicito per i nostri script. In effetti, la mainfunzione è tipicamente "nascosta" a noi come autori e manutentori di script.

Una seconda osservazione dall'analisi dell'output generato da Groovydoc è che l'output generato distingue tra Groovy e il codice sorgente Java. Gli script e le classi Groovy sono etichettati con "[Groovy]" e le classi Java sono etichettate con "[Java]." Ciò è evidente anche nella documentazione dell'API Groovy generata da Groovydoc, dove questa funzionalità rende facile identificare che groovy.sql.Sql e AntBuilder sono classi Java mentre JmxBuilder e SwingBuilder sono classi Groovy.