org.ambraproject.service.XMLServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.service.XMLServiceImpl.java

Source

/*
 * $HeadURL$
 * $Id$
 *
 * Copyright (c) 2006-2011 by Public Library of Science
 *     http://plos.org
 *     http://ambraproject.org
 *
 * 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 org.ambraproject.service;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.ambraproject.ApplicationException;
import org.ambraproject.util.FileUtils;
import org.topazproject.xml.transform.cache.CachedSource;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;

/**
 * Convenience class to aggregate common methods used to deal with XML transforms on articles.
 * Used to transform article with annotations, captions of tables/figures, and citation information.
 *
 * @author Bill OConnor
 * @author Stephen Cheng
 * @author Alex Worden
 * @author Joe Osowski
 *
 */
public class XMLServiceImpl implements XMLService {

    private Configuration configuration;

    private static final Logger log = LoggerFactory.getLogger(XMLServiceImpl.class);

    private File xslDefaultTemplate;
    private Map<String, String> xslTemplateMap;
    private DocumentBuilderFactory factory;
    private String articleRep;
    private Map<String, String> xmlFactoryProperty;

    // designed for Singleton use, set in init(), then Templates are threadsafe for reuse
    private Templates translet; // initialized from xslTemplate, per bean property

    /**
     * Initialization method called by Spring.
     *
     * @throws org.ambraproject.ApplicationException On Template creation Exceptions.
     */
    public void init() throws ApplicationException {
        // set JAXP properties
        System.getProperties().putAll(xmlFactoryProperty);

        // Create a document builder factory and set the defaults
        factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);

        // set the Templates
        final TransformerFactory tFactory = TransformerFactory.newInstance();

        try {
            translet = tFactory.newTemplates(new StreamSource(xslDefaultTemplate));
        } catch (TransformerConfigurationException tce) {
            throw new ApplicationException(tce);
        }
    }

    /**
     * Pass in an XML string fragment, and will return back a string representing the document after
     * going through the XSL transform.
     *
     * @param description
     * @return Transformed document as a String
     * @throws org.ambraproject.ApplicationException
     */
    public String getTransformedDocument(String description) throws ApplicationException {
        try {
            final DocumentBuilder builder = createDocBuilder();
            Document desc = builder.parse(new InputSource(new StringReader("<desc>" + description + "</desc>")));
            return getTransformedDocument(desc);
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("Could not transform document", e);
            }
            throw new ApplicationException(e);
        }
    }

    /**
     * Given an XML Document as input, will return an XML string representing the document after
     * transformation.
     *
     * @param doc
     * @return XML String of transformed document
     * @throws org.ambraproject.ApplicationException
     */
    public String getTransformedDocument(Document doc) throws ApplicationException {
        String transformedString;
        try {
            if (log.isDebugEnabled())
                log.debug("Applying XSLT transform to the document...");

            final DOMSource domSource = new DOMSource(doc);
            final Transformer transformer = getTranslet(doc);
            final Writer writer = new StringWriter(1000);

            transformer.transform(domSource, new StreamResult(writer));
            transformedString = writer.toString();
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
        return transformedString;
    }

    public byte[] getTransformedByArray(byte[] xml) throws ApplicationException {
        try {

            Document doc = createDocBuilder().parse(new ByteArrayInputStream(xml));
            Transformer transformer = this.getTranslet(doc);
            DOMSource domSource = new DOMSource(doc);

            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            transformer.transform(domSource, new StreamResult(bs));
            return bs.toByteArray();
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
    }

    public InputStream getTransformedInputStream(InputStream xml) throws ApplicationException {
        try {
            final Writer writer = new StringWriter(1000);

            Document doc = createDocBuilder().parse(xml);
            Transformer transformer = this.getTranslet(doc);
            DOMSource domSource = new DOMSource(doc);
            transformer.transform(domSource, new StreamResult(writer));
            return new ByteArrayInputStream(writer.toString().getBytes("UTF-8"));
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
    }

    /**
     * Given a string as input, will return an XML string representing the document after
     * transformation.
     *
     * @param description
     * @return string
     * @throws org.ambraproject.ApplicationException
     */
    public String getTransformedDescription(String description) throws ApplicationException {
        String transformedString;
        try {
            final DocumentBuilder builder = createDocBuilder();
            Document desc = builder.parse(new InputSource(new StringReader("<desc>" + description + "</desc>")));
            final DOMSource domSource = new DOMSource(desc);
            final Transformer transformer = getTranslet(desc);
            final Writer writer = new StringWriter();

            transformer.transform(domSource, new StreamResult(writer));
            transformedString = writer.toString();
        } catch (Exception e) {
            throw new ApplicationException(e);
        }

        // Ambra stylesheet leaves "END_TITLE" as a marker for other processes
        transformedString = transformedString.replace("END_TITLE", "");
        return transformedString;
    }

    /**
     * Convenience method to create a DocumentBuilder with the factory configs
     *
     * @return Document Builder
     * @throws javax.xml.parsers.ParserConfigurationException
     */
    public DocumentBuilder createDocBuilder() throws ParserConfigurationException {
        // Create the builder and parse the file
        final DocumentBuilder builder = factory.newDocumentBuilder();
        builder.setEntityResolver(CachedSource.getResolver());
        return builder;
    }

    /**
     * Get a translet, compiled stylesheet, for the xslTemplate. If the doc is null
     * use the default template. If the doc is not null then get the DTD version.
     * IF the DTD version does not exist use the default template else use the
     * template associated with that version.
     *
     * @param  doc  the dtd version of document
     * @return Translet for the xslTemplate.
     * @throws javax.xml.transform.TransformerException TransformerException.
     */
    private Transformer getTranslet(Document doc) throws TransformerException, URISyntaxException {
        Transformer transformer;
        // key is "" if the Attribute does not exist
        String key = (doc == null) ? "default" : doc.getDocumentElement().getAttribute("dtd-version").trim();

        if ((!xslTemplateMap.containsKey(key)) || (key.equalsIgnoreCase(""))) {
            transformer = this.translet.newTransformer();
        } else {
            Templates translet;
            String templateName = xslTemplateMap.get(key);

            File file = getAsFile(templateName);
            if (!file.exists()) {
                file = new File(templateName);
            }
            // set the Templates
            final TransformerFactory tFactory = TransformerFactory.newInstance();
            translet = tFactory.newTemplates(new StreamSource(file));
            transformer = translet.newTransformer();
        }
        transformer.setParameter("pubAppContext", configuration.getString("ambra.platform.appContext", ""));
        return transformer;
    }

    /**
     * Setter for XSL Templates.  Takes in a string as the filename and searches for it in resource
     * path and then as a URI.
     *
     * @param xslTemplate The xslTemplate to set.
     * @throws java.net.URISyntaxException
     */
    @Required
    public void setXslDefaultTemplate(String xslTemplate) throws URISyntaxException {
        File file = getAsFile(xslTemplate);
        if (!file.exists()) {
            file = new File(xslTemplate);
        }
        log.debug("XSL template location = " + file.getAbsolutePath());
        this.xslDefaultTemplate = file;
    }

    /**
    * Setter for XSL Templates.  Takes in a string as the filename and searches for it in resource
    * path and then as a URI.
    *
    * @param xslTemplateMap The xslTemplate to set.
    * @throws java.net.URISyntaxException
    */
    @Required
    public void setXslTemplateMap(Map<String, String> xslTemplateMap) throws URISyntaxException {
        this.xslTemplateMap = xslTemplateMap;
    }

    /**
     * Setter method for configuration. Injected through Spring.
     *
     * @param configuration Ambra configuration
     */
    @Required
    public void setAmbraConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * Setter for article represenation
     *
     * @param articleRep The articleRep to set.
     */
    @Required
    public void setArticleRep(String articleRep) {
        this.articleRep = articleRep;
    }

    /**
     * @param xmlFactoryProperty The xmlFactoryProperty to set.
     */
    @Required
    public void setXmlFactoryProperty(Map<String, String> xmlFactoryProperty) {
        this.xmlFactoryProperty = xmlFactoryProperty;
    }

    /**
     * @return Returns the articleRep.
     */
    public String getArticleRep() {
        return articleRep;
    }

    /**
     * @param filenameOrURL filenameOrURL
     * @throws java.net.URISyntaxException URISyntaxException
     * @return the local or remote file or url as a java.io.File
     */
    public File getAsFile(final String filenameOrURL) throws URISyntaxException {
        final URL resource = getClass().getClassLoader().getResource(filenameOrURL);
        if (null == resource) {
            //access it as a local file resource
            return new File(FileUtils.getFileName(filenameOrURL));
        } else {
            return new File(resource.toURI());
        }
    }
}