001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2015 ForgeRock AS.
015 */
016
017package org.forgerock.doc.maven.utils;
018
019import org.apache.commons.io.DirectoryWalker;
020import org.apache.commons.io.FileUtils;
021
022import javax.xml.transform.Source;
023import javax.xml.transform.Transformer;
024import javax.xml.transform.TransformerConfigurationException;
025import javax.xml.transform.TransformerException;
026import javax.xml.transform.TransformerFactory;
027import javax.xml.transform.stream.StreamResult;
028import javax.xml.transform.stream.StreamSource;
029import java.io.ByteArrayInputStream;
030import java.io.File;
031import java.io.FileFilter;
032import java.io.IOException;
033import java.nio.charset.Charset;
034import java.util.ArrayList;
035import java.util.Arrays;
036import java.util.Collection;
037import java.util.List;
038
039/**
040 * Apply and XSL transformation to DocBook XML files.
041 */
042public class XmlTransformer extends DirectoryWalker<File> {
043
044    /**
045     * Construct an updater to match DocBook XML files.
046     *
047     * <p>
048     *
049     * The files are updated in place.
050     *
051     * <p>
052     *
053     * The XSL resource must be on the classpath,
054     * and so you probably want to extend this with your own transformer class.
055     *
056     * @param  filterToMatch    Filter to match XML files.
057     * @param  xslResource      Path to XSL resource.
058     */
059    public XmlTransformer(final FileFilter filterToMatch, final String xslResource) {
060        super(filterToMatch, -1);
061
062        try {
063            this.transformer = getTransformer(xslResource);
064        } catch (IOException ie) {
065            System.err.println(Arrays.toString(ie.getStackTrace()));
066            System.exit(1);
067        } catch (TransformerConfigurationException tce) {
068            System.err.println(Arrays.toString(tce.getStackTrace()));
069            System.exit(1);
070        }
071    }
072
073    /**
074     * Construct an updater to match DocBook XML files.
075     *
076     * <p>
077     *
078     * The files are updated in place.
079     *
080     * @param  filterToMatch    Filter to match XML files.
081     * @param  xsl              XSL as string.
082     */
083    public XmlTransformer(final String xsl, final FileFilter filterToMatch) {
084        super(filterToMatch, -1);
085
086        try {
087            this.transformer = getTransformerForString(xsl);
088        } catch (TransformerConfigurationException tce) {
089            System.err.println(Arrays.toString(tce.getStackTrace()));
090            System.exit(1);
091        }
092    }
093
094    private Transformer transformer;
095
096    private Transformer getTransformer(final String xslResource)
097            throws IOException, TransformerConfigurationException {
098        TransformerFactory factory = TransformerFactory.newInstance();
099        Source xslt = new StreamSource(getClass().getResource(xslResource).openStream());
100        return factory.newTransformer(xslt);
101    }
102
103    private Transformer getTransformerForString(final String xsl) throws TransformerConfigurationException {
104        TransformerFactory factory = TransformerFactory.newInstance();
105        Source xslt = new StreamSource(new ByteArrayInputStream(xsl.getBytes(Charset.forName("UTF-8"))));
106        return factory.newTransformer(xslt);
107    }
108
109    /**
110     * Update files that match the filter.
111     *
112     * @param startDirectory
113     *            Base directory under which to update files, recursively
114     * @return List of updated files
115     * @throws java.io.IOException
116     *             Something went wrong changing a file's content.
117     */
118    public final List<File> update(final File startDirectory) throws IOException {
119        List<File> results = new ArrayList<File>();
120        walk(startDirectory, results);
121        return results;
122    }
123
124    /**
125     * Update files that match, adding them to the results.
126     *
127     * @param file
128     *            File to update
129     * @param depth
130     *            Not used
131     * @param results
132     *            List of files updated
133     * @throws java.io.IOException
134     *             Something went wrong changing a file's content.
135     */
136    @Override
137    protected final void handleFile(final File file,
138                                    final int depth,
139                                    final Collection<File> results)
140            throws IOException {
141        if (file.isFile()) {
142            try {
143                Source xml = new StreamSource(file);
144                File tmpFile = File.createTempFile(file.getName(), ".tmp");
145                transformer.transform(xml, new StreamResult(tmpFile));
146
147                FileUtils.deleteQuietly(file);
148                FileUtils.moveFile(tmpFile, file);
149                results.add(file);
150            } catch (TransformerException te) {
151                throw new IOException(te.getMessageAndLocation(), te);
152            }
153        }
154    }
155}