All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.commons.SimpleXml Maven / Gradle / Ivy

/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.commons;

import org.xml.sax.SAXException;
import org.xml.sax.Locator;
import org.xml.sax.helpers.AttributesImpl;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerException;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Stack;

/**
 * The SimpleXml class facilitates the creation of an XML document.
 */
public class SimpleXml {

    private boolean indentSet;
    private boolean encodingSet;
    private boolean inited;
    private PrintWriter writer;
    private TransformerHandler handler;
    private Transformer transformer;
    private StreamResult result;
    private Element currentElement;
    private Stack openElements;

    /**
     * Creates a SimpleXml instance using the specified
     * print writer for the output of the XML document.
     * @param w The writer to send the XML document to
     * @throws IOException If initialization fails
     */
    public SimpleXml(PrintWriter w) throws IOException {
        try {
            writer = w;
            handler = ((SAXTransformerFactory)SAXTransformerFactory.newInstance()).
                    newTransformerHandler();
            result = new StreamResult(writer);
            openElements = new Stack();
        } catch (TransformerException te) {
            throw new IOException(te.getMessage());
        }
    }

    /**
     * Returns the print writer used for the output of the XML document.
     * @return The print writer
     */
    public PrintWriter getWriter() {
        return writer;
    }

    /**
     * Defines whether the output should use indetation. Default is
     * true.
     * @param indent true if the output should use indentation,
     *               false otherwise
     * @return The XML writer
     */
    public SimpleXml setIndent(boolean indent) {
        getTransformer().setOutputProperty(OutputKeys.INDENT,
                indent? "yes" : "no");
        indentSet = true;
        return this;
    }

    /**
     * Sets the character encoding of the output. Default is UTF-8.
     * @param encoding The character encoding
     * @return The XML writer
     */
    public SimpleXml setEncoding(String encoding) {
        getTransformer().setOutputProperty(OutputKeys.ENCODING,
                encoding);
        encodingSet = true;
        return this;
    }

    /**
     * Defines whether the XML declaration should be omitted. Default is
     * false.
     * @param omit true if the XML declaration should be omitted,
     *             false otherwise
     * @return The XML writer
     */
    public SimpleXml omitXmlDeclaration(boolean omit) {
        getTransformer().setOutputProperty(
                OutputKeys.OMIT_XML_DECLARATION, omit ? "yes" : "no");
        return this;
    }

    /**
     * Sets the document locator of the output.
     * @param locator The document locator
     * @return The XML writer
     */
    public SimpleXml setDocumentLocator(Locator locator) {
        handler.setDocumentLocator(locator);
        return this;
    }

    /**
     * Opens the XML document. This method is a shorthand for
     * {@link #openDocument()}. 
     * @throws IOException If output fails
     * @return The XML writer
     */
    public SimpleXml open() throws IOException {
        return openDocument();
    }

    /**
     * Opens the XML document.
     * @throws IOException If output fails
     * @return The XML writer
     */
    public SimpleXml openDocument() throws IOException {
        if (!"yes".equals(getTransformer().getOutputProperty(
                OutputKeys.OMIT_XML_DECLARATION))) {
            init();
            try {
                handler.startDocument();
            } catch (SAXException se) {
                throw new IOException(se.getMessage());
            }
        }
        return this;
    }

    /**
     * Opens and returns a new XML element with the specified name.
     * @param name The name of the XML element
     * @return The XML element
     * @throws IOException If output fails
     */
    public Element open(String name) throws IOException {
        return open("", "", name);
    }

    /**
     * Opens and returns a new XML element with the specified name.
     * @param localName The local name of the XML element
     * @param name The name of the XML element
     * @return The XML element
     * @throws IOException If output fails
     */
    public Element open(String localName, String name) throws IOException {
        return open("", localName, name);
    }

    /**
     * Opens and returns a new XML element with the specified name.
     * @param uri The URI of the XML element
     * @param localName The local name of the XML element
     * @param name The name of the XML element
     * @return The XML element
     * @throws IOException If output fails
     */
    public Element open(String uri, String localName, String name)
            throws IOException {
        startElement(currentElement);
        currentElement = openElements.push(new Element(uri, localName, name));
        return currentElement;
    }

    /**
     * Opens and returns a new XML element with the specified name and content.
     * @param name The name of the XML element
     * @param content The content of the XML element
     * @param cdata true if content should be in a CDATA block,
     *              false otherwise
     * @return The XML element
     * @throws IOException If output fails
     */
    public Element open(String name, String content, boolean cdata)
            throws IOException {
        Element element = open("", "", name);
        element.setText(content, cdata);
        return element;
    }

    /**
     * Closes the XML document after closing all open elements.
     * @throws IOException If output fails
     */
    public void closeDocument() throws IOException {
        tidyUp();
        init();
        if (!"yes".equals(getTransformer().getOutputProperty(
                OutputKeys.OMIT_XML_DECLARATION))) {
            try {
                handler.endDocument();
            } catch (SAXException se) {
                throw new IOException(se.getMessage());
            }
        }
    }

    /**
     * Closes the most recent open element.
     * @throws IOException If output fails
     * @return The XML writer
     */
    public SimpleXml close() throws IOException {
        endElement(openElements.pop());
        return this;
    }

    /**
     * Closes all currently open elements.
     * @throws IOException If output fails
     * @return The XML writer
     */
    public SimpleXml tidyUp() throws IOException {
        while (!openElements.empty()) {
            Element element = openElements.pop();
            endElement(element);
        }
        return this;
    }

    /**
     * Initializes the output mechanism and sets defaults.
     */
    private void init() {
        if (!inited) {
            handler.setResult(result);
            if (!indentSet) {
                setIndent(true);
            }
            if (!encodingSet) {
                setEncoding("utf-8");
            }
            inited = true;
        }
    }

    /**
     * Returns the XML transformer.
     * @return The transformer
     */
    private Transformer getTransformer() {
        if (transformer == null) {
            transformer = handler.getTransformer();
        }
        return transformer;
    }

    /**
     * Starts the output of the specified XML element.
     * @param element The XML element
     * @throws IOException If output fails
     */
    private void startElement(Element element) throws IOException {
        if (element == null) {
            return;
        }
        if (!element.isOpened()){
            init();
            try {
                String uri = element.getUri();
                String localName = element.getLocalName();
                String name = element.getName();
                AttributesImpl atts = element.getAttributes();
                handler.startElement(uri, localName, name, atts);
            } catch (SAXException se) {
                throw new IOException(se.getMessage());
            }
            element.setOpened(true);
        }
    }

    /**
     * Ends the output of the specified XML element, removing it from
     * the list of open elements.
     * @param element The XML element
     * @throws IOException If output fails
     */
    private void endElement(Element element) throws IOException {
        if (element == null) {
            return;
        }
        if (!element.isClosed()) {
            startElement(element); // make sure element is started
            init();
            try {
                String uri = element.getUri();
                String localName = element.getLocalName();
                String name = element.getName();
                String content = element.getText();
                if (content != null) {
                    if (element.hasCDATA()) {
                        handler.startCDATA();
                    }
                    handler.characters(content.toCharArray(), 0,
                            content.length());
                    if (element.hasCDATA()) {
                        handler.endCDATA();
                    }
                }
                handler.endElement(uri, localName, name);
            } catch (SAXException se) {
                throw new IOException(se.getMessage());
            }
            element.setClosed(true);
            currentElement = null;
        }
        openElements.remove(element);
    }

    /**
     * The SimpleXml.Element reperesents an XML element.
     */
    public class Element {

        private boolean opened;
        private boolean closed;
        private boolean cdata;
        private AttributesImpl atts;
        private String uri;
        private String localName;
        private String name;
        private String text;

        /**
         * Creates a new SimpleXML.Element instance using the
         * specified URI, local name and name.
         * @param uri The URI of the XML element
         * @param localName The local name of the XML element
         * @param name The name of the XML element
         */
        protected Element(String uri, String localName, String name) {
            this.uri = uri;
            this.localName = localName;
            this.name = name;
            atts = new AttributesImpl();
        }

        /**
         * Adds a new attribute to the XML element. This method is a shorthand
         * for {@link #addAttribute(String, String, String, String, String)}.
         * @param name The name of the attribute
         * @param value The value of the attribute
         * @return The XML element
         */
        public Element attr(String name, String value) {
            return attr("", "", name, value, "CDATA");
        }

        /**
         * Adds a new attribute to the XML element. This method is a shorthand
         * for {@link #addAttribute(String, String, String, String, String)}.
         * @param localName The local name of the attribute
         * @param name The name of the attribute
         * @param value The value of the attribute
         * @return The XML element
         */
        public Element attr(String localName, String name, String value) {
            return attr("", localName, name, value, "CDATA");
        }

        /**
         * Adds a new attribute to the XML element. This method is a shorthand
         * for {@link #addAttribute(String, String, String, String, String)}.
         * @param uri The URI of the attribute
         * @param localName The local name of the attribute
         * @param name The name of the attribute
         * @param value The value of the attribute
         * @param type The type of the attribute
         * @return The XML element
         */
        public Element attr(String uri, String localName, String name,
                                 String value, String type) {
            return addAttribute(uri, localName, name, value, type);
        }

        /**
         * Adds the specified attributes to the XML element.
         * @param atts The attributes
         * @return The XML element
         */
        public Element attrs(String[] ... atts) {
            for (int i = 0; i < atts.length; i++) {
                attr(atts[i][0], atts[i][1]);
            }
            return this;
        }

        /**
         * Adds a new attribute to the XML element.
         * @param uri The URI of the attribute
         * @param localName The local name of the attribute
         * @param name The name of the attribute
         * @param value The value of the attribute
         * @param type The type of the attribute
         * @return The XML element
         */
        public Element addAttribute(String uri, String localName, String name,
                                 String value, String type) {
            if (!opened) {
                atts.addAttribute(uri, localName, name, type, value);
            }
            return this;
        }

        /**
         * Sets the content of the XML element. This method is a shorthand
         * for {@link #setText(String, boolean)}.
         * @param text The content
         * @return The XML element
         */
        public Element text(String text) {
            return text(text, false);
        }

        /**
         * Sets the content of the XML element. This method is a shorthand
         * for {@link #setText(String, boolean)}.
         * @param text The content
         * @param cdata true if content should be in a CDATA block,
         *              false otherwise
         * @return The XML element
         */
        public Element text(String text, boolean cdata) {
            return setText(text, cdata);
        }

        /**
         * Sets the content of the XML element.
         * @param text The content
         * @param cdata true if content should be in a CDATA block,
         *              false otherwise
         * @return The XML element
         */
        public Element setText(String text, boolean cdata) {
            if (!closed) {
                this.text = text;
                this.cdata = cdata;
            }
            return this;
        }

        /**
         * Defines whether the XML element has a CDATA block.
         * @param cdata true if the XML element has a CDATA block,
         *              false otherwise
         * @return The XML element
         */
        public Element setCDATA(boolean cdata) {
            if (!closed) {
                this.cdata = cdata;
            }
            return this;
        }

        /**
         * Explicitly opens the XML element. No more attributes can be added
         * to the XML element after calling this method.
         * @return The XML writer
         * @throws IOException If output fails
         */
        public SimpleXml open() throws IOException {
            if (!opened) {
                startElement(this);
            }
            return getWriter();
        }

        /**
         * Opens and returns a new XML element with the specified name.
         * This method can be used for chaining. It opens the current XML
         * element, then calls its equivalent in {@link SimpleXml}.
         * @see SimpleXml#open(String)
         * @param name The name of the XML element
         * @return The XML element
         * @throws IOException If output fails
         */
        public Element open(String name) throws IOException {
            open();
            return getWriter().open(name);
        }

        /**
         * Opens and returns a new XML element with the specified name.
         * This method can be used for chaining. It opens the current XML
         * element, then calls its equivalent in {@link SimpleXml}.
         * @see SimpleXml#open(String, String)
         * @param localName The local name of the XML element
         * @param name The name of the XML element
         * @return The XML element
         * @throws IOException If output fails
         */
        public Element open(String localName, String name) throws IOException {
            open();
            return getWriter().open(localName, name);
        }

        /**
         * Opens and returns a new XML element with the specified name.
         * This method can be used for chaining. It opens the current XML
         * element, then calls its equivalent in {@link SimpleXml}.
         * @see SimpleXml#open(String, String, String)
         * @param uri The URI of the XML element
         * @param localName The local name of the XML element
         * @param name The name of the XML element
         * @return The XML element
         * @throws IOException If output fails
         */
        public Element open(String uri, String localName, String name)
                throws IOException {
            open();
            return getWriter().open(uri, localName, name);
        }

        /**
         * Opens and returns a new XML element with the specified name and content.
         * This method can be used for chaining. It opens the current XML
         * element, then calls its equivalent in {@link SimpleXml}.
         * @see SimpleXml#open(String, String, boolean)
         * @param name The name of the XML element
         * @param content The content of the XML element
         * @param cdata true if content should be in a CDATA block,
         *              false otherwise
         * @return The XML element
         * @throws IOException If output fails
         */
        public Element open(String name, String content, boolean cdata)
            throws IOException {
            open();
            return getWriter().open(name, content, cdata);
        }

        /**
         * Explicitly closes the XML element. This method will write the
         * XML element and consequently render it immutable.
         * @return The XML writer
         * @throws IOException If output fails
         */
        public SimpleXml close() throws IOException {
            if (!closed) {
                endElement(this);
            }
            return getWriter();
        }

        /**
         * States whether the XML element has a CDATA block.
         * @return true if the XML element has a CDATA block,
         *         false otherwise
         */
        public boolean hasCDATA() {
            return cdata;
        }

        /**
         * Returns the underlying XML writer
         * @return The XML writer
         */
        protected SimpleXml getWriter() {
            return SimpleXml.this;
        }

        /**
         * States whether the output of the XML element has been started.
         * @return true if the output has been started,
         *         false otherwise
         */
        protected boolean isOpened() {
            return opened;
        }

        /**
         * Defines whether the output of the XML element has been started.
         * @param opened true if the output has been started,
         *                false otherwise
         */
        protected void setOpened(boolean opened) {
            this.opened = opened;
        }

        /**
         * States whether the output of the XML element has been ended.
         * @return true if the output has been ended,
         *         false otherwise
         */
        protected boolean isClosed() {
            return closed;
        }

        /**
         * Defines whether the output of the XML element has been ended.
         * @param closed true if the output has been ended,
         *                false otherwise
         */
        protected void setClosed(boolean closed) {
            this.closed = closed;
        }

        /**
         * Returns the attributes of the XML element.
         * @return The attributes
         */
        protected AttributesImpl getAttributes() {
            return atts;
        }

        /**
         * Returns the URI of the XML element.
         * @return The URI
         */
        protected String getUri() {
            return uri;
        }

        /**
         * Returns the local name of the XML element.
         * @return The local name
         */
        protected String getLocalName() {
            return localName;
        }

        /**
         * Returns the name of the XML element.
         * @return The name
         */
        protected String getName() {
            return name;
        }

        /**
         * Returns the content of the XML element.
         * @return The content
         */
        protected String getText() {
            return text;
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy