XmlTransformer.java

/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2013-2015 ForgeRock AS.
 */

package org.forgerock.doc.maven.utils;

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

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;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Apply and XSL transformation to DocBook XML files.
 */
public class XmlTransformer extends DirectoryWalker<File> {

    /**
     * Construct an updater to match DocBook XML files.
     *
     * <p>
     *
     * The files are updated in place.
     *
     * <p>
     *
     * The XSL resource must be on the classpath,
     * and so you probably want to extend this with your own transformer class.
     *
     * @param  filterToMatch    Filter to match XML files.
     * @param  xslResource      Path to XSL resource.
     */
    public XmlTransformer(final FileFilter filterToMatch, final String xslResource) {
        super(filterToMatch, -1);

        try {
            this.transformer = getTransformer(xslResource);
        } catch (IOException ie) {
            System.err.println(Arrays.toString(ie.getStackTrace()));
            System.exit(1);
        } catch (TransformerConfigurationException tce) {
            System.err.println(Arrays.toString(tce.getStackTrace()));
            System.exit(1);
        }
    }

    /**
     * Construct an updater to match DocBook XML files.
     *
     * <p>
     *
     * The files are updated in place.
     *
     * @param  filterToMatch    Filter to match XML files.
     * @param  xsl              XSL as string.
     */
    public XmlTransformer(final String xsl, final FileFilter filterToMatch) {
        super(filterToMatch, -1);

        try {
            this.transformer = getTransformerForString(xsl);
        } catch (TransformerConfigurationException tce) {
            System.err.println(Arrays.toString(tce.getStackTrace()));
            System.exit(1);
        }
    }

    private Transformer transformer;

    private Transformer getTransformer(final String xslResource)
            throws IOException, TransformerConfigurationException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(getClass().getResource(xslResource).openStream());
        return factory.newTransformer(xslt);
    }

    private Transformer getTransformerForString(final String xsl) throws TransformerConfigurationException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(new ByteArrayInputStream(xsl.getBytes(Charset.forName("UTF-8"))));
        return factory.newTransformer(xslt);
    }

    /**
     * Update files that match the filter.
     *
     * @param startDirectory
     *            Base directory under which to update files, recursively
     * @return List of updated files
     * @throws java.io.IOException
     *             Something went wrong changing a file's content.
     */
    public final List<File> update(final File startDirectory) throws IOException {
        List<File> results = new ArrayList<File>();
        walk(startDirectory, results);
        return results;
    }

    /**
     * Update files that match, adding them to the results.
     *
     * @param file
     *            File to update
     * @param depth
     *            Not used
     * @param results
     *            List of files updated
     * @throws java.io.IOException
     *             Something went wrong changing a file's content.
     */
    @Override
    protected final void handleFile(final File file,
                                    final int depth,
                                    final Collection<File> results)
            throws IOException {
        if (file.isFile()) {
            try {
                Source xml = new StreamSource(file);
                File tmpFile = File.createTempFile(file.getName(), ".tmp");
                transformer.transform(xml, new StreamResult(tmpFile));

                FileUtils.deleteQuietly(file);
                FileUtils.moveFile(tmpFile, file);
                results.add(file);
            } catch (TransformerException te) {
                throw new IOException(te.getMessageAndLocation(), te);
            }
        }
    }
}