/* * Copyright 2013 The Sculptor Project Team, including the original * author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sculptor.maven.plugin; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.HashSet; import java.util.Set; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.apache.commons.exec.Executor; import org.apache.commons.exec.PumpStreamHandler; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; /** * This plugin uses the commandline tool <a * href="http://www.graphviz.org/doc/info/command.html">dot</a> from the open * source graph visualization software <a * href="http://www.graphviz.org/">Graphviz</a> to generate images (PNG format) * for all graph files (<code>src/generated/resources/*.dot</code>) created * during the previous run of the Sculptor code generator. */ @Mojo(name = "generate-images", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true) public class GraphvizMojo extends AbstractGeneratorMojo { /** * Command to execute to generate the images. Default is <a * href="http://www.graphviz.org/doc/info/command.html">dot</a>. * <p> * Can be set from command line using '-Dsculptor.graphviz.command=<command>'. * <p> * Don't include any command line arguments, e.g.image format or input * files. These are handled by the mojo. */ @Parameter(property = "sculptor.graphviz.command", defaultValue = "dot") private String command; /** * Skip the generation of image files. * <p> * Can be set from command line using '-Dsculptor.graphviz.skip=true'. */ @Parameter(property = "sculptor.graphviz.skip", defaultValue = "false") private boolean skip; /** * Check if the execution should be skipped. * * @return true to skip */ protected boolean isSkip() { return skip; } /** * Strategy implementation of running the command line tool <code>dot</code> * : * <ol> * <li>check the <code>skip</code> flag * <li>get a list of modified dot files from the StatusFile * <li>run the dot command * </ol> */ public final void execute() throws MojoExecutionException, MojoFailureException { // If skip flag set then omit image generation if (isSkip()) { getLog().info("Skipping creation of image files"); } else { // Run the dot command only if dot files have been changed Set<String> changedDotFiles = getChangedDotFiles(); if (changedDotFiles != null && !changedDotFiles.isEmpty()) { if (!executeDot(changedDotFiles)) { throw new MojoExecutionException("Executing '" + command + "' command failed"); } } } } /** * Returns a list with file names of dot files that have been changed since * previous image generation run. Empty if no files changed or * <code>null</code> if there is no status file to compare against. */ protected Set<String> getChangedDotFiles() { Set<String> generatedFiles = getGeneratedFiles(); // If there is no status file to compare against or no files are // generated then skip image generation if (generatedFiles == null || generatedFiles.isEmpty()) { return null; } // Check if images for generated dot files are missing or outdated Set<String> changedDotFiles = new HashSet<String>(); for (String generatedFile : generatedFiles) { if (generatedFile.endsWith(".dot")) { File dotFile = new File(getProject().getBasedir(), generatedFile); File imageFile = new File(getProject().getBasedir(), generatedFile + ".png"); if (!imageFile.exists() || imageFile.lastModified() < dotFile.lastModified()) { changedDotFiles.add(generatedFile); } } } // Print info of result if (changedDotFiles.size() == 1) { String fileName = changedDotFiles.iterator().next(); if (fileName.startsWith(project.getBasedir().getAbsolutePath())) { fileName = fileName.substring(project.getBasedir() .getAbsolutePath().length() + 1); } final String message = MessageFormat.format( "\"{0}\" has been changed", fileName); getLog().info(message); } else if (changedDotFiles.size() > 1) { final String message = MessageFormat.format( "{0} dot files have been changed", changedDotFiles.size()); getLog().info(message); } else { getLog().info( "Everything is up to date - no image generation is needed"); } return changedDotFiles; } /** * Executes the command line tool <code>dot</code>. * * @param dotFiles * list of dot files from the * {@link AbstractGeneratorMojo#statusFile} */ protected boolean executeDot(Set<String> dotFiles) throws MojoExecutionException { // Build executor for projects base directory MavenLogOutputStream stdout = getStdoutStream(); MavenLogOutputStream stderr = getStderrStream(); Executor exec = getExecutor(); exec.setWorkingDirectory(project.getBasedir()); exec.setStreamHandler(new PumpStreamHandler(stdout, stderr, System.in)); // Execute commandline and check return code try { int exitValue = exec.execute(getDotCommandLine(dotFiles)); if (exitValue == 0 && stdout.getErrorCount() == 0) { return true; } } catch (ExecuteException e) { // ignore } catch (IOException e) { // ignore } return false; } /** * Returns {@link MavenLogOutputStream} implementation for stdout. * <p> * Can be overriden to replace the default implementation of * <code>MavenLogOutputStream(getLog())</code>. */ protected MavenLogOutputStream getStdoutStream() { return new MavenLogOutputStream(getLog()); } /** * Returns {@link MavenLogOutputStream} implementation for stderr. * <p> * Can be overriden to replace the default implementation of * <code>MavenLogOutputStream(getLog(), true)</code>. */ protected MavenLogOutputStream getStderrStream() { return new MavenLogOutputStream(getLog(), true); } /** * Returns {@link Executor} implementation. * <p> * Can be overriden to replace the default implementation of * {@link DefaultExecutor}. */ protected Executor getExecutor() { return new DefaultExecutor(); } /** * Builds command line for starting the <code>dot</code>. * * @param generatedFiles * list of generated files from the * {@link AbstractGeneratorMojo#statusFile} */ protected CommandLine getDotCommandLine(Set<String> generatedFiles) throws MojoExecutionException { CommandLine cl = new CommandLine(command); if (isVerbose()) { cl.addArgument("-v"); } cl.addArgument("-Tpng"); cl.addArgument("-O"); for (String generatedFile : generatedFiles) { if (generatedFile.endsWith(".dot")) { cl.addArgument(generatedFile); } } if (isVerbose()) { getLog().info("Commandline: " + cl); } return cl; } }