Package com.agilejava.maven.docbkx

Source Code of com.agilejava.maven.docbkx.GeneratorMojo

/*
* Copyright 2006 Wilfred Springer
*
* 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 com.agilejava.maven.docbkx;

import com.agilejava.maven.docbkx.ZipFileProcessor.ZipEntryVisitor;
import com.agilejava.maven.docbkx.spec.Parameter;
import com.agilejava.maven.docbkx.spec.Specification;

import com.icl.saxon.TransformerFactoryImpl;

import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;

import org.codehaus.plexus.util.SelectorUtils;

import org.jaxen.JaxenException;

import org.jaxen.dom.DOMXPath;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import org.xml.sax.SAXException;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

/**
* The base class for all Mojo's that perform some work based on a DocBook XSL *
* distribution.
*
* @author Wilfred Springer
* @goal build
* @phase generate-sources
* @requiresDependencyResolution compile
*/
public class GeneratorMojo
    extends AbstractMojo
{
    /**
     * The name of the stylesheet used as the basis of the {@link Transformer}
     * returned by {@link #createParamListTransformer()}.
     */
    private static String TRANSFORMER_LOCATION = "extract-params.xsl";

    /**
     * The classname of the Mojo that wil provide the desired functionality.
     *
     * @parameter
     */
    private String className;

    /**
     * The package name of the Mojo that will provide the desired functionality.
     *
     * @parameter
     */
    private String packageName;

    /**
     * The classname of the super class from which the generate Mojo will
     * inherit.
     *
     * @parameter expression="com.agilejava.docbkx.maven.AbstractTransformerMojo"
     */
    private String superClassName;

    /**
     * The suffix to be used in the generated plugin.
     *
     * @parameter
     */
    private String pluginSuffix;

    /**
     * Target directory.
     *
     * @parameter expression="${project.build.directory}/generated-sources"
     */
    private File targetDirectory;

    /**
     * The maven project helper class for adding resources.
     *
     * @component role="org.apache.maven.project.MavenProjectHelper"
     */
    private MavenProjectHelper projectHelper;

    /**
     * The directory where all new resources need to be stored.
     *
     * @parameter expression="${basedir}/target/generated-resources"
     */
    private File targetResourcesDirectory;

    /**
     * A reference to the project.
     *
     * @parameter expression="${project}"
     * @required
     */
    private MavenProject project;

    /**
     * The type of output to be generated. (Currently matches the name of the
     * directory in the DocBook XSL distribution holding the relevant
     * stylesheets.)
     *
     * @parameter default-value="html"
     * @required
     */
    private String type;

    /**
     * The extension of the target file
     *
     * @parameter
     */
    private String targetFileExtension;

    /**
     * False if the stylesheet is responsible to create the output file(s) using its own naming scheme.
     *
     * @parameter default-value=true
     */
    private boolean useStandardOutput = true;

    /**
     * An XPath expression for selecting the description.
     */
    protected DOMXPath selectDescription;

    /**
     * An XPath expression for selecting the datatype.
     */
    private DOMXPath selectType;

    /**
     * The directory in the jar file in which the DocBook XSL artifacts will be
     * stored.
     *
     * @parameter default-value="docbook"
     */
    private String stylesheetTargetRoot;

    /**
     * The location of the stylesheet in the destination jar file. Normally this
     * would be derived from the {@link #stylesheetTargetRoot} and
     * {@link #stylesheetPath}. By default:
     * <code>${stylesheetTargetRoot}/${stylesheetPath}</code>.
     *
     * @parameter
     */
    private String stylesheetTargetLocation;

    /**
     * The default location of the stylesheet within the distribution. By
     * default: <code>${type}/docbook.xsl</code>.
     *
     * @parameter
     */
    private String stylesheetPath;

    /**
     * The groupId of any results coming out of this plugin.
     *
     * @parameter expression="net.sf.docbook";
     */
    private String groupId;

    /**
     * The version of the DocBook XSL stylesheets.
     *
     */
    private String version;

    /**
     * The DocBook-XSL distribution. By default
     * <code>${basedir}/lib/docbook-xsl-${version}.zip</code>.
     *
     */
    private File distribution;

    /**
     * The root directory within the source zip file containing the DocBook XSL
     * stylesheet distribution. Default: <code>docbook-xsl-${version}/</code>.
     *
     * @parameter
     */
    private String sourceRootDirectory = "docbook/";

    /**
     * A comma-separated list of properties that should be exluded from the
     * generated code.
     *
     * @parameter
     */
    protected String excludedProperties;

    public GeneratorMojo(  )
    {
        try
        {
            selectDescription = new DOMXPath( "//refsection[position()=1]/para[position()=1]/text()" );
            selectType = new DOMXPath( "//refmiscinfo[@class='other' and @otherclass='datatype']/text()" );
        } catch ( JaxenException e )
        {
            throw new IllegalStateException( "Failed to parse XPath expression." );

            // This would render the object to be unusable.
        }
    }

    // JavaDoc inherited
    public void execute(  )
                 throws MojoExecutionException, MojoFailureException
    {
        completeConfiguration(  );
        generateSourceCode(  );
    }

    /**
     * Completes configuration.
     */
    private void completeConfiguration(  )
                                throws MojoExecutionException
    {
        if ( distribution == null )
        {
            boolean found = false;

            Set artifacts = project.getDependencyArtifacts(  );

            if ( artifacts != null )
            {
                Iterator i = artifacts.iterator(  );

                while ( i.hasNext(  ) )
                {
                    Artifact artifact = (Artifact) i.next(  );

                    if ( "net.sf.docbook".equals( artifact.getGroupId(  ) ) &&
                             "docbook-xsl".equals( artifact.getArtifactId(  ) ) )
                    {
                        distribution = artifact.getFile(  );
                        version = artifact.getVersion(  );
                        found = true;
                        getLog(  )
                            .debug( "Docbook artifact used for generation: " + artifact.getGroupId(  ) + ":" +
                                    artifact.getArtifactId(  ) + ":" + artifact.getVersion(  ) + ":" +
                                    artifact.getClassifier(  ) );

                        break;
                    }
                }
            }

            if ( ! found )
            {
                throw new MojoExecutionException( "Unable to find a valid docbook depencency artifact" );
            }
        }

        if ( stylesheetPath == null )
        {
            // ${type}/docbook.xsl
            stylesheetPath = type + "/docbook.xsl";
        }

        if ( stylesheetTargetLocation == null )
        {
            // ${stylesheetTargetRoot}/${stylesheetPath}
            stylesheetTargetLocation = stylesheetTargetRoot + "/" + stylesheetPath;
        }
    }

    /**
     * Constructs a new {@link DocumentBuilder}.
     *
     * @return A new {@link DocumentBuilder} instance.
     * @throws ParserConfigurationException
     *             If we can't construct a parser.
     */
    private DocumentBuilder createDocumentBuilder(  )
                                           throws ParserConfigurationException
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(  );

        return factory.newDocumentBuilder(  );
    }

    /**
     * Generate the source code of the plugin supporting the {@link #type}.
     *
     * @throws MojoExecutionException
     *             If we fail to generate the source code.
     */
    private void generateSourceCode(  )
                             throws MojoExecutionException
    {
        File sourcesDir = new File( targetDirectory,
                                    getPackageName(  ).replace( '.', '/' ) );

        try
        {
            FileUtils.forceMkdir( sourcesDir );
        } catch ( IOException ioe )
        {
            throw new MojoExecutionException( "Can't create directory for sources.", ioe );
        }

        ClassLoader loader = this.getClass(  ).getClassLoader(  );
        InputStream in = loader.getResourceAsStream( "plugins.stg" );
        Reader reader = new InputStreamReader( in );
        StringTemplateGroup group = new StringTemplateGroup( reader );
        StringTemplate template = group.getInstanceOf( "plugin" );
        File targetFile = new File( sourcesDir, getClassName(  ) + ".java" );
        Specification specification = null;

        try
        {
            specification = createSpecification(  );
            getLog(  ).info( "Number of parameters: " + specification.getParameters(  ).size(  ) );
            template.setAttribute( "spec", specification );
            FileUtils.writeStringToFile( targetFile,
                                         template.toString(  ),
                                         null );
        } catch ( IOException ioe )
        {
            if ( specification == null )
            {
                throw new MojoExecutionException( "Failed to read parameters.", ioe );
            } else
            {
                throw new MojoExecutionException( "Failed to create " + targetFile + ".", ioe );
            }
        }

        project.addCompileSourceRoot( targetDirectory.getAbsolutePath(  ) );
    }

    /**
     * Creates the {@link Specification} used for generating the plugin code.
     *
     * @return The {@link Specification} uesd for generating the plugin source
     *         code.
     * @throws MojoExecutionException
     *             If the {@link Specification} cannot be created.
     */
    private Specification createSpecification(  )
                                       throws MojoExecutionException
    {
        String stylesheetLocation =
            ( stylesheetTargetLocation == null ) ? ( stylesheetTargetRoot + "/" + type + "/docbook.xsl" )
                                                 : stylesheetTargetLocation;
        Specification specification = new Specification(  );
        specification.setType( type );
        specification.setStylesheetLocation( stylesheetLocation );
        specification.setClassName( getClassName(  ) );
        specification.setSuperClassName( superClassName );
        specification.setPackageName( getPackageName(  ) );
        specification.setDocbookXslVersion( version );
        specification.setPluginSuffix( pluginSuffix );
        specification.setParameters( extractParameters(  ) );
        specification.setUseStandardOutput( useStandardOutput );
        specification.setTargetFileExtension(targetFileExtension);

        return specification;
    }

    /**
     * Extracts the {@link Parameter} definitions from the stylesheets.
     *
     * @return A <code>List</code> of {@link Parameter} elements defining the
     *         parameters of the plugin.
     * @throws MojoExecutionException
     *             If we can't create the list of parameters.
     */
    private List extractParameters(  )
                            throws MojoExecutionException
    {
        String stylesheetURL = createURL( stylesheetPath );
        Collection parameterNames = getParameterNames( stylesheetURL );
        List parameters = new ArrayList(  );
        Iterator iterator = parameterNames.iterator(  );
        Collection excluded = getExcludedProperties(  );

        while ( iterator.hasNext(  ) )
        {
            String name = (String) iterator.next(  );

            if ( ! excluded.contains( name ) )
            {
                parameters.add( extractParameter( name ) );
            }
        }

        return parameters;
    }

    /**
     * Returns a <code>List</code> of property names that will be excluded
     * from the code generation.
     *
     * @return A <code>List</code> of property names, identifying the
     *         properties that must be excluded from code generation.
     */
    private List getExcludedProperties(  )
    {
        List excluded;

        if ( excludedProperties != null )
        {
            excluded = Arrays.asList( excludedProperties.split( ",[ ]*" ) );
        } else
        {
            excluded = Collections.EMPTY_LIST;
        }

        return excluded;
    }

    /**
     * Extracts the Parameter metadata from the parameter metadata file.
     *
     * @param name
     *            The name of the (XSLT) parameter.
     * @return The Parameter object holding the metadata.
     */
    private Parameter extractParameter( String name )
                                throws MojoExecutionException
    {
        String url = createURL( "params/" + name + ".xml" );
        Parameter parameter = new Parameter(  );
        parameter.setName( name );

        try
        {
            DocumentBuilder builder = createDocumentBuilder(  );
            Document document = builder.parse( url );
            Node node = (Node) selectDescription.selectSingleNode( document );

            if ( node == null )
            {
                getLog(  ).warn( "Failed to parse description for " + name );

                return parameter;
            }

            String result = node.getNodeValue(  );
            result = result.substring( 0, result.indexOf( '.' ) + 1 );
            result = result.trim(  );
            result = result.replace( '\n', ' ' );
            parameter.setDescription( result );
            node = (Node) selectType.selectSingleNode( document );

            if ( node != null )
            {
                parameter.setTypeFromRefType( node.getNodeValue(  ) );
            } else
            {
                getLog(  ).warn( "Missing type info for " + name );
            }
        } catch ( FileNotFoundException fnfe )
        {
            logMissingDescription( name, fnfe );
        } catch ( IOException ioe )
        {
            logMissingDescription( name, ioe );
        } catch ( ParserConfigurationException pce )
        {
            logMissingDescription( name, pce );
        } catch ( SAXException se )
        {
            logMissingDescription( name, se );
        } catch ( JaxenException je )
        {
            logMissingDescription( name, je );
        }

        return parameter;
    }

    /**
     * Logs a missing description of a parameter.
     *
     * @param name
     *            The name of the parameter.
     * @param cause
     *            The exception causing the problem.
     */
    private void logMissingDescription( String name, Throwable cause )
    {
        getLog(  ).warn( "Failed to obtain description for " + name );
        getLog(  ).debug( cause );
    }

    /**
     * Returns a String version of the URL pointing the specific file in the
     * distribution. (Note that the filename passed in is expected to leave out
     * the version specific directory name. It will be included by this
     * operation.)
     *
     * @param filename
     *            The filename for which we need a URL.
     * @return A URL pointing to the specific filename.
     * @throws MojoExecutionException
     *             If we can't create a URL from the filename passed in.
     */
    private String createURL( String filename )
                      throws MojoExecutionException
    {
        try
        {
            StringBuilder builder = new StringBuilder(  );
            builder.append( "jar:" );
            builder.append( distribution.toURL(  ).toExternalForm(  ) );
            builder.append( "!/" );
            builder.append( sourceRootDirectory );
            builder.append( filename );

            return builder.toString(  );
        } catch ( MalformedURLException mue )
        {
            throw new MojoExecutionException( "Failed to construct URL for " + filename + ".", mue );
        }
    }

    /**
     * Returns a {@link Collection} of all parameter names defined in the
     * stylesheet or in one of the stylesheets imported or included in the
     * stylesheet.
     *
     * @param url
     *            The location of the stylesheet to analyze.
     * @return A {@link Collection} of all parameter names found in the
     *         stylesheet pinpointed by the <code>url</code> argument.
     *
     * @throws MojoExecutionException
     *             If the operation fails to detect parameter names.
     */
    private Collection getParameterNames( String url )
                                  throws MojoExecutionException
    {
        ByteArrayOutputStream out = null;

        try
        {
            Transformer transformer = createParamListTransformer(  );
            Source source = new StreamSource( url );
            out = new ByteArrayOutputStream(  );

            Result result = new StreamResult( out );
            transformer.transform( source, result );
            out.flush(  );

            String[] paramNames = new String( out.toByteArray(  ) ).split( "\n" );

            return new HashSet( Arrays.asList( paramNames ) );
        } catch ( IOException ioe )
        {
            // Impossible, but let's satisfy PMD and FindBugs
            getLog(  ).warn( "Failed to flush ByteArrayOutputStream." );
        } catch ( TransformerConfigurationException tce )
        {
            throw new MojoExecutionException( "Failed to create Transformer for retrieving parameter names", tce );
        } catch ( TransformerException te )
        {
            throw new MojoExecutionException( "Failed to apply Transformer for retrieving parameter names.", te );
        } finally
        {
            IOUtils.closeQuietly( out );
        }

        return Collections.EMPTY_SET;
    }

    /**
     * Creates a {@link Transformer} with the ability to transitively detect the
     * names of all parameters defined on global level for a certain XSLT
     * stylesheet.
     *
     * @return The {@link Transformer} that takes a stylesheet as input, and
     *         outputs a text stream containing the parameter names.
     * @throws TransformerConfigurationException
     *             If we can't create the <code>Transformer</code>.
     */
    private Transformer createParamListTransformer(  )
                                            throws TransformerConfigurationException
    {
        TransformerFactory factory = new TransformerFactoryImpl(  );
        URL stylesheet = Thread.currentThread(  ).getContextClassLoader(  ).getResource( TRANSFORMER_LOCATION );
        Source source = new StreamSource( stylesheet.toExternalForm(  ) );

        return factory.newTransformer( source );
    }

    /**
     * Returns the package name to be used for the generated plugin.
     *
     * @return The package name to be used for the generated plugin.
     */
    private String getPackageName(  )
    {
        return ( packageName != null ) ? packageName : groupId;
    }

    /**
     * Returns the name of the class that will be used. If {@link #className} is
     * set, then that name will be used instead of the default.
     *
     * @return The name of the class for the Mojo being generated.
     */
    private String getClassName(  )
    {
        if ( className == null )
        {
            StringBuffer builder = new StringBuffer(  );
            builder.append( "Docbkx" );
            builder.append( Character.toUpperCase( type.charAt( 0 ) ) );
            builder.append( type.substring( 1 ) );
            builder.append( "Mojo" );

            return builder.toString(  );
        } else
        {
            return className;
        }
    }
}
TOP

Related Classes of com.agilejava.maven.docbkx.GeneratorMojo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.