org.apache.tuscany.sca.common.xml.stax.impl.XMLStreamSerializer Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tuscany.sca.common.xml.stax.impl;
import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
import static javax.xml.XMLConstants.NULL_NS_URI;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
/**
* The XMLStreamSerializer pulls events from the XMLStreamReader and dumps into the XMLStreamWriter
*
* @version $Rev: 820125 $ $Date: 2009-09-29 16:41:25 -0700 (Tue, 29 Sep 2009) $
*/
public class XMLStreamSerializer implements XMLStreamConstants {
public static final String NAMESPACE_PREFIX = "ns";
private static int namespaceSuffix;
/*
* The behavior of the Serializer is such that it returns when it encounters the starting element for the second
* time. The depth variable tracks the depth of the Serializer and tells it when to return. Note that it is assumed
* that this Serialization starts on an Element.
*/
/**
* Field depth
*/
private int depth;
/**
* A flag to tell if the writer has javax.xml.stream.isRepairingNamespaces set to true
*/
private boolean isRepairingNamespaces = true;
/**
* @param isRepairingNamespaces
*/
public XMLStreamSerializer(boolean isRepairingNamespaces) {
super();
this.isRepairingNamespaces = isRepairingNamespaces;
}
public XMLStreamSerializer() {
this(true);
}
/**
* Generates a unique namespace prefix that is not in the scope of the NamespaceContext
*
* @param nsCtxt
* @return string
*/
private String generateUniquePrefix(NamespaceContext nsCtxt) {
String prefix = NAMESPACE_PREFIX + namespaceSuffix++;
// null should be returned if the prefix is not bound!
while (nsCtxt.getNamespaceURI(prefix) != null) {
prefix = NAMESPACE_PREFIX + namespaceSuffix++;
}
return prefix;
}
/**
* Method serialize.
*
* @param node
* @param writer
* @throws XMLStreamException
*/
public void serialize(XMLStreamReader node, XMLStreamWriter writer) throws XMLStreamException {
serializeNode(node, writer);
}
/**
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeAttributes(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
int count = reader.getAttributeCount();
String prefix;
String namespaceName;
String localName;
String value;
for (int i = 0; i < count; i++) {
prefix = reader.getAttributePrefix(i);
namespaceName = reader.getAttributeNamespace(i);
localName = reader.getAttributeLocalName(i);
value = reader.getAttributeValue(i);
writeAttribute(writer, prefix, localName, namespaceName, value);
}
}
public void writeAttribute(XMLStreamWriter writer, QName name, String value) throws XMLStreamException {
writeAttribute(writer, name.getPrefix(), name.getLocalPart(), name.getNamespaceURI(), value);
}
public String writeAttribute(XMLStreamWriter writer,
String prefix,
String localName,
String namespaceURI,
String value) throws XMLStreamException {
String writerPrefix;
/*
* Due to parser implementations returning null as the namespace URI (for the empty namespace) we need to
* make sure that we deal with a namespace name that is not null. The best way to work around this issue is
* to set the namespace URI to "" if it is null
*/
if (namespaceURI == null) {
namespaceURI = NULL_NS_URI;
}
if (prefix == null) {
prefix = DEFAULT_NS_PREFIX;
}
if (isRepairingNamespaces) {
writer.writeAttribute(prefix, namespaceURI, localName, value);
return writer.getPrefix(namespaceURI);
}
writerPrefix = writer.getPrefix(namespaceURI);
if (!NULL_NS_URI.equals(namespaceURI)) {
if (writerPrefix != null && isDefaultNSPrefix(prefix)) {
// prefix has already being declared but this particular attrib has a
// no prefix attached. So use the prefix provided by the writer
writer.writeAttribute(writerPrefix, namespaceURI, localName, value);
return writerPrefix;
} else if (!isDefaultNSPrefix(prefix) && !prefix.equals(writerPrefix)) {
// writer prefix is available but different from the current
// prefix of the attrib. We should be declaring the new prefix
// as a namespace declaration
writer.writeNamespace(prefix, namespaceURI);
writer.writeAttribute(prefix, namespaceURI, localName, value);
return prefix;
} else if (isDefaultNSPrefix(prefix)) {
// prefix is null (or empty), but the namespace name is valid! it has not
// being written previously also. So we need to generate a prefix here
prefix = generateUniquePrefix(writer.getNamespaceContext());
writer.writeNamespace(prefix, namespaceURI);
writer.writeAttribute(prefix, namespaceURI, localName, value);
return prefix;
} else {
writer.writeAttribute(prefix, namespaceURI, localName, value);
return prefix;
}
} else {
// empty namespace is equal to no namespace!
writer.writeAttribute(localName, value);
return prefix;
}
}
private boolean isDefaultNSPrefix(String prefix) {
return (prefix == null || prefix.equals(DEFAULT_NS_PREFIX));
}
/**
* Method serializeCData.
*
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeCData(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
writer.writeCData(reader.getText());
}
/**
* Method serializeComment.
*
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeComment(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
writer.writeComment(reader.getText());
}
/**
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeElement(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
writeStartElement(writer, reader.getName());
// add the namespaces
int count = reader.getNamespaceCount();
String namespacePrefix;
for (int i = 0; i < count; i++) {
namespacePrefix = reader.getNamespacePrefix(i);
serializeNamespace(namespacePrefix, reader.getNamespaceURI(i), writer);
}
// add attributes
serializeAttributes(reader, writer);
}
public void writeStartElement(XMLStreamWriter writer, QName name) throws XMLStreamException {
writeStartElement(writer, name.getPrefix(), name.getLocalPart(), name.getNamespaceURI());
}
public void writeStartElement(XMLStreamWriter writer, String prefix, String localName, String namespaceURI)
throws XMLStreamException {
if (namespaceURI == null) {
namespaceURI = NULL_NS_URI;
}
if (prefix == null) {
prefix = DEFAULT_NS_PREFIX;
}
if (isRepairingNamespaces) {
writer.writeStartElement(prefix, localName, namespaceURI);
return;
}
String writerPrefix = writer.getPrefix(namespaceURI);
if (writerPrefix != null) {
// Namespace is bound
writer.writeStartElement(writerPrefix, localName, namespaceURI);
} else {
// Namespace is not bound
if (NULL_NS_URI.equals(namespaceURI)) {
writer.writeStartElement(localName);
String defaultNS = writer.getNamespaceContext().getNamespaceURI(DEFAULT_NS_PREFIX);
if (defaultNS != null && !NULL_NS_URI.equals(defaultNS)) {
writer.writeNamespace(prefix, namespaceURI);
}
} else {
writer.writeStartElement(prefix, localName, namespaceURI);
// writeNamespace() will call setPrefix()
writer.writeNamespace(prefix, namespaceURI);
}
}
}
/**
* Method serializeEndElement.
*
* @param writer
* @throws XMLStreamException
*/
protected void serializeEndElement(XMLStreamWriter writer) throws XMLStreamException {
writer.writeEndElement();
}
/**
* Method serializeNamespace.
*
* @param prefix
* @param uri
* @param writer
* @throws XMLStreamException
*/
private void serializeNamespace(String prefix, String uri, XMLStreamWriter writer) throws XMLStreamException {
writeNamespace(writer, prefix, uri);
}
public String writeNamespace(XMLStreamWriter writer, String prefix, String uri) throws XMLStreamException {
if (uri == null) {
uri = NULL_NS_URI;
}
String prefix1 = writer.getPrefix(uri);
if (prefix1 == null) {
if (prefix == null) {
prefix = DEFAULT_NS_PREFIX;
}
if (DEFAULT_NS_PREFIX.equals(prefix) && !XMLConstants.NULL_NS_URI.equals(uri)) {
String ns = writer.getNamespaceContext().getNamespaceURI(prefix);
if (ns != null) {
prefix = generateUniquePrefix(writer.getNamespaceContext());
}
}
writer.writeNamespace(prefix, uri);
return prefix;
} else {
return prefix1;
}
}
/**
* Method serializeNode.
*
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeNode(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
while (true) {
int event = reader.getEventType();
if (event == START_ELEMENT) {
serializeElement(reader, writer);
depth++;
} else if (event == ATTRIBUTE) {
serializeAttributes(reader, writer);
} else if (event == CHARACTERS) {
serializeText(reader, writer);
} else if (event == COMMENT) {
serializeComment(reader, writer);
} else if (event == CDATA) {
serializeCData(reader, writer);
} else if (event == END_ELEMENT) {
serializeEndElement(writer);
depth--;
} else if (event == START_DOCUMENT) {
depth++; // if a start document is found then increment
writer.writeStartDocument();
// the depth
} else if (event == END_DOCUMENT) {
if (depth != 0) {
depth--; // for the end document - reduce the depth
}
writer.writeEndDocument();
}
if (depth == 0) {
break;
}
if (reader.hasNext()) {
reader.next();
} else {
break;
}
}
}
/**
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeText(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
writer.writeCharacters(reader.getText());
}
}