org.apache.ws.commons.serialize.DOMSerializer Maven / Gradle / Ivy
Go to download
This is a small collection of utility classes, that allow high performance XML processing based on SAX. Basically, it is assumed, that you are using an JAXP 1.1 compliant XML parser and nothing else. In particular, no dependency on the javax.xml.transform package is introduced.
/*
* Copyright 2003, 2004 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.ws.commons.serialize;
import javax.xml.XMLConstants;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
/** Serializes a DOM node into a stream of SAX events.
*/
public class DOMSerializer {
private boolean namespaceDeclarationAttribute;
private boolean parentsNamespaceDeclarationDisabled;
private boolean startingDocument = true;
/** Sets whether XML namespace declarations are being serialized as
* attributes or as SAX events (default).
* @param pXmlDeclarationAttribute True, if a namespace declaration
* is being transmitted as an XML attribute. False otherwise.
*/
public void setNamespaceDeclarationAttribute(boolean pXmlDeclarationAttribute) {
namespaceDeclarationAttribute = pXmlDeclarationAttribute;
}
/** Returns whether XML declarations are being serialized as
* attributes or as SAX events (default).
* @return True, if a namespace declaration
* is being transmitted as an XML attribute. False otherwise.
*/
public boolean isNamespaceDeclarationAttribute() {
return namespaceDeclarationAttribute;
}
/** Returns whether XML declarations present in the parent nodes
* are being serialized (default) or not. This option takes effect
* only if the namespace declarations are sent as events. In other
* words, if the namespaceDeclarationAttribute
* properts is false.
* @param pParentsXmlDeclarationDisabled True, if namespace
* declarations of the parent nodes are disabled, false otherwise.
*/
public void setParentsNamespaceDeclarationDisabled(boolean pParentsXmlDeclarationDisabled) {
parentsNamespaceDeclarationDisabled = pParentsXmlDeclarationDisabled;
}
/** Sets whether XML declarations present in the parent nodes
* are being serialized (default) or not. This option takes effect
* only if the namespace declarations are sent as events. In other
* words, if the namespaceDeclarationAttribute
* properts is false.
* @return True, if namespace declarations of the parent nodes are
* disabled, false otherwise.
*/
public boolean isParentsNamespaceDeclarationDisabled() {
return parentsNamespaceDeclarationDisabled;
}
/** Returns, whether startDocument
and
* endDocument
events are generated for
* document nodes.
* @return True (default), if startDocument
and
* endDocument
events are being generated.
* False otherwise.
*/
public boolean isStartingDocument() {
return startingDocument;
}
/** Sets, whether startDocument
and
* endDocument
events are generated for
* document nodes.
* @param pStartingDocument True (default), if
* startDocument
and
* endDocument
events are being generated.
* False otherwise.
*/
public void setStartingDocument(boolean pStartingDocument) {
startingDocument = pStartingDocument;
}
/** Serializes the childs of pNode
.
* @param pNode The parent node, whose childs are being serialized.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
protected void doSerializeChilds(Node pNode, ContentHandler pHandler)
throws SAXException {
for (Node child = pNode.getFirstChild(); child != null;
child = child.getNextSibling()) {
doSerialize(child, pHandler);
}
}
/** Initially creates startPrefixMapping events for the nodes parents. This
* is invoked only, if {@link #isNamespaceDeclarationAttribute()},
* and {@link #isParentsNamespaceDeclarationDisabled()} are false.
* @param pNode The node, for which namespace declarations are being
* created.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
private void parentsStartPrefixMappingEvents(Node pNode, ContentHandler pHandler)
throws SAXException {
if (pNode != null) {
parentsStartPrefixMappingEvents(pNode.getParentNode(), pHandler);
if (pNode.getNodeType() == Node.ELEMENT_NODE) {
startPrefixMappingEvents(pNode, pHandler);
}
}
}
/** Finally creates endPrefixMapping events for the nodes parents. This
* is invoked only, if {@link #isNamespaceDeclarationAttribute()},
* and {@link #isParentsNamespaceDeclarationDisabled()} are false.
* @param pNode The node, for which namespace declarations are being
* created.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
private void parentsEndPrefixMappingEvents(Node pNode, ContentHandler pHandler)
throws SAXException {
if (pNode != null) {
if (pNode.getNodeType() == Node.ELEMENT_NODE) {
endPrefixMappingEvents(pNode, pHandler);
}
parentsEndPrefixMappingEvents(pNode.getParentNode(), pHandler);
}
}
/** Creates startPrefixMapping events for the node pNode
.
* @param pNode The node being serialized.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
private void startPrefixMappingEvents(Node pNode, ContentHandler pHandler)
throws SAXException {
NamedNodeMap nnm = pNode.getAttributes();
if (nnm != null) {
for (int i = 0; i < nnm.getLength(); i++) {
Node attr = nnm.item(i);
if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attr.getNamespaceURI())) {
String prefix;
if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getPrefix())) {
prefix = attr.getLocalName();
} else if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getNodeName())) {
prefix = "";
} else {
throw new IllegalStateException("Unable to parse namespace declaration: " + attr.getNodeName());
}
String uri = attr.getNodeValue();
if (uri == null) {
uri = "";
}
pHandler.startPrefixMapping(prefix, uri);
}
}
}
}
/** Creates endPrefixMapping events for the node pNode
.
* @param pNode The node being serialized.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
private void endPrefixMappingEvents(Node pNode, ContentHandler pHandler)
throws SAXException {
NamedNodeMap nnm = pNode.getAttributes();
if (nnm != null) {
for (int i = nnm.getLength()-1; i >= 0; i--) {
Node attr = nnm.item(i);
if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attr.getNamespaceURI())) {
String prefix = attr.getLocalName();
pHandler.endPrefixMapping(prefix);
}
}
}
}
private void characters(ContentHandler pHandler, String pValue, boolean pCdata)
throws SAXException {
LexicalHandler lh;
if (pCdata) {
lh = (pHandler instanceof LexicalHandler) ? (LexicalHandler) pHandler : null;
} else {
lh = null;
}
if (lh != null) {
lh.startCDATA();
}
pHandler.characters(pValue.toCharArray(), 0, pValue.length());
if (lh != null) {
lh.endCDATA();
}
}
/** Converts the given node pNode
into a
* stream of SAX events, which are fired into the
* content handler pHandler
.
* @param pNode The node being serialized.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
public void serialize(Node pNode, ContentHandler pHandler)
throws SAXException {
if (!isNamespaceDeclarationAttribute() &&
!isParentsNamespaceDeclarationDisabled()) {
parentsStartPrefixMappingEvents(pNode.getParentNode(), pHandler);
}
doSerialize(pNode, pHandler);
if (!isNamespaceDeclarationAttribute() &&
!isParentsNamespaceDeclarationDisabled()) {
parentsEndPrefixMappingEvents(pNode.getParentNode(), pHandler);
}
}
/** Converts the given node pNode
into a
* stream of SAX events, which are fired into the
* content handler pHandler
. Unlike
* {@link #serialize(Node, ContentHandler)}, this method
* doesn't call
* {@link #parentsStartPrefixMappingEvents(Node, ContentHandler)},
* and
* {@link #parentsEndPrefixMappingEvents(Node, ContentHandler)}.
* @param pNode The node being serialized.
* @param pHandler The target handler.
* @throws SAXException The target handler reported an error.
*/
protected void doSerialize(Node pNode, ContentHandler pHandler)
throws SAXException {
switch (pNode.getNodeType()) {
case Node.DOCUMENT_NODE:
boolean startDocumentEvent = isStartingDocument();
if (startDocumentEvent) {
pHandler.startDocument();
}
doSerializeChilds(pNode, pHandler);
if (startDocumentEvent) {
pHandler.endDocument();
}
break;
case Node.DOCUMENT_FRAGMENT_NODE:
doSerializeChilds(pNode, pHandler);
break;
case Node.ELEMENT_NODE:
AttributesImpl attr = new AttributesImpl();
boolean isNamespaceDeclarationAttribute = isNamespaceDeclarationAttribute();
if (!isNamespaceDeclarationAttribute) {
startPrefixMappingEvents(pNode, pHandler);
}
NamedNodeMap nnm = pNode.getAttributes();
if (nnm != null) {
for (int i = 0; i < nnm.getLength(); i++) {
Node a = nnm.item(i);
if (isNamespaceDeclarationAttribute ||
!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(a.getNamespaceURI())) {
String aUri = a.getNamespaceURI();
String aLocalName = a.getLocalName();
String aNodeName = a.getNodeName();
if (aLocalName == null) {
if (aUri == null || aUri.length() == 0) {
aLocalName = aNodeName;
} else {
throw new IllegalStateException("aLocalName is null");
}
}
attr.addAttribute(aUri == null ? "" : aUri, aNodeName,
aLocalName, "CDATA", a.getNodeValue());
}
}
}
String nUri = pNode.getNamespaceURI();
if (nUri == null) {
nUri = "";
}
pHandler.startElement(nUri, pNode.getLocalName(),
pNode.getNodeName(), attr);
doSerializeChilds(pNode, pHandler);
pHandler.endElement(nUri, pNode.getLocalName(),
pNode.getNodeName());
if (!isNamespaceDeclarationAttribute) {
endPrefixMappingEvents(pNode, pHandler);
}
break;
case Node.TEXT_NODE:
characters(pHandler, pNode.getNodeValue(), false);
break;
case Node.CDATA_SECTION_NODE:
characters(pHandler, pNode.getNodeValue(), true);
break;
case Node.PROCESSING_INSTRUCTION_NODE:
pHandler.processingInstruction(pNode.getNodeName(), pNode.getNodeValue());
break;
case Node.ENTITY_REFERENCE_NODE:
pHandler.skippedEntity(pNode.getNodeName());
break;
case Node.COMMENT_NODE:
if (pHandler instanceof LexicalHandler) {
String s = pNode.getNodeValue();
((LexicalHandler) pHandler).comment(s.toCharArray(), 0, s.length());
}
break;
default:
throw new IllegalStateException("Unknown node type: " + pNode.getNodeType());
}
}
}