ohair@286: /* alanb@368: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.util; ohair@286: alanb@368: import com.sun.istack.internal.NotNull; alanb@368: import com.sun.istack.internal.Nullable; alanb@368: import com.sun.xml.internal.ws.util.xml.XmlUtil; alanb@368: import org.w3c.dom.*; ohair@286: alanb@368: import javax.xml.XMLConstants; alanb@368: import javax.xml.namespace.NamespaceContext; ohair@286: import javax.xml.parsers.DocumentBuilder; ohair@286: import javax.xml.parsers.DocumentBuilderFactory; ohair@286: import javax.xml.parsers.FactoryConfigurationError; ohair@286: import javax.xml.parsers.ParserConfigurationException; ohair@286: import javax.xml.stream.XMLStreamException; ohair@286: import javax.xml.stream.XMLStreamWriter; alanb@368: import java.util.ArrayList; ohair@286: import java.util.Iterator; ohair@286: import java.util.List; ohair@286: ohair@286: /** alanb@368: * @author JAXWS Development Team ohair@286: */ ohair@286: public class DOMUtil { ohair@286: ohair@286: private static DocumentBuilder db; ohair@286: ohair@286: /** ohair@286: * Creates a new DOM document. ohair@286: */ ohair@286: public static Document createDom() { ohair@286: synchronized (DOMUtil.class) { ohair@286: if (db == null) { ohair@286: try { alanb@368: DocumentBuilderFactory dbf = XmlUtil.newDocumentBuilderFactory(); ohair@286: dbf.setNamespaceAware(true); ohair@286: db = dbf.newDocumentBuilder(); ohair@286: } catch (ParserConfigurationException e) { ohair@286: throw new FactoryConfigurationError(e); ohair@286: } ohair@286: } ohair@286: return db.newDocument(); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Traverses a DOM node and writes out on a streaming writer. ohair@286: * ohair@286: * @param node ohair@286: * @param writer ohair@286: */ ohair@286: public static void serializeNode(Element node, XMLStreamWriter writer) throws XMLStreamException { ohair@286: writeTagWithAttributes(node, writer); ohair@286: ohair@286: if (node.hasChildNodes()) { ohair@286: NodeList children = node.getChildNodes(); ohair@286: for (int i = 0; i < children.getLength(); i++) { ohair@286: Node child = children.item(i); ohair@286: switch (child.getNodeType()) { ohair@286: case Node.PROCESSING_INSTRUCTION_NODE: ohair@286: writer.writeProcessingInstruction(child.getNodeValue()); alanb@368: break; ohair@286: case Node.DOCUMENT_TYPE_NODE: ohair@286: break; ohair@286: case Node.CDATA_SECTION_NODE: ohair@286: writer.writeCData(child.getNodeValue()); ohair@286: break; ohair@286: case Node.COMMENT_NODE: ohair@286: writer.writeComment(child.getNodeValue()); ohair@286: break; ohair@286: case Node.TEXT_NODE: ohair@286: writer.writeCharacters(child.getNodeValue()); ohair@286: break; ohair@286: case Node.ELEMENT_NODE: ohair@286: serializeNode((Element) child, writer); ohair@286: break; alanb@368: default: break; ohair@286: } ohair@286: } ohair@286: } ohair@286: writer.writeEndElement(); ohair@286: } ohair@286: ohair@286: public static void writeTagWithAttributes(Element node, XMLStreamWriter writer) throws XMLStreamException { ohair@286: String nodePrefix = fixNull(node.getPrefix()); ohair@286: String nodeNS = fixNull(node.getNamespaceURI()); ohair@286: //fix to work with DOM level 1 nodes. ohair@286: String nodeLocalName = node.getLocalName()== null?node.getNodeName():node.getLocalName(); ohair@286: ohair@286: // See if nodePrefix:nodeNS is declared in writer's NamespaceContext before writing start element ohair@286: // Writing start element puts nodeNS in NamespaceContext even though namespace declaration not written ohair@286: boolean prefixDecl = isPrefixDeclared(writer, nodeNS, nodePrefix); ohair@286: writer.writeStartElement(nodePrefix, nodeLocalName, nodeNS); ohair@286: ohair@286: if (node.hasAttributes()) { ohair@286: NamedNodeMap attrs = node.getAttributes(); ohair@286: int numOfAttributes = attrs.getLength(); ohair@286: // write namespace declarations first. ohair@286: // if we interleave this with attribue writing, ohair@286: // Zephyr will try to fix it and we end up getting inconsistent namespace bindings. ohair@286: for (int i = 0; i < numOfAttributes; i++) { ohair@286: Node attr = attrs.item(i); ohair@286: String nsUri = fixNull(attr.getNamespaceURI()); ohair@286: if (nsUri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { ohair@286: // handle default ns declarations ohair@286: String local = attr.getLocalName().equals(XMLConstants.XMLNS_ATTRIBUTE) ? "" : attr.getLocalName(); ohair@286: if (local.equals(nodePrefix) && attr.getNodeValue().equals(nodeNS)) { ohair@286: prefixDecl = true; ohair@286: } ohair@286: if (local.equals("")) { ohair@286: writer.writeDefaultNamespace(attr.getNodeValue()); ohair@286: } else { ohair@286: // this is a namespace declaration, not an attribute ohair@286: writer.setPrefix(attr.getLocalName(), attr.getNodeValue()); ohair@286: writer.writeNamespace(attr.getLocalName(), attr.getNodeValue()); ohair@286: } ohair@286: } ohair@286: } ohair@286: } ohair@286: // node's namespace is not declared as attribute, but declared on ancestor ohair@286: if (!prefixDecl) { ohair@286: writer.writeNamespace(nodePrefix, nodeNS); ohair@286: } ohair@286: ohair@286: // Write all other attributes which are not namespace decl. ohair@286: if (node.hasAttributes()) { ohair@286: NamedNodeMap attrs = node.getAttributes(); ohair@286: int numOfAttributes = attrs.getLength(); ohair@286: ohair@286: for (int i = 0; i < numOfAttributes; i++) { ohair@286: Node attr = attrs.item(i); ohair@286: String attrPrefix = fixNull(attr.getPrefix()); ohair@286: String attrNS = fixNull(attr.getNamespaceURI()); ohair@286: if (!attrNS.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { ohair@286: String localName = attr.getLocalName(); ohair@286: if (localName == null) { ohair@286: // TODO: this is really a bug in the caller for not creating proper DOM tree. ohair@286: // will remove this workaround after plugfest ohair@286: localName = attr.getNodeName(); ohair@286: } ohair@286: boolean attrPrefixDecl = isPrefixDeclared(writer, attrNS, attrPrefix); ohair@286: if (!attrPrefix.equals("") && !attrPrefixDecl) { ohair@286: // attr has namespace but namespace decl is there in ancestor node ohair@286: // So write the namespace decl before writing the attr ohair@286: writer.setPrefix(attr.getLocalName(), attr.getNodeValue()); ohair@286: writer.writeNamespace(attrPrefix, attrNS); ohair@286: } ohair@286: writer.writeAttribute(attrPrefix, attrNS, localName, attr.getNodeValue()); ohair@286: } ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: private static boolean isPrefixDeclared(XMLStreamWriter writer, String nsUri, String prefix) { ohair@286: boolean prefixDecl = false; ohair@286: NamespaceContext nscontext = writer.getNamespaceContext(); ohair@286: Iterator prefixItr = nscontext.getPrefixes(nsUri); ohair@286: while (prefixItr.hasNext()) { ohair@286: if (prefix.equals(prefixItr.next())) { ohair@286: prefixDecl = true; ohair@286: break; ohair@286: } ohair@286: } ohair@286: return prefixDecl; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the first child of the given name, or null. ohair@286: */ ohair@286: public static Element getFirstChild(Element e, String nsUri, String local) { ohair@286: for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) { ohair@286: if (n.getNodeType() == Node.ELEMENT_NODE) { ohair@286: Element c = (Element) n; alanb@368: if (c.getLocalName().equals(local) && c.getNamespaceURI().equals(nsUri)) { ohair@286: return c; alanb@368: } ohair@286: } ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: private static ohair@286: @NotNull ohair@286: String fixNull(@Nullable String s) { alanb@368: if (s == null) { alanb@368: return ""; alanb@368: } else { alanb@368: return s; alanb@368: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the first element child. ohair@286: */ ohair@286: public static ohair@286: @Nullable ohair@286: Element getFirstElementChild(Node parent) { ohair@286: for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) { ohair@286: if (n.getNodeType() == Node.ELEMENT_NODE) { ohair@286: return (Element) n; ohair@286: } ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: public static @NotNull ohair@286: List getChildElements(Node parent){ ohair@286: List elements = new ArrayList(); ohair@286: for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) { ohair@286: if (n.getNodeType() == Node.ELEMENT_NODE) { ohair@286: elements.add((Element)n); ohair@286: } ohair@286: } ohair@286: return elements; ohair@286: } ohair@286: }