package org.codehaus.mojo.naturaldocs; /* * The MIT License * * Copyright (c) 2008, The Codehaus * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; /* * Debug syntax * mvn org.codehaus.mojo.naturaldocs:naturaldocs-maven-plugin:1.0-SNAPSHOT:generate --debug */ /** * Generate the Natural Docs report. This will invoke Natural Docs already installed on your system. * <p> * TODO: Could this be bound to the site phase by default and included in the site documentation (perhaps in place of * JavaDoc)? * <p> * TODO: Do you want to consider a main and test invocation of the plugin? * * @author <a href="mailto:timothy.astle@caris.com">Tim Astle</a> * @goal generate */ public class NaturalDocsMojo extends AbstractMojo { /** * The Natural Docs home directory. TODO: These should be private and have getters/setters. * * @parameter * @required */ protected File naturalDocsHome; /** * The input directory to run Natural Docs on. * <p> * TODO: I would think having some defaults here might be useful e.g. src/main/java * * @parameter * @required */ protected File input; /** * The output format of the generated documenation. The supported formats are HTML and FramedHTML. * * @parameter default-value="HTML" * @required */ protected String outputFormat; /** * The output directory where the generated documenation will be placed. * * @parameter expression="${project.naturaldocs.build.directory}" * default-value="${project.build.directory}/naturaldocs" * @required */ protected File output; /** * The project directory where the configuration for the project documentation is located. * * @parameter * @required */ protected File project; /** * Excludes a subdirectory from being scanned. The output and project directories are automatically excluded. * * @parameter */ protected File excludeImport; /** * Adds a directory to search for image files when using (see [file]). * * @parameter */ protected File images; /** * Selects the CSS style for HTML output. The default styles are Default, Small, and Roman. You can use any CSS file * in your project directory or Natural Docs' Styles directory just by using its name without the .css extension. If * you include more than one, they will all be included in the HTML that order. * * @parameter */ protected List<String> style; /** * Rebuilds everything from scratch. All source files will be rescanned and all output files will be rebuilt * * @parameter default-value="false" */ protected boolean rebuild; /** * Rebuilds all output files from scratch. * * @parameter default-value="false" */ protected boolean rebuildOutput; /** * Sets the number of spaces tabs should be expanded to. This only needs to be set if you use tabs in example code * or text diagrams. The default is 4. * * @parameter */ protected Integer tabLength; /** * Sets the syntax highlighting option used in the output. Off turns off all syntax highlighting. Code applies it to * prototypes and (start code) segments. All applies it to prototypes, (start code) segments, and lines prefixed * with >, |, or :. The default is Code. * * @parameter */ protected String highlight; /** * Tells Natural Docs to only include what you explicitly document in the output, and not to find undocumented * classes, functions, and variables. This option is only relevant if you have full language support. * * @parameter default-value="false" */ protected boolean documentedOnly; /** * Tells Natural Docs to only use the file name for its menu and page titles. It won't try to determine one from the * contents of the file. * * @parameter default-value="false" */ protected boolean onlyFileTitles; /** * Tells Natural Docs to not automatically create group topics if you don't add them yourself. * * @parameter default-value="false" */ protected boolean noAutoGroup; /** * Sets the character set property of the generated HTML, such as UTF-8 or Shift_JIS. The default leaves it * unspecified. * * @parameter */ protected String characterSet; /** * Suppresses all non-error output. * * @parameter default-value="false" */ protected boolean quiet; /* * (non-Javadoc) * @see org.apache.maven.plugin.AbstractMojo#execute() */ public void execute() throws MojoExecutionException { this.checkConfig(); // Get the Natual Docs script path String naturalDocsScriptPath = this.getNaturalDocsScriptPath(); // We copy the project directory to avoid file modification if project is in a RCS. this.copyProjectDirectory(); // Run Natural Docs this.generateNaturalDocs( this.createNaturalDocsCommand( naturalDocsScriptPath ) ); } /** * Verify the configuration passed given to the mojo. * * @throws MojoExecutionException The configuration provided was not correct. */ protected void checkConfig() throws MojoExecutionException { if ( !this.naturalDocsHome.isDirectory() ) { throw new MojoExecutionException( "The <naturalDocsHome/> configuration must be set to the directory where Natural Docs is located." ); } if ( !this.input.isDirectory() ) { throw new MojoExecutionException( "The <input/> configuration must be set to a valid directory." ); } if ( !this.project.isDirectory() ) { throw new MojoExecutionException( "The <project/> configuration must be set to a valid directory." ); } if ( !( this.outputFormat.equals( "HTML" ) || this.outputFormat.equals( "FramedHTML" ) ) ) { throw new MojoExecutionException( "The <outputFormat/> configuration must be set to HTML or FramedHTML." ); } // Optional check if ( this.highlight != null && ( !this.highlight.equals( "Off" ) && !this.highlight.equals( "Code" ) && !this.highlight.equalsIgnoreCase( "All" ) ) ) { throw new MojoExecutionException( "The <highlight/> configuration must be set to Off or Code or All." ); } } /** * Construct a fully qualified path to where the Natural Docs perl script should be located based on the given * Natural Docs home directory. * * @return The path to the natural docs perl script. * @throws MojoExecutionException Could not find the NaturalDocs perl script. */ protected String getNaturalDocsScriptPath() throws MojoExecutionException { String naturalDocsScriptPath = naturalDocsHome.getPath() + File.separator + "NaturalDocs"; // TODO: Surround with debug check. this.getLog().debug( "Natural Docs script path: " + naturalDocsScriptPath ); File f = new File( naturalDocsScriptPath ); if ( !f.isFile() ) { throw new MojoExecutionException( "Cannot find the NaturalDocs perl script in the given <naturalDocsHome/> location." ); } return naturalDocsScriptPath; } /** * Create the command that will be used to execute Natural Docs. * * @param naturalDocsScriptPath The path to the Natural Docs script file. * @return The command that will run Natural Docs. * @see <a href="http://www.naturaldocs.org/running.html">Running Natural Docs</a> */ protected String createNaturalDocsCommand( String naturalDocsScriptPath ) { // Mandatory parameters // TODO: You probably want a StringBuilder instead: // http://download.oracle.com/javase/1.5.0/docs/api/java/lang/StringBuilder.html StringBuffer naturalDocsCommand = new StringBuffer( "perl " ); naturalDocsCommand.append( "\"" + naturalDocsScriptPath + "\"" ); naturalDocsCommand.append( " -i " ); naturalDocsCommand.append( "\"" + this.input + "\"" ); naturalDocsCommand.append( " -o " ); naturalDocsCommand.append( this.outputFormat ); naturalDocsCommand.append( " " ); naturalDocsCommand.append( "\"" + this.output + "\"" ); naturalDocsCommand.append( " -p " ); naturalDocsCommand.append( "\"" + this.project + "\"" ); // Optional parameters if ( this.excludeImport != null && excludeImport.isDirectory() ) { naturalDocsCommand.append( " -xi " ); naturalDocsCommand.append( "\"" + excludeImport.getPath() + "\"" ); this.getLog().debug( "Exclude Import: " + excludeImport.getPath() ); } if ( this.images != null && images.isDirectory() ) { naturalDocsCommand.append( " -img " ); naturalDocsCommand.append( "\"" + images.getPath() + "\"" ); this.getLog().debug( "Images: " + images.getPath() ); } if ( this.style != null && this.style.size() > 0 ) { naturalDocsCommand.append( " -s" ); for ( String s : this.style ) { naturalDocsCommand.append( " " + s ); this.getLog().debug( "Style: " + s ); } } if ( this.rebuild ) { naturalDocsCommand.append( " -r" ); this.getLog().debug( "Rebuild" ); } if ( this.rebuildOutput ) { naturalDocsCommand.append( " -ro" ); this.getLog().debug( "Rebuild Output" ); } if ( this.tabLength != null ) { naturalDocsCommand.append( " -t " + this.tabLength ); this.getLog().debug( "Tab Length: " + this.tabLength ); } if ( this.highlight != null ) { naturalDocsCommand.append( " -hl " + this.highlight ); this.getLog().debug( "Highlight: " + this.highlight ); } if ( this.documentedOnly ) { naturalDocsCommand.append( " -do" ); this.getLog().debug( "Documented Only" ); } if ( this.onlyFileTitles ) { naturalDocsCommand.append( " -oft" ); this.getLog().debug( "Only File Titles" ); } if ( this.noAutoGroup ) { naturalDocsCommand.append( " -nag" ); this.getLog().debug( "No Auto Group" ); } if ( this.characterSet != null ) { naturalDocsCommand.append( " -cs " + this.characterSet ); this.getLog().debug( "Character Set: " + this.characterSet ); } if ( this.quiet ) { naturalDocsCommand.append( " -q" ); this.getLog().debug( "Quiet" ); } // This is what we'll run. // TODO: Surround is a getLog().isDebugEnabled() check. this.getLog().debug( "Natural Docs command: " + naturalDocsCommand ); return naturalDocsCommand.toString(); } /** * Copy the project directory before we run natural docs. Often a user will have the project configuration committed * in a revision control system and Natual Docs modifies these files. So we'll copy the project directory elsewhere * before Natural Docs uses it. * * @throws MojoExecutionException There was a problem copying the project directory. */ private void copyProjectDirectory() throws MojoExecutionException { String tmpProjectPath = System.getProperty( "java.io.tmpdir" ) + "NaturalDocsProject"; this.getLog().debug( "Temporary Project Path: " + tmpProjectPath ); // Prepare the directory. File tmpProjectDirectory = new File( tmpProjectPath ); if ( tmpProjectDirectory.exists() ) { tmpProjectDirectory.delete(); } tmpProjectDirectory.mkdir(); try { FileUtils.copyDirectory( this.project, tmpProjectDirectory ); } catch ( IOException e ) { this.getLog().error( e ); throw new MojoExecutionException( "There was a problem copying the project directory to java.io.tmpdir.", e ); } // Make the project directory the newly copied one. this.project = tmpProjectDirectory; } /** * Generate documentation by running Natural Docs. * * @param naturalDocsCommand The command to run natural docs. * @throws MojoExecutionException There was a problem running the Natural Docs process. */ private void generateNaturalDocs( String naturalDocsCommand ) throws MojoExecutionException { // If the output directory doesn't exist, we need to make it. if ( !this.output.exists() ) { this.output.mkdir(); } try { // We need to run Natural Docs from the Natural Docs home directory to avoid 'Can't open perl script // "NaturalDocs": No such file or directory' type messages. Process p = Runtime.getRuntime().exec( naturalDocsCommand.toString(), null, naturalDocsHome ); // Let's give a little extra info when --debug is provided. if ( this.getLog().isDebugEnabled() ) { String s = null; this.getLog().debug( "Standard Input:" ); BufferedReader stdInput = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( ( s = stdInput.readLine() ) != null ) { this.getLog().debug( s ); } this.getLog().debug( "Standard Output:" ); BufferedReader stdError = new BufferedReader( new InputStreamReader( p.getErrorStream() ) ); while ( ( s = stdError.readLine() ) != null ) { this.getLog().debug( s ); } } } catch ( IOException e ) { this.getLog().error( e ); throw new MojoExecutionException( "An IO Exception occurred while executing Natural Docs.", e ); } } }