Package com.agilejava.docbkx.maven

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

package com.agilejava.docbkx.maven;

/*
* 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.
*/

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.VariableResolver;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
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.URIResolver;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.antrun.AntPropertyHelper;
import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.PropertyHelper;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.types.Path;
import org.apache.xerces.jaxp.SAXParserFactoryImpl;
import org.apache.xml.resolver.CatalogManager;
import org.apache.xml.resolver.tools.CatalogResolver;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.dom.DOMXPath;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.icl.saxon.Controller;
import com.icl.saxon.TransformerFactoryImpl;

/**
* The abstract Mojo base for concrete Mojos that generate some kind of output
* format from DocBook. This Mojo will search documents in the directory
* returned by {@link #getTargetDirectory()}, and apply the stylesheets on
* these documents. This Mojo will be subclassed by Mojo's that generate a
* particular type of output.
*
* @author Wilfred Springer
*/
public abstract class AbstractTransformerMojo extends AbstractMojo {

    /**
     * Builds the actual output document.
     */
    public void execute() throws MojoExecutionException, MojoFailureException {
        preProcess();
        File targetDirectory = getTargetDirectory();
        File sourceDirectory = getSourceDirectory();
        if (!sourceDirectory.exists()) {
            return; // No sources, so there is nothing to render.
        }
        if (!targetDirectory.exists()) {
            FileUtils.mkdir(targetDirectory.getAbsolutePath());
        }
        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setBasedir(sourceDirectory);
        scanner.setIncludes(getIncludes());
        scanner.scan();
        String[] included = scanner.getIncludedFiles();
        CatalogManager catalogManager = createCatalogManager();
        CatalogResolver catalogResolver = new CatalogResolver(catalogManager);
        URIResolver uriResolver = null;
        try {
            URL url = getNonDefaultStylesheetURL() == null ? getDefaultStylesheetURL()
                    : getNonDefaultStylesheetURL();
            uriResolver = new StylesheetResolver("urn:docbkx:stylesheet",
                    new StreamSource(url.openStream(), url.toExternalForm()),
                    catalogResolver);
        } catch (IOException ioe) {
            throw new MojoExecutionException("Failed to read stylesheet.", ioe);
        }
        Transformer transformer = createTransformer(uriResolver);
        transformer.setURIResolver(uriResolver);
        EntityResolver resolver = catalogResolver;
        InjectingEntityResolver injectingResolver = null;
        if (getEntities() != null) {
            injectingResolver = new InjectingEntityResolver(getEntities(),
                    resolver, getType(), getLog());
            resolver = injectingResolver;
        }
        SAXParserFactory factory = createParserFactory();
        for (int i = included.length - 1; i >= 0; i--) {
            try {
                if (injectingResolver != null) {
                    injectingResolver.forceInjection();
                }
                String filename = included[i];
                String targetFilename = filename.substring(0,
                        filename.length() - 4)
                        + "." + getTargetFileExtension();
                File targetFile = new File(targetDirectory, targetFilename);
                File sourceFile = new File(sourceDirectory, filename);
                Result result = new StreamResult(targetFile);
                XMLReader reader = factory.newSAXParser().getXMLReader();
                reader.setEntityResolver(resolver);
                PreprocessingFilter filter = new PreprocessingFilter(reader);
                ProcessingInstructionHandler resolvingHandler = new ExpressionHandler(
                        new VariableResolver() {

                            private Map tree = ExpressionUtils.createTree(getMavenProject().getProperties());

                            public Object resolveVariable(String name)
                                    throws ELException {
                                if ("project".equals(name)) {
                                    return getMavenProject();
                                } else {
                                    return tree.get(name);
                                }
                            }

                        }, getLog());
                filter.setHandlers(Arrays
                        .asList(new Object[] { resolvingHandler }));
                filter.setEntityResolver(resolver);
                getLog().info("Processing " + filename);
                SAXSource xmlSource = new SAXSource(filter, new InputSource(
                        sourceFile.getAbsolutePath()));
                adjustTransformer(transformer, filename, targetFile);
                transformer.transform(xmlSource, result);
                postProcessResult(targetFile);
            } catch (SAXException saxe) {
                throw new MojoExecutionException("Failed to parse "
                        + included[i] + ".", saxe);
            } catch (TransformerException te) {
                throw new MojoExecutionException("Failed to transform "
                        + included[i] + ".", te);
            } catch (ParserConfigurationException pce) {
                throw new MojoExecutionException("Failed to construct parser.",
                        pce);
            }
        }
        postProcess();
    }

    /**
     * Returns the SAXParserFactory used for constructing parsers.
     *
     */
    private SAXParserFactory createParserFactory() {
        SAXParserFactory factory = new SAXParserFactoryImpl();
        factory.setXIncludeAware(getXIncludeSupported());
        return factory;
    }

    /**
     * Returns a boolean indicting if XInclude should be supported.
     *
     * @return A boolean indicating if XInclude should be supported.
     */
    protected abstract boolean getXIncludeSupported();

    /**
     * The stylesheet location override by a class in the mojo hierarchy.
     *
     * @return The location of the stylesheet set by one of the superclasses, or
     *         <code>null</code>.
     */
    protected String getNonDefaultStylesheetLocation() {
        return null;
    }

    /**
     * The operation to override when it is required to make some adjustments to
     * the {@link Transformer} right before it is applied to a certain source
     * file. The two parameters provide some context, allowing implementers to
     * respond to specific conditions for specific files.
     *
     * @param transformer
     *            The <code>Transformer</code> that must be adjusted.
     * @param sourceFilename
     *            The name of the source file that is being transformed.
     * @param targetFile
     *            The target File.
     */
    public void adjustTransformer(Transformer transformer,
            String sourceFilename, File targetFile) {
        // To be implemented by subclasses.
    }

    /**
     * Allows subclasses to add their own specific pre-processing logic.
     *
     * @throws MojoExecutionException
     *             If the Mojo fails to pre-process the results.
     */
    public void preProcess() throws MojoExecutionException {
        if (getPreProcess() != null) {
            executeTasks(getPreProcess(), getMavenProject());
        }
    }

    /**
     * Alles classes to add their own specific post-processing logic.
     *
     * @throws MojoExecutionException
     *             If the Mojo fails to post-process the results.
     */
    public void postProcess() throws MojoExecutionException {
        if (getPostProcess() != null) {
            executeTasks(getPostProcess(), getMavenProject());
        }
    }

    /**
     * Post-processes the file. (Might be changed in the future to except an XML
     * representation instead of a file, in order to prevent the file from being
     * parsed.)
     *
     * @param result
     *            An individual result.
     */
    public void postProcessResult(File result) throws MojoExecutionException {

    }

    /**
     * Returns a <code>Transformer</code> capable of rendering a particular
     * type of output from DocBook input.
     *
     * @param uriResolver
     *
     * @return A <code>Transformer</code> capable of rendering a particular
     *         type of output from DocBook input.
     * @throws MojoExecutionException
     *             If the operation fails to create a <code>Transformer</code>.
     */
    protected Transformer createTransformer(URIResolver uriResolver)
            throws MojoExecutionException {
        URL url = getStylesheetURL();
        try {
            TransformerFactory transformerFactory = new TransformerFactoryImpl();
            transformerFactory.setURIResolver(uriResolver);
            Source source = new StreamSource(url.openStream(), url
                    .toExternalForm());
            Transformer transformer = transformerFactory.newTransformer(source);
            Controller controller = (Controller) transformer;
            try {
                controller.makeMessageEmitter();
                controller.getMessageEmitter().setWriter(new NullWriter());
            } catch (TransformerException te) {
                getLog().error("Failed to redirect xsl:message output.", te);
            }
            return transformer;
        } catch (IOException ioe) {
            throw new MojoExecutionException("Failed to read stylesheet from "
                    + url.toExternalForm(), ioe);
        } catch (TransformerConfigurationException tce) {
            throw new MojoExecutionException(
                    "Failed to build Transformer from " + url.toExternalForm(),
                    tce);
        }
    }

    /**
     * Creates a <code>CatalogManager</code>, used to resolve DTDs and other
     * entities.
     *
     * @return A <code>CatalogManager</code> to be used for resolving DTDs and
     *         other entities.
     */
    protected CatalogManager createCatalogManager() {
        CatalogManager manager = new CatalogManager();
        manager.setIgnoreMissingProperties(true);
        ClassLoader classLoader = Thread.currentThread()
                .getContextClassLoader();
        StringBuffer builder = new StringBuffer();
        boolean first = true;
        try {
            Enumeration enumeration = classLoader.getResources("/catalog.xml");
            while (enumeration.hasMoreElements()) {
                if (!first) {
                    builder.append(';');
                } else {
                    first = false;
                }
                URL resource = (URL) enumeration.nextElement();
                builder.append(resource.toExternalForm());
            }
        } catch (IOException ioe) {
            getLog().warn("Failed to search for catalog files.");
            // Let's be a little tolerant here.
        }
        String catalogFiles = builder.toString();
        if (catalogFiles.length() == 0) {
            getLog().warn("Failed to find catalog files.");
        } else {
            manager.setCatalogFiles(catalogFiles);
        }
        return manager;
    }

    /**
     * Creates a <code>DocumentBuilder</code> to be used to parse DocBook XML
     * documents.
     *
     * @return A <code>DocumentBuilder</code> instance.
     * @throws MojoExecutionException
     *             If we cannot create an instance of the
     *             <code>DocumentBuilder</code>.
     */
    protected DocumentBuilder createDocumentBuilder()
            throws MojoExecutionException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder;
        } catch (ParserConfigurationException pce) {
            throw new MojoExecutionException("Failed to construct parser.", pce);
        }
    }

    /**
     * Creates an instance of an XPath expression for picking the title from a
     * document.
     *
     * @return An XPath expression to pick the title from a document.
     * @throws MojoExecutionException
     *             If the XPath expression cannot be parsed.
     */
    protected XPath createTitleXPath() throws MojoExecutionException {
        try {
            StringBuffer builder = new StringBuffer();
            builder
                    .append("(article/title|article/articleinfo/title|book/title|book/bookinfo/title)[position()=1]");
            return new DOMXPath(builder.toString());
        } catch (JaxenException je) {
            throw new MojoExecutionException("Failed to parse XPath.", je);
        }
    }

    /**
     * Returns the title of the document.
     *
     * @param document
     *            The document from which we want the title.
     * @return The title of the document, or <code>null</code> if we can't
     *         find the title.
     */
    private String getTitle(Document document) throws MojoExecutionException {
        try {
            XPath titleXPath = createTitleXPath();
            Node titleNode = (Node) titleXPath.selectSingleNode(document);
            if (titleNode != null) {
                return titleNode.getNodeValue();
            } else {
                return null;
            }
        } catch (JaxenException je) {
            getLog().debug("Failed to find title of document.");
            return null;
        }
    }

    protected void executeTasks(Target antTasks, MavenProject mavenProject)
            throws MojoExecutionException {
        try {
            ExpressionEvaluator exprEvaluator = (ExpressionEvaluator) antTasks
                    .getProject().getReference("maven.expressionEvaluator");
            Project antProject = antTasks.getProject();
            PropertyHelper propertyHelper = PropertyHelper
                    .getPropertyHelper(antProject);
            propertyHelper.setNext(new AntPropertyHelper(exprEvaluator,
                    getLog()));
            DefaultLogger antLogger = new DefaultLogger();
            antLogger.setOutputPrintStream(System.out);
            antLogger.setErrorPrintStream(System.err);
            antLogger.setMessageOutputLevel(2);
            antProject.addBuildListener(antLogger);
            antProject.setBaseDir(mavenProject.getBasedir());
            Path p = new Path(antProject);
            p.setPath(StringUtils.join(mavenProject.getArtifacts().iterator(),
                    File.pathSeparator));
            antProject.addReference("maven.dependency.classpath", p);
            p = new Path(antProject);
            p.setPath(StringUtils.join(mavenProject
                    .getCompileClasspathElements().iterator(),
                    File.pathSeparator));
            antProject.addReference("maven.compile.classpath", p);
            p = new Path(antProject);
            p.setPath(StringUtils.join(mavenProject
                    .getRuntimeClasspathElements().iterator(),
                    File.pathSeparator));
            antProject.addReference("maven.runtime.classpath", p);
            p = new Path(antProject);
            p.setPath(StringUtils.join(mavenProject.getTestClasspathElements()
                    .iterator(), File.pathSeparator));
            antProject.addReference("maven.test.classpath", p);
            List artifacts = getArtifacts();
            List list = new ArrayList(artifacts.size());
            File file;
            for (Iterator i = artifacts.iterator(); i.hasNext(); list.add(file
                    .getPath())) {
                Artifact a = (Artifact) i.next();
                file = a.getFile();
                if (file == null)
                    throw new DependencyResolutionRequiredException(a);
            }

            p = new Path(antProject);
            p.setPath(StringUtils.join(list.iterator(), File.pathSeparator));
            antProject.addReference("maven.plugin.classpath", p);
            getLog().info("Executing tasks");
            antTasks.execute();
            getLog().info("Executed tasks");
        } catch (Exception e) {
            throw new MojoExecutionException("Error executing ant tasks", e);
        }
    }

    /**
     * Returns the target directory in which all results should be placed.
     *
     * @return The target directory in which all results should be placed.
     */
    protected abstract File getTargetDirectory();

    /**
     * Returns the source directory containing the source XML files.
     *
     * @return The source directory containing the source XML files.
     */
    protected abstract File getSourceDirectory();

    /**
     * Returns the include patterns, as a comma-seperate collection of patterns.
     */
    protected abstract String[] getIncludes();

    /**
     * Returns the URL of the stylesheet. You can override this operation to
     * return a URL pointing to a stylesheet residing on a location that can be
     * adressed by a URL. By default, it will return a stylesheet that will be
     * loaded from the classpath, using the resource name returned by
     * {@link #getStylesheetLocation()}.
     *
     * @return The URL of the stylesheet.
     */
    protected URL getStylesheetURL() {
        URL url = this.getClass().getClassLoader().getResource(
                getStylesheetLocation());
        if (url == null) {
            try {
                return new File(getStylesheetLocation()).toURL();
            } catch (MalformedURLException mue) {
                return null;
            }
        } else {
            return url;
        }
    }

    /**
     * Returns the URL of the default stylesheet.
     *
     * @return The URL of the stylesheet.
     */
    protected URL getNonDefaultStylesheetURL() {
        if (getNonDefaultStylesheetLocation() != null) {
            URL url = this.getClass().getClassLoader().getResource(
                    getNonDefaultStylesheetLocation());
            return url;
        } else {
            return null;
        }
    }

    /**
     * Returns the URL of the default stylesheet.
     *
     * @return The URL of the stylesheet.
     */
    protected URL getDefaultStylesheetURL() {
        URL url = this.getClass().getClassLoader().getResource(
                getDefaultStylesheetLocation());
        return url;
    }

    /**
     * Returns the default stylesheet location within the root of the stylesheet
     * distribution.
     *
     * @return The location of the directory containing the stylesheets.
     */
    protected abstract String getDefaultStylesheetLocation();

    /**
     * Returns the actual stylesheet location.
     *
     * @return The actual stylesheet location.
     */
    protected abstract String getStylesheetLocation();

    /**
     * Returns the extension of the target files, e.g. "html" for HTML files,
     * etc.
     *
     * @return The extension of the target files.
     */
    protected abstract String getTargetFileExtension();

    /**
     * Returns a list of {@link Entity Entities}
     */
    protected abstract List getEntities();

    /**
     * Returns the tasks that should be executed before the transformation.
     *
     * @return The tasks that should be executed before the transformation.
     */
    protected abstract Target getPreProcess();

    /**
     * Returns the tasks that should be executed after the transformation.
     *
     * @return The tasks that should be executed after the transformation.
     */
    protected abstract Target getPostProcess();

    /**
     * Returns a reference to the current project.
     *
     * @return A reference to the current project.
     */
    protected abstract MavenProject getMavenProject();

    /**
     * Returns the plugin dependencies.
     *
     * @return The plugin dependencies.
     */
    protected abstract List getArtifacts();

    /**
     * Returns the type of conversion.
     */
    protected abstract String getType();

}
TOP

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

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.