/* * 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 <code>namespaceDeclarationAttribute</code> * 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 <code>namespaceDeclarationAttribute</code> * properts is false. * @return True, if namespace declarations of the parent nodes are * disabled, false otherwise. */ public boolean isParentsNamespaceDeclarationDisabled() { return parentsNamespaceDeclarationDisabled; } /** Returns, whether <code>startDocument</code> and * <code>endDocument</code> events are generated for * document nodes. * @return True (default), if <code>startDocument</code> and * <code>endDocument</code> events are being generated. * False otherwise. */ public boolean isStartingDocument() { return startingDocument; } /** Sets, whether <code>startDocument</code> and * <code>endDocument</code> events are generated for * document nodes. * @param pStartingDocument True (default), if * <code>startDocument</code> and * <code>endDocument</code> events are being generated. * False otherwise. */ public void setStartingDocument(boolean pStartingDocument) { startingDocument = pStartingDocument; } /** Serializes the childs of <code>pNode</code>. * @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 <code>pNode</code>. * @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 <code>pNode</code>. * @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 <code>pNode</code> into a * stream of SAX events, which are fired into the * content handler <code>pHandler</code>. * @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 <code>pNode</code> into a * stream of SAX events, which are fired into the * content handler <code>pHandler</code>. 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"); } } // uri The Namespace URI, or the empty string if none is available or Namespace processing is not being performed. // localName The local name, or the empty string if Namespace processing is not being performed. // qName The qualified (prefixed) name, or the empty string if qualified names are not available. // type The attribute type as a string. // value The attribute value attr.addAttribute(aUri == null ? "" : aUri, aLocalName, aNodeName, // JBH 2013 05 17 these args were the wrong way around! "CDATA", a.getNodeValue()); // 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()); } } }