/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.util;
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;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/*
* A XMLStreamWriter which writes to a SAX ContentHandler
* DAL Note: This code borrows heavily from stax-utils class StAXStremWriter (author Christian Niles)
*
*/
public class XMLStreamWriterToContentHandler implements XMLStreamWriter {
private ContentHandler mHandler;
/** The root namespace context. */
private NamespaceContext rootContext;
/**
* The current {@link ElementContext}. used to keep track of opened
* elements.
*/
private ElementContext elementContext;
public XMLStreamWriterToContentHandler(ContentHandler handler) {
mHandler = handler;
}
@Override
public void close() throws XMLStreamException {
;
}
@Override
public void flush() throws XMLStreamException {
closeElementContext();
}
@Override
public NamespaceContext getNamespaceContext() {
return elementContext;
}
@Override
public String getPrefix(String uri) throws XMLStreamException {
return getNamespaceContext().getPrefix(uri);
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
// TODO provide access to properties?
throw new IllegalArgumentException(name + " property not supported");
}
@Override
public void setDefaultNamespace(String uri) throws XMLStreamException {
elementContext.putNamespace("", uri);
}
@Override
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
if (rootContext == null && elementContext == null) {
rootContext = context;
} else {
throw new IllegalStateException(
"NamespaceContext has already been set or document is already in progress");
}
}
@Override
public void setPrefix(String prefix, String uri) throws XMLStreamException {
elementContext.putNamespace(prefix, uri);
}
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);
}
@Override
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);
}
@Override
public void writeAttribute(String namespaceURI, String localName, String value)
throws XMLStreamException {
writeAttribute("", namespaceURI, localName, value);
}
@Override
public void writeAttribute(String localName, String value) throws XMLStreamException {
writeAttribute("", "", localName, value);
}
@Override
public void writeCharacters(String text) throws XMLStreamException {
// flush any cached start element content
closeElementContext();
try {
mHandler.characters(text.toCharArray(), 0, text.length());
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
@Override
public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
// flush any cached start element content
closeElementContext();
try {
mHandler.characters(text, start , len );
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
@Override
public void writeComment(String data) throws XMLStreamException {
// Ignore comments
}
@Override
public void writeDTD(String dtd) throws XMLStreamException {
// ignore DTD
}
@Override
public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
writeNamespace("", namespaceURI);
}
@Override
public void writeEmptyElement(String localName) throws XMLStreamException {
writeEmptyElement("",localName,"");
}
@Override
public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
writeEmptyElement( "" , localName , namespaceURI );
}
@Override
public void writeEmptyElement(String prefix, String localName, String uri)
throws XMLStreamException {
writeStartElement( prefix , localName , uri , true );
}
@Override
public void writeEndDocument() throws XMLStreamException {
// flush any cached start element content
closeElementContext();
try {
mHandler.endDocument();
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
@Override
public void writeEndElement() throws XMLStreamException {
// flush any cached start element content
closeElementContext();
if (elementContext != null) {
QName name = elementContext.getName();
String rawname = getRawname(name);
try {
mHandler.endElement( name.getNamespaceURI() , name.getLocalPart() , rawname);
} catch (SAXException e) {
throw new XMLStreamException(e);
}
// pop the context
elementContext = elementContext.getParentContext();
} else {
throw new XMLStreamException("Unmatched END_ELEMENT");
}
}
private String getRawname(QName name) {
String prefix = name.getPrefix();
String rawname;
if (prefix == null || prefix.length() == 0) {
rawname = name.getLocalPart();
} else {
rawname = prefix + ':' + name.getLocalPart();
}
return rawname;
}
@Override
public void writeEntityRef(String name) throws XMLStreamException {
// flush any cached start element content
closeElementContext();
// ???
}
@Override
public 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");
}
}
@Override
public void writeProcessingInstruction(String target) throws XMLStreamException {
writeProcessingInstruction(target,"");
}
@Override
public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
// flush any cached start element content
closeElementContext();
try {
mHandler.processingInstruction(target, data);
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
@Override
public void writeStartDocument() throws XMLStreamException {
writeStartDocument("","");
}
@Override
public void writeStartDocument(String version) throws XMLStreamException {
writeStartDocument("",version);
}
@Override
public void writeStartDocument(String encoding, String version) throws XMLStreamException {
try {
mHandler.startDocument();
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
@Override
public void writeStartElement(String localName) throws XMLStreamException {
writeStartElement("",localName,"",false);
}
@Override
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
writeStartElement("",localName,namespaceURI,false);
}
@Override
public void writeStartElement(String prefix, String localName, String namespaceURI)
throws XMLStreamException {
writeStartElement(prefix,localName,namespaceURI,false);
}
/**
* Core start tag output method called by all other <code>writeXXXElement</code>
* 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);
}
/**
*
* Returns the current position of the writer
*
* @return The current position of the writer.
*/
public synchronized String getCurrentPath() {
if (elementContext == null) {
return "/";
} else {
return elementContext.getPath();
}
}
@Override
public void writeCData(String data) throws XMLStreamException {
// flush any cached start element content
closeElementContext();
try {
mHandler.characters(data.toCharArray(), 0, data.length());
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
/**
* 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 {
for (int i = 0, s = elementContext.namespaceCount(); i < s; i++) {
String prefix = elementContext.getNamespacePrefix(i);
String uri = elementContext.getNamespaceURI(i);
mHandler.startPrefixMapping(prefix, uri);
}
AttributesImpl attrs = new AttributesImpl();
for (int i = 0, s = elementContext.attributeCount(); i < s; i++) {
QName name = elementContext.getAttributeName(i);
String value = elementContext.getAttribute(i);
attrs.addAttribute(name.getNamespaceURI(), name.getLocalPart(), getRawname(name), "CDATA", value);
}
QName name = elementContext.getName();
mHandler.startElement(name.getNamespaceURI(), name.getLocalPart(), getRawname(name), attrs);
} catch (Exception e) {
throw new XMLStreamException(getCurrentPath()
+ ": error writing start tag to stream", e);
}
}
}
}
//
//
// Copyright (C) 2008-2014 David A. Lee.
//
// The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is David A. Lee
//
// Portions created by (your name) are Copyright (C) (your legal entity). All
// Rights Reserved.
//
// Contributor(s): none.
//