/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.gatein.common.xml.stax.writer; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.gatein.common.xml.stax.writer.formatting.NoOpFormatter; import org.gatein.common.xml.stax.writer.formatting.XmlStreamingFormatter; import org.staxnav.Naming; import org.staxnav.StaxNavException; /** * @author <a href="mailto:nscavell@redhat.com">Nick Scavelli</a> * @version $Revision$ */ public class StaxWriterImpl<N> implements StaxWriter<N>, XMLStreamConstants { private Naming<N> naming; private XMLStreamWriter writer; private XmlStreamingFormatter formatter; private Deque<Element> elementStack = new ArrayDeque<Element>(); public StaxWriterImpl(Naming<N> naming, XMLStreamWriter writer) { this(naming, writer, null); } public StaxWriterImpl(Naming<N> naming, XMLStreamWriter writer, XmlStreamingFormatter formatter) { this(naming, writer, formatter, null, null); } public StaxWriterImpl(Naming<N> naming, XMLStreamWriter writer, XmlStreamingFormatter formatter, String encoding, String version) { this.naming = naming; this.writer = writer; if (formatter == null) formatter = NoOpFormatter.INSTANCE; this.formatter = formatter; elementStack.push(new RootElement(writer, formatter, encoding, version)); } public StaxWriter<N> writeAttribute(String name, String value) { if (name == null) throw new IllegalArgumentException("name cannot be null"); if (value == null) throw new IllegalArgumentException("value cannot be null"); Element current = elementStack.peek(); current.writeAttribute(name, value); return this; } public StaxWriter<N> writeAttribute(QName name, String value) { if (name == null) throw new IllegalArgumentException("name cannot be null"); Element current = elementStack.peek(); current.writeAttribute(name, value); return this; } public StaxWriter<N> writeStartElement(N element) throws StaxNavException { Element current = elementStack.peek(); if (current instanceof RootElement) { if (((RootElement) current).started) { current = new StreamElement(writer, formatter); elementStack.push(current); } } else { current = new StreamElement(writer, formatter); elementStack.push(current); } current.writeStartElement(naming, element); return this; } public StaxWriter<N> writeEndElement() throws StaxNavException { if (elementStack.isEmpty()) throw new StaxNavException(null, "No matching startElement was found for this endElement"); elementStack.pop().end(); return this; } public StaxWriter<N> writeContent(String content) throws StaxNavException { return writeContent(WritableValueTypes.STRING, content); } public <V> StaxWriter<N> writeContent(WritableValueType<V> valueType, V content) throws StaxNavException { if (valueType == null) throw new IllegalArgumentException("valueType cannot be null."); if (content == null) throw new IllegalArgumentException("content cannot be null."); Element current = elementStack.peek(); current.writeContent(valueType.format(content)); return this; } public <V> StaxWriter<N> writeElement(N element, String content) throws StaxNavException { return writeElement(element, WritableValueTypes.STRING, content); } public <V> StaxWriter<N> writeElement(N element, WritableValueType<V> valueType, V content) throws StaxNavException { writeStartElement(element).writeContent(valueType, content).writeEndElement(); return this; } public StaxWriter<N> writeNamespace(String prefix, String uri) throws StaxNavException { Element current = elementStack.peek(); current.writeNamespace(prefix, uri); return this; } public StaxWriter<N> writeDefaultNamespace(String uri) throws StaxNavException { Element current = elementStack.peek(); current.writeDefaultNamespace(uri); return this; } public StaxWriter<N> writeComment(final String comment) throws StaxNavException { Element current = elementStack.peek(); current.writeComment(comment); return this; } public StaxWriter<N> writeCData(String cdata) throws StaxNavException { Element current = elementStack.peek(); current.writeCData(cdata); return this; } public void finish() throws StaxNavException { while (!elementStack.isEmpty()) { elementStack.pop().end(); } } private abstract static class Element { abstract void writeAttribute(String name, String value) throws StaxNavException; abstract <N> void writeAttribute(QName name, String value) throws StaxNavException; abstract <N> void writeStartElement(Naming<N> naming, N name) throws StaxNavException; abstract void writeContent(String content) throws StaxNavException; abstract void writeNamespace(String prefix, String uri) throws StaxNavException; abstract void writeDefaultNamespace(String uri) throws StaxNavException; abstract void writeComment(String comment) throws StaxNavException; abstract void writeCData(String cdata) throws StaxNavException; abstract void end(); XMLStreamWriter writer; XmlStreamingFormatter formatter; private List<StreamClosure> closures; Element(XMLStreamWriter writer, XmlStreamingFormatter formatter) { this(writer, formatter, null); } Element(XMLStreamWriter writer, XmlStreamingFormatter formatter, List<StreamClosure> closures) { this.writer = writer; this.formatter = formatter; this.closures = closures; } void apply(int event, StreamClosure closure) throws StaxNavException { try { formatter.before(writer, event); closure.execute(writer); formatter.after(writer, event); } catch (XMLStreamException e) { throw new StaxNavException(e); } } } private static class RootElement extends Element { private String encoding; private String version; private Element element; private boolean started; RootElement(XMLStreamWriter writer, XmlStreamingFormatter formatter, String encoding, String version) { super(writer, formatter); this.encoding = encoding; this.version = version; } public void writeAttribute(String name, String value) throws StaxNavException { get().writeAttribute(name, value); } public <N> void writeAttribute(QName name, String value) throws StaxNavException { get().writeAttribute(name, value); } public <N> void writeStartElement(Naming<N> naming, N name) throws StaxNavException { get().writeStartElement(naming, name); started = true; } public void writeContent(String content) throws StaxNavException { get().writeContent(content); } public void writeNamespace(String prefix, String uri) throws StaxNavException { get().writeNamespace(prefix, uri); } public void writeDefaultNamespace(String uri) throws StaxNavException { get().writeDefaultNamespace(uri); } public void writeComment(String comment) throws StaxNavException { get().writeComment(comment); } public void writeCData(String cdata) throws StaxNavException { get().writeCData(cdata); } public void end() { if (started) { get().end(); } endDocument(); } private Element get() { if (element == null) { startDocument(); element = new StreamElement(writer, formatter); } return element; } private void startDocument() throws StaxNavException { apply(START_DOCUMENT, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { if (encoding == null && version == null) { writer.writeStartDocument(); } else if (encoding == null) { writer.writeStartDocument(version); } else { writer.writeStartDocument(encoding, version); } } }); } private void endDocument() throws StaxNavException { try { apply(END_DOCUMENT, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndDocument(); } }); writer.flush(); } catch (XMLStreamException e) { throw new StaxNavException(e); } finally { try { writer.close(); } catch (XMLStreamException e) { } } } } private static class StreamElement extends Element { StreamElement(XMLStreamWriter writer, XmlStreamingFormatter formatter) { super(writer, formatter); } public void writeAttribute(final String name, final String value) throws StaxNavException { apply(ATTRIBUTE, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeAttribute(name, value); } }); } public <N> void writeAttribute(QName name, final String value) throws StaxNavException { final String prefix = name.getPrefix(); final String uri = name.getNamespaceURI(); final String localPart = name.getLocalPart(); apply(ATTRIBUTE, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { if (uri == null || XMLConstants.NULL_NS_URI.equals(uri)) { writer.writeAttribute(localPart, value); } else if (prefix == null || XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { writer.writeAttribute(uri, localPart, value); } else { writer.writeAttribute(prefix, uri, localPart, value); } } }); } public <N> void writeStartElement(Naming<N> naming, N name) throws StaxNavException { final String prefix = naming.getPrefix(name); final String uri = naming.getURI(name); final String localPart = naming.getLocalPart(name); apply(START_ELEMENT, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { if (uri == null || XMLConstants.NULL_NS_URI.equals(uri)) { writer.writeStartElement(localPart); } else if (prefix == null || XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { writer.writeStartElement(uri, localPart); } else { writer.writeStartElement(prefix, localPart, uri); } } }); } public void writeContent(final String content) throws StaxNavException { apply(CHARACTERS, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeCharacters(content); } }); } public void writeNamespace(final String prefix, final String uri) throws StaxNavException { apply(NAMESPACE, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeNamespace(prefix, uri); } }); } public void writeDefaultNamespace(final String uri) throws StaxNavException { apply(NAMESPACE, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeDefaultNamespace(uri); } }); } public void writeComment(final String comment) throws StaxNavException { apply(COMMENT, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeComment(comment); } }); } public void writeCData(final String cdata) throws StaxNavException { apply(CDATA, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeCData(cdata); } }); } public void end() { apply(END_ELEMENT, new StreamClosure() { public void execute(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndElement(); } }); } } private interface StreamClosure { void execute(XMLStreamWriter writer) throws XMLStreamException; } }