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

javanet.staxutils.io.StAXStreamWriter Maven / Gradle / Ivy

There is a newer version: 4.0.3
Show newest version
/*
 * $Id: StAXStreamWriter.java,v 1.1 2004/07/15 02:15:56 cniles Exp $
 * 
 * Copyright (c) 2004, Christian Niles, unit12.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *		*   Redistributions of source code must retain the above copyright
 *          notice, this list of conditions and the following disclaimer.
 * 
 *	    *	Redistributions in binary form must reproduce the above copyright
 *          notice, this list of conditions and the following disclaimer in the
 *          documentation and/or other materials provided with the distribution.
 * 
 *      *   Neither the name of Christian Niles, Unit12, nor the names of its
 *          contributors may be used to endorse or promote products derived from
 *          this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package javanet.staxutils.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

import javanet.staxutils.helpers.ElementContext;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

/**
 * An {@link XMLStreamWriter} implementation that writes to a character stream.
 * 
 * @author Christian Niles
 * @version $Revision: 1.1 $
 */
public class StAXStreamWriter implements XMLStreamWriter {

    /** The destination stream. */
    private Writer writer;

    /** Whether the stream has been closed or not. */
    private boolean closed;

    /** The root namespace context. */
    private NamespaceContext rootContext;

    /** The current {@link ElementContext}. used to keep track of opened elements. */
    private ElementContext elementContext;

    /**
     * Constructs a StAXStreamWriter that writes to the provided
     * {@link OutputStream} using the platform default encoding.
     * 
     * @param stream The destination stream.
     */
    public StAXStreamWriter(OutputStream stream) {

        this(new OutputStreamWriter(stream));

    }

    /**
     * Constructs a StAXStreamWriter that writes to the provided
     * {@link OutputStream} using the specified encoding.
     * 
     * @param stream The destination stream.
     * @param encoding The output encoding.
     * @throws UnsupportedEncodingException If the encoding isn't supported.
     */
    public StAXStreamWriter(OutputStream stream, String encoding)
            throws UnsupportedEncodingException {

        this(new OutputStreamWriter(stream, encoding));

    }

    /**
     * Constructs a StAXStreamWriter that writes to the provided
     * {@link Writer}.
     * 
     * @param writer The destination {@link Writer} instance.
     */
    public StAXStreamWriter(Writer writer) {

        this.writer = writer;

    }

    /**
     * Constructs a StAXStreamWriter that writes to the provided
     * {@link Writer}.
     * 
     * @param writer The destination {@link Writer} instance.
     * @param rootContext The root namespace context.
     */
    public StAXStreamWriter(Writer writer, NamespaceContext rootContext) {

        this.writer = writer;
        this.rootContext = rootContext;

    }

    public synchronized void close() throws XMLStreamException {

        if (!closed) {

            flush();

            closed = true;
            writer = null;

        }

    }

    public synchronized void flush() throws XMLStreamException {

        // if cached element info exists, send it
        closeElementContext();

        try {

            writer.flush();

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public String getPrefix(String uri) throws XMLStreamException {

        return getNamespaceContext().getPrefix(uri);

    }

    public Object getProperty(String name) throws IllegalArgumentException {

        // TODO provide access to properties?
        throw new IllegalArgumentException(name + " property not supported");

    }

    public void writeStartDocument() throws XMLStreamException {

        try {

            XMLWriterUtils.writeStartDocument(writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public void writeStartDocument(String version) throws XMLStreamException {

        try {

            XMLWriterUtils.writeStartDocument(version, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeStartDocument(String encoding, String version)
            throws XMLStreamException {

        // TODO perform check that StartDocument can be entered
        try {

            XMLWriterUtils.writeStartDocument(version, encoding, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeEndDocument() throws XMLStreamException {

        // flush any cached start element content
        closeElementContext();

        // close any tags
        while (elementContext != null) {

            writeEndElement();

        }

    }

    public synchronized void writeCData(String data) throws XMLStreamException {

        if (data == null) {

            throw new IllegalArgumentException("CDATA argument was null");

        }

        // flush any cached start element content
        closeElementContext();

        // TODO verify data is appropriate for CDATA
        try {

            XMLWriterUtils.writeCData(data, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeCharacters(char[] text, int start, int len)
            throws XMLStreamException {

        if (text == null) {

            throw new IllegalArgumentException(
                    "Character text argument was null");

        }

        // flush any cached start element content
        closeElementContext();

        try {

            XMLWriterUtils.writeCharacters(text, start, len, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeCharacters(String text)
            throws XMLStreamException {

        if (text == null) {

            throw new IllegalArgumentException(
                    "Character text argument was null");

        }

        // flush any cached start element content
        closeElementContext();

        try {

            XMLWriterUtils.writeCharacters(text, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeComment(String data)
            throws XMLStreamException {

        if (data == null) {

            throw new IllegalArgumentException("Comment data argument was null");

        }

        // flush any cached start element content
        closeElementContext();

        // TODO check comment for non-SGML compatible characters?
        try {

            XMLWriterUtils.writeComment(data, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeDTD(String dtd) throws XMLStreamException {

        if (dtd == null) {

            throw new IllegalArgumentException("dtd argument was null");

        }

        try {

            XMLWriterUtils.writeDTD(dtd, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeEntityRef(String name)
            throws XMLStreamException {

        // flush any cached start element content
        closeElementContext();

        try {

            XMLWriterUtils.writeEntityReference(name, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public synchronized void writeProcessingInstruction(String target,
            String data) throws XMLStreamException {

        // flush any cached start element content
        closeElementContext();

        // TODO test processing instruction validity?
        try {

            XMLWriterUtils.writeProcessingInstruction(target, data, writer);

        } catch (IOException e) {

            throw new XMLStreamException(e);

        }

    }

    public void writeProcessingInstruction(String target)
            throws XMLStreamException {

        writeProcessingInstruction(target, null);

    }

    public NamespaceContext getNamespaceContext() {

        return elementContext;

    }

    public void setNamespaceContext(NamespaceContext context)
            throws XMLStreamException {

        if (this.rootContext == null && elementContext == null) {

            this.rootContext = context;

        } else {

            throw new IllegalStateException(
                    "NamespaceContext has already been set or document is already in progress");

        }

    }

    public synchronized void setDefaultNamespace(String uri)
            throws XMLStreamException {

        elementContext.putNamespace("", uri);

    }

    public synchronized void setPrefix(String prefix, String uri)
            throws XMLStreamException {

        elementContext.putNamespace(prefix, uri);

    }

    /**
     * Core start tag output method called by all other writeXXXElement
     * methods.
     * 
     * @param prefix The tag prefix.
     * @param localName The tag local name.
     * @param namespaceURI The namespace URI of the prefix.
     * @param isEmpty Whether the tag is empty.
     * @throws XMLStreamException If an error occurs writing the tag to the stream.
     */
    public synchronized void writeStartElement(String prefix, String localName,
            String namespaceURI, boolean isEmpty) throws XMLStreamException {

        if (prefix == null) {

            throw new IllegalArgumentException("prefix may not be null @ ["
                    + getCurrentPath() + "]");

        } else if (localName == null) {

            throw new IllegalArgumentException("localName may not be null @ ["
                    + getCurrentPath() + "]");

        } else if (namespaceURI == null) {

            throw new IllegalArgumentException(
                    "namespaceURI may not be null @ [" + getCurrentPath() + "]");

        }

        // new context is beginning; close the current context if needed
        if (elementContext != null) {

            closeElementContext();

            // test if we just closed an empty root context
            if (elementContext == null) {

                throw new XMLStreamException(
                        "Writing start tag after close of root element");

            }

        }

        // create the new context
        QName name = new QName(namespaceURI, localName, prefix);
        elementContext = new ElementContext(name, elementContext, isEmpty);

    }

    public void writeStartElement(String prefix, String localName,
            String namespaceURI) throws XMLStreamException {

        writeStartElement(prefix, localName, namespaceURI, false);

    }

    public void writeStartElement(String namespaceURI, String localName)
            throws XMLStreamException {

        writeStartElement("", localName, namespaceURI, false);

    }

    public void writeStartElement(String localName) throws XMLStreamException {

        writeStartElement("", localName, "", false);

    }

    public void writeEmptyElement(String prefix, String localName,
            String namespaceURI) throws XMLStreamException {

        writeStartElement(prefix, localName, namespaceURI, true);

    }

    public void writeEmptyElement(String namespaceURI, String localName)
            throws XMLStreamException {

        writeStartElement("", localName, namespaceURI, true);

    }

    public void writeEmptyElement(String localName) throws XMLStreamException {

        writeStartElement("", localName, "", true);

    }

    public synchronized void writeAttribute(QName name, String value)
            throws XMLStreamException {

        if (elementContext == null || elementContext.isReadOnly()) {

            throw new XMLStreamException(
                    getCurrentPath()
                            + ": attributes must be written directly following a start element.");

        }

        elementContext.putAttribute(name, value);

    }

    public void writeAttribute(String prefix, String namespaceURI,
            String localName, String value) throws XMLStreamException {

        if (prefix == null) {

            throw new IllegalArgumentException(
                    "attribute prefix may not be null @ [" + getCurrentPath()
                            + "]");

        } else if (localName == null) {

            throw new IllegalArgumentException(
                    "attribute localName may not be null @ ["
                            + getCurrentPath() + "]");

        } else if (namespaceURI == null) {

            throw new IllegalArgumentException(
                    "attribute namespaceURI may not be null @ ["
                            + getCurrentPath() + "]");

        }

        writeAttribute(new QName(namespaceURI, localName, prefix), value);

    }

    public void writeAttribute(String namespaceURI, String localName,
            String value) throws XMLStreamException {

        writeAttribute("", namespaceURI, localName, value);

    }

    public void writeAttribute(String localName, String value)
            throws XMLStreamException {

        writeAttribute("", "", localName, value);

    }

    public void writeDefaultNamespace(String namespaceURI)
            throws XMLStreamException {

        writeNamespace("", namespaceURI);

    }

    public synchronized void writeNamespace(String prefix, String namespaceURI)
            throws XMLStreamException {

        if (prefix == null) {

            throw new IllegalArgumentException(
                    "Namespace prefix may not be null @ [" + getCurrentPath()
                            + "]");

        } else if (namespaceURI == null) {

            throw new IllegalArgumentException(
                    "Namespace URI may not be null @ [" + getCurrentPath()
                            + "]");

        }

        if (elementContext != null && !elementContext.isReadOnly()) {

            elementContext.putNamespace(prefix, namespaceURI);

        } else {

            throw new XMLStreamException(
                    getCurrentPath()
                            + ": Namespaces must be written directly following a start tag");
        }

    }

    public synchronized void writeEndElement() throws XMLStreamException {

        // flush any cached start element content
        closeElementContext();

        if (elementContext != null) {

            QName name = elementContext.getName();
            try {

                XMLWriterUtils.writeEndElement(name, writer);

            } catch (IOException e) {

                throw new XMLStreamException(getCurrentPath()
                        + ": Error writing end element to stream", e);

            }

            // pop the context
            elementContext = elementContext.getParentContext();

        } else {

            throw new XMLStreamException("Unmatched END_ELEMENT");

        }

    }

    /**
     * Returns the current position of the writer as a path of {@link QName} strings.
     * 
     * @return The current position of the writer.
     */
    public synchronized String getCurrentPath() {

        if (elementContext == null) {

            return "/";

        } else {

            return elementContext.getPath();

        }

    }

    /**
     * Closes the current {@link ElementContext}, writing any cached content and 
     * making it read-only. If the current context is empty, it will be popped and
     * replaced with its parent context. If no context is open, this method has no
     * effects.
     * 
     * @throws XMLStreamException If an error occurs flushing any element content.
     */
    protected void closeElementContext() throws XMLStreamException {

        if (elementContext != null && !elementContext.isReadOnly()) {

            elementContext.setReadOnly();

            // it hasn't been closed yet, so write it
            try {

                writer.write('<');
                XMLWriterUtils.writeQName(elementContext.getName(), writer);

                for (int i = 0, s = elementContext.attributeCount(); i < s; i++) {

                    QName name = elementContext.getAttributeName(i);
                    String value = elementContext.getAttribute(i);

                    writer.write(' ');
                    XMLWriterUtils.writeAttribute(name, value, writer);

                }

                for (int i = 0, s = elementContext.namespaceCount(); i < s; i++) {

                    String prefix = elementContext.getNamespacePrefix(i);
                    String uri = elementContext.getNamespaceURI(i);

                    writer.write(' ');
                    XMLWriterUtils.writeNamespace(prefix, uri, writer);

                }

                if (elementContext.isEmpty()) {

                    writer.write("/>");
                    elementContext = elementContext.getParentContext();

                } else {

                    writer.write('>');

                }

            } catch (IOException e) {

                throw new XMLStreamException(getCurrentPath()
                        + ": error writing start tag to stream", e);

            }

        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy