zcommon/src/main/java/org/zkoss/idom/transform/Transformer.java

Summary

Maintainability
A
25 mins
Test Coverage
/* Transformer.java

    Purpose:
        
    Description:
        
    History:
        Mon May 13 17:31:01  2002, Created by andrewho
        May 19 2003, Modified by tomyeh to make API more complete
        and let developer know about Source and Result

Copyright (C) 2002 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
    This program is distributed under LGPL Version 2.1 in the hope that
    it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.idom.transform;

import java.util.Properties;

import javax.xml.XMLConstants;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;

import org.zkoss.idom.DocType;
import org.zkoss.idom.Document;
import org.zkoss.idom.Element;
import org.zkoss.idom.input.SAXHandler;

/**
 * Transforms an iDOM Document.
 *
 * @author <a href="mailto:andrewho@potix.com">andrewho@potix.com</a>
 * @author tomyeh
 */
public class Transformer {
    /** The transformer. */
    private final javax.xml.transform.Transformer _tfmr;
    /** Whether to output doc-type. */
    private boolean _outDocType = true;

    /**
     * Transformer constructor without stylesheet.
     */
    public Transformer()
    throws TransformerConfigurationException {
        final TransformerFactory tf = initFactory();
        _tfmr = tf.newTransformer();
    }
    /** Constructs a transformer with a stylesheet in form of Source.
     *
     * <p>Examples:<br>
     * <dl>
     * <dt>File file</dt>
     * <dd>new Transformer(new javax.xml.transform.stream.StreamSource(file));</dd>
     * <dt>Reader reader</dt>
     * <dd>new Transformer(new javax.xml.transform.stream.StreamSource(reader));</dd>
     * <dt>URL url</dt>
     * <dd>new Transformer(new javax.xml.transform.stream.StreamSource(url.openStream()));</dd>
     * <dt>iDOM or DOM dom</dt>
     * <dd>new Transformer(new javax.xml.transform.dom.DOMSource(dom));</dd>
     * </dl>
     *
     * <p>See javax.xml.transform.stream.StreamSource
     * and javax.xml.transform.dom.DOMSource
     */
    public Transformer(Source source)
    throws TransformerConfigurationException {
        final TransformerFactory tf = initFactory();
        _tfmr = source != null ? tf.newTransformer(source): tf.newTransformer();
    }

    // Fix XXE issue
    private TransformerFactory initFactory() {
        final TransformerFactory tf = TransformerFactory.newInstance();
        try {
            // Prevents external entity attacks (XXE)
            tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
            tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");

            // Alternatively, you can use the following settings
            // These settings might be required for some Java versions
            tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            tf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            tf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            tf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        } catch (TransformerConfigurationException e) {
            // Handle the potential exception here
        }
        return tf;
    }

    /** Sets whether to output the doc type.
     * Default: true.
     *
     * <p>Useful only if {@link Document} is used in transform(), e.g.,
     * {@link #transform(Document, Result)}.
     * If not, you have to set OutputKeys.DOCTYPE_SYSTEM and
     * OutputKeys.DOCTYPE_PUBLIC explicitly (thru {@link #getTransformer}).
     */
    public final void enableOutputDocType(boolean enable) {
        _outDocType = enable;
    }
    private final Document processDocType(final Document doc) {
        if (!_outDocType)
            return doc;

        final DocType dt = doc.getDocType();
        if (dt == null)
            return doc;

        final String sysid = dt.getSystemId();
        if (sysid != null && sysid.length() > 0)
            _tfmr.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, sysid);
        final String pubid = dt.getPublicId();
        if (pubid != null && pubid.length() > 0)
            _tfmr.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, pubid);
        return doc;
    }
    /** Returns the JAXP transformer encapsulated by this object.
     * Then, you can use it to set properties, listener and so on.
     * <p>Notice: OutputKeys.DOCTYPE_SYSTEM and OutputKeys.DOCTYPE_PUBLIC
     * are set automatically if outDocType is true when constructing
     * this object and {@link Document} is used to transform.
     */
    public final javax.xml.transform.Transformer getTransformer() {
        return _tfmr;
    }

    /**
     * Transforms from a source to a result.
     *
     * and javax.xml.transform.dom.DOMSource
     * @param source the source
     * @param result the result
     * @see #transform(Document, Result)
     */
    public final void transform(Source source, Result result)
    throws TransformerException {
        _tfmr.transform(source, result);
    }
    /**
     * Transforms from an iDOM document to a result.
     *
     * <p>Examples:<br>
     * <dl>
     * <dt>File file</dt>
     * <dd>transformer.transform(doc, new javax.xml.transform.stream.StreamResult(file));</dd>
     * <dt>Writer writer</dt>
     * <dd>transformer.transform(doc, new javax.xml.transform.stream.StreamResult(writer));</dd>
     * <dt>URL url</dt>
     * <dd>No simple way yet.</dd>
     * <dt>iDOM or DOM dom</dt>
     * <dd>{@link #transform(Source)} and {@link #transform(Document)}.</dd>
     * <dt>String systemId</dt>
     * <dd>transformer.transform(doc, new javax.xml.transform.stream.StreamResult(systemId));</dd>
     * </dl>
     *
     * <p>See javax.xml.transform.stream.StreamResult
     * and javax.xml.transform.dom.DOMResult
     *
     * @param doc the source document
     * @param result the result
     * @see #transform(Source, Result)
     * @see #transform(Document)
     */
    public final void transform(Document doc, Result result)
    throws TransformerException {
        _tfmr.transform(new DOMSource(processDocType(doc)), result);
    }
    /**
     * Transforms from an iDOM element to a result.
     *
     * @param elm the source element
     * @param result the result
     */
    public final void transform(Element elm, Result result)
    throws TransformerException {
        _tfmr.transform(new DOMSource(elm), result);
    }
    /**
     * Transforms a source and returns the transformed result as
     * an iDOM Document.
     *
     * @param source the source
     * @return the transformed result in an iDOM document
     */
    public final Document transform(Source source)
    throws TransformerException {
        final SAXHandler hdl = new SAXHandler();
        _tfmr.transform(source, new SAXResult(hdl));
        return hdl.getDocument();
    }
    /**
     * Transforms an iDOM document and returns the transformed result as
     * another iDOM Document.
     *
     * @param doc the source document
     * @return the transformed result in an iDOM document
     */
    public final Document transform(Document doc)
    throws TransformerException {
        return transform(new DOMSource(processDocType(doc)));
    }
    /**
     * Transforms an iDOM element and returns the transformed result as
     * another iDOM Document.
     *
     * @param elm the source element
     * @return the transformed result in an iDOM document
     */
    public final Document transform(Element elm)
    throws TransformerException {
        return transform(new DOMSource(elm));
    }
    /** Get a copy of the output properties for the transformation.
     */
    public final Properties getOutputProperties() {
        return _tfmr.getOutputProperties();
    }
    /** Get an output property that is in effect for the transformation.
     */
    public final String getOutputProperty(String name) {
        return _tfmr.getOutputProperty(name);
    }

    /** Set an output property that will be in effect for the transformation.
     */
    public final void setOutputProperty(String name, String value) {
        _tfmr.setOutputProperty(name, value);
    }
    /** Set the output properties for the transformation.
     */
    public final void setOutputProperties(Properties props) {
        _tfmr.setOutputProperties(props);
    }

    /** Get the error event handler in effect for the transformation. 
     */
    public final ErrorListener getErrorListener() {
        return _tfmr.getErrorListener();
    }
    /** Set the error event listener in effect for the transformation.
     */
    public final void setErrorListener(ErrorListener listener) {
        _tfmr.setErrorListener(listener);
    }
}