org.fornax.toolsupport.sculptor.maven.plugin.GraphvizMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.fornax.toolsupport.sculptor.maven.plugin.GraphvizMojo.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.fornax.toolsupport.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;

/**
 * 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.
 * 
 * @goal generate-images
 * @phase generate-resources
 * @description Create images for all graph files created by a previous run of
 *              the Sculptor code generator
 * @author Torsten Juergeleit
 */
public class GraphvizMojo extends AbstractSculptorMojo {

    /**
     * Command to execute to generate the images. Default is <a
     * href="http://www.graphviz.org/doc/info/command.html">dot</a>.
     * <p>
     * Don't include any command line arguments, e.g.image format or input
     * files. These are handled by the mojo.
     * 
     * @parameter expression="${sculptor.graphviz.command}" default-value= "dot"
     */
    private String command;

    /**
     * Skip the generation of image files.
     * <p>
     * Can be set from command line using '-Dsculptor.graphviz.skip=true'.
     * 
     * @parameter expression="${sculptor.graphviz.skip}" default-value="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 then skip image
        // generation
        if (generatedFiles == null) {
            return null;
        }

        // Check if images for generated dot files are missing or outdated
        Set<String> changedDotFiles = new HashSet<String>();
        for (String generatedFile : getGeneratedFiles()) {
            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 AbstractSculptorMojo#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 AbstractSculptorMojo#statusFile}
     */
    protected CommandLine getDotCommandLine(Set<String> generatedFiles) throws MojoExecutionException {
        CommandLine cl = new CommandLine(command);
        cl.addArgument("-q");
        cl.addArgument("-Tpng");
        cl.addArgument("-O");
        for (String generatedFile : generatedFiles) {
            if (generatedFile.endsWith(".dot")) {
                cl.addArgument(generatedFile);
            }
        }
        if (isVerbose() || getLog().isDebugEnabled()) {
            getLog().info("Commandline: " + cl);
        }
        return cl;
    }

}