blob: 3fab71c4a7c0380dbc5d009dfb681b3250e54a20 [file] [log] [blame]
/*
* Copyright 2004,2005 The Apache Software Foundation.
*
* Licensed 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.axiom.om.impl.serialize;
import java.util.ArrayList;
import org.apache.axiom.om.OMSerializer;
import org.apache.axiom.om.impl.util.OMSerializerUtil;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
/**
* Class StreamingOMSerializer
*/
public class StreamingOMSerializer implements XMLStreamConstants, OMSerializer {
private static int namespaceSuffix = 0;
public static final String NAMESPACE_PREFIX = "ns";
/*
* 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
* serilizer and tells it when to return.
* Note that it is assumed that this serialization starts on an Element.
*/
/**
* Field depth
*/
private int depth = 0;
/**
* Method serialize.
*
* @param node
* @param writer
* @throws XMLStreamException
*/
public void serialize(XMLStreamReader node, XMLStreamWriter writer)
throws XMLStreamException {
serializeNode(node, writer);
}
/**
* Method serializeNode.
*
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeNode(XMLStreamReader reader, XMLStreamWriter writer)
throws XMLStreamException {
//TODO We get the StAXWriter at this point and uses it hereafter assuming that this is the only entry point to this class.
// If there can be other classes calling methodes of this we might need to change methode signatures to OMOutputer
while (reader.hasNext()) {
int event = reader.next();
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 the depth
} else if (event == END_DOCUMENT) {
if (depth!=0) depth--; //for the end document - reduce the depth
try {
serializeEndElement(writer);
} catch (Exception e) {
//TODO: log exceptions
}
}
if (depth == 0) {
break;
}
}
}
/**
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeElement(XMLStreamReader reader,
XMLStreamWriter writer)
throws XMLStreamException {
// Note: To serialize the start tag, we must follow the order dictated by the JSR-173 (StAX) specification.
// Please keep this code in sync with the code in OMSerializerUtil.serializeStartpart
// The algorithm is:
// ... generate setPrefix/setDefaultNamespace for each namespace declaration if the prefix is unassociated.
// ... generate setPrefix/setDefaultNamespace if the prefix of the element is unassociated
// ... generate setPrefix/setDefaultNamespace for each unassociated prefix of the attributes.
//
// ... generate writeStartElement (See NOTE_A)
//
// ... generate writeNamespace/writerDefaultNamespace for the new namespace declarations determine during the "set" processing
// ... generate writeAttribute for each attribute
// NOTE_A: To confuse matters, some StAX vendors (including woodstox), believe that the setPrefix bindings
// should occur after the writeStartElement. If this is the case, the writeStartElement is generated first.
ArrayList writePrefixList = null;
ArrayList writeNSList = null;
// Get the prefix and namespace of the element. "" and null are identical.
String ePrefix = reader.getPrefix();
ePrefix = (ePrefix != null && ePrefix.length() == 0) ? null : ePrefix;
String eNamespace = reader.getNamespaceURI();
eNamespace = (eNamespace != null && eNamespace.length() == 0) ? null : eNamespace;
// Write the startElement if required
boolean setPrefixFirst = OMSerializerUtil.isSetPrefixBeforeStartElement(writer);
if (!setPrefixFirst) {
if (eNamespace != null) {
if (ePrefix == null) {
writer.writeStartElement("", reader.getLocalName(), eNamespace);
} else {
writer.writeStartElement(ePrefix, reader.getLocalName(), eNamespace);
}
} else {
writer.writeStartElement(reader.getLocalName());
}
}
// Generate setPrefix for the namespace declarations
int count = reader.getNamespaceCount();
for (int i = 0; i < count; i++) {
String prefix = reader.getNamespacePrefix(i);
prefix = (prefix != null && prefix.length() == 0) ? null : prefix;
String namespace = reader.getNamespaceURI(i);
namespace = (namespace != null && namespace.length() == 0) ? null : namespace;
String newPrefix = generateSetPrefix(prefix, namespace, writer);
// If this is a new association, remember it so that it can written out later
if (newPrefix != null) {
if (writePrefixList == null) {
writePrefixList= new ArrayList();
writeNSList = new ArrayList();
}
if (!writePrefixList.contains(newPrefix)) {
writePrefixList.add(newPrefix);
writeNSList.add(namespace);
}
}
}
// Generate setPrefix for the element
// If the prefix is not associated with a namespace yet, remember it so that we can
// write out a namespace declaration
String newPrefix = generateSetPrefix(ePrefix, eNamespace, writer);
// If this is a new association, remember it so that it can written out later
if (newPrefix != null) {
if (writePrefixList == null) {
writePrefixList= new ArrayList();
writeNSList = new ArrayList();
}
if (!writePrefixList.contains(newPrefix)) {
writePrefixList.add(newPrefix);
writeNSList.add(eNamespace);
}
}
// Now Generate setPrefix for each attribute
count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
String prefix = reader.getAttributePrefix(i);
prefix = (prefix != null && prefix.length() == 0) ? null : prefix;
String namespace = reader.getAttributeNamespace(i);
namespace = (namespace != null && namespace.length() == 0) ? null : namespace;
// Default prefix referencing is not allowed on an attribute
if (prefix == null && namespace != null) {
String writerPrefix = writer.getPrefix(namespace);
writerPrefix = (writerPrefix != null && writerPrefix.length() == 0) ? null : writerPrefix;
prefix = (writerPrefix != null) ?
writerPrefix :
generateUniquePrefix(writer.getNamespaceContext());
}
newPrefix = generateSetPrefix(prefix, namespace, writer);
// If the prefix is not associated with a namespace yet, remember it so that we can
// write out a namespace declaration
if (newPrefix != null) {
if (writePrefixList == null) {
writePrefixList= new ArrayList();
writeNSList = new ArrayList();
}
if (!writePrefixList.contains(newPrefix)) {
writePrefixList.add(newPrefix);
writeNSList.add(namespace);
}
}
}
// Now write the startElement
if (setPrefixFirst) {
if (eNamespace != null) {
if (ePrefix == null) {
writer.writeStartElement("", reader.getLocalName(), eNamespace);
} else {
writer.writeStartElement(ePrefix, reader.getLocalName(), eNamespace);
}
} else {
writer.writeStartElement(reader.getLocalName());
}
}
// Now write out the list of namespace declarations in this list that we constructed
// while doing the "set" processing.
if (writePrefixList != null) {
for (int i=0; i<writePrefixList.size(); i++) {
String prefix = (String) writePrefixList.get(i);
String namespace = (String) writeNSList.get(i);
if (prefix != null) {
writer.writeNamespace(prefix, namespace);
} else {
writer.writeDefaultNamespace(namespace);
}
}
}
// Now write the attributes
count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
String prefix = reader.getAttributePrefix(i);
prefix = (prefix != null && prefix.length() == 0) ? null : prefix;
String namespace = reader.getAttributeNamespace(i);
namespace = (namespace != null && namespace.length() == 0) ? null : namespace;
if (prefix == null && namespace != null) {
// Default namespaces are not allowed on an attribute reference.
// Earlier in this code, a unique prefix was added for this case...now obtain and use it
prefix = writer.getPrefix(namespace);
} else if (namespace != null) {
// Use the writer's prefix if it is different
String writerPrefix = writer.getPrefix(namespace);
if (!prefix.equals(writerPrefix)) {
prefix = writerPrefix;
}
}
if (namespace != null) {
// Qualified attribute
writer.writeAttribute(prefix, namespace,
reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
} else {
// Unqualified attribute
writer.writeAttribute(reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
}
}
}
/**
* Generate setPrefix/setDefaultNamespace if the prefix is not associated
* @param prefix
* @param namespace
* @param writer
* @return prefix name if a setPrefix/setDefaultNamespace is performed
*/
private String generateSetPrefix(String prefix, String namespace, XMLStreamWriter writer) throws XMLStreamException {
return OMSerializerUtil.generateSetPrefix(prefix, namespace, writer);
}
/**
* Method serializeEndElement.
*
* @param writer
* @throws XMLStreamException
*/
protected void serializeEndElement(XMLStreamWriter writer)
throws XMLStreamException {
writer.writeEndElement();
}
/**
* @param reader
* @param writer
* @throws XMLStreamException
*/
protected void serializeText(XMLStreamReader reader,
XMLStreamWriter writer)
throws XMLStreamException {
writer.writeCharacters(reader.getText());
}
/**
* 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 serializeAttributes(XMLStreamReader reader,
XMLStreamWriter writer)
throws XMLStreamException {
int count = reader.getAttributeCount();
String prefix = null;
String namespaceName = null;
String writerPrefix=null;
for (int i = 0; i < count; i++) {
prefix = reader.getAttributePrefix(i);
namespaceName = reader.getAttributeNamespace(i);
/*
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 (namespaceName==null) namespaceName="";
writerPrefix =writer.getNamespaceContext().getPrefix(namespaceName);
if (!"".equals(namespaceName)){
//prefix has already being declared but this particular attrib has a
//no prefix attached. So use the prefix provided by the writer
if (writerPrefix!=null && (prefix==null || prefix.equals(""))){
writer.writeAttribute(writerPrefix, namespaceName,
reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
//writer prefix is available but different from the current
//prefix of the attrib. We should be decalring the new prefix
//as a namespace declaration
}else if (prefix!=null && !"".equals(prefix)&& !prefix.equals(writerPrefix)){
writer.writeNamespace(prefix,namespaceName);
writer.writeAttribute(prefix, namespaceName,
reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
//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
}else{
prefix = generateUniquePrefix(writer.getNamespaceContext());
writer.writeNamespace(prefix,namespaceName);
writer.writeAttribute(prefix, namespaceName,
reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
}
}else{
//empty namespace is equal to no namespace!
writer.writeAttribute(reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
}
}
}
/**
* 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 serializeNamespace.
* @param prefix
* @param URI
* @param writer
* @throws XMLStreamException
*/
private void serializeNamespace(String prefix,
String URI,
XMLStreamWriter writer)
throws XMLStreamException {
String prefix1 = writer.getPrefix(URI);
if (prefix1 == null) {
writer.writeNamespace(prefix, URI);
writer.setPrefix(prefix, URI);
}
}
}