/** * 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.wss4j.policy.model; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; /** * This class is a utility to serialize a DOM node as XML. This class * uses the <code>DOM Level 2</code> APIs. * The main difference between this class and DOMWriter is that this class * generates and prints out namespace declarations. */ final class DOM2Writer { public static final char NL = '\n'; public static final String LS = System.getProperty("line.separator", Character.valueOf(NL).toString()); public static final String XMLNS_NS = "http://www.w3.org/2000/xmlns/"; public static final String XML_NS = "http://www.w3.org/XML/1998/namespace"; private DOM2Writer() { // Complete } /** * Return a string containing this node serialized as XML. */ public static String nodeToString(Node node) { StringWriter sw = new StringWriter(); serializeAsXML(node, sw, true); return sw.toString(); } /** * Return a string containing this node serialized as XML. */ public static String nodeToString(Node node, boolean omitXMLDecl) { StringWriter sw = new StringWriter(); serializeAsXML(node, sw, omitXMLDecl); return sw.toString(); } /** * Serialize this node into the writer as XML. */ public static void serializeAsXML(Node node, Writer writer, boolean omitXMLDecl) { serializeAsXML(node, writer, omitXMLDecl, false); } /** * Serialize this node into the writer as XML. */ public static void serializeAsXML(Node node, Writer writer, boolean omitXMLDecl, boolean pretty) { PrintWriter out = new PrintWriter(writer); if (!omitXMLDecl) { out.print("<?xml version=\"1.0\" encoding=UTF-8 ?>"); } NSStack namespaceStack = new NSStack(); print(node, namespaceStack, out, pretty, 0); out.flush(); } private static void print(Node node, NSStack namespaceStack, PrintWriter out, boolean pretty, int indent) { if (node == null) { return; } boolean hasChildren = false; int type = node.getNodeType(); switch (type) { case Node.DOCUMENT_NODE: Node child = node.getFirstChild(); while (child != null) { print(child, namespaceStack, out, pretty, indent); child = child.getNextSibling(); } break; case Node.ELEMENT_NODE: namespaceStack.push(); if (pretty) { for (int i = 0; i < indent; i++) { out.print(' '); } } out.print('<' + node.getNodeName()); String elPrefix = node.getPrefix(); String elNamespaceURI = node.getNamespaceURI(); if (elPrefix != null && elNamespaceURI != null && elPrefix.length() > 0) { boolean prefixIsDeclared = false; try { String namespaceURI = namespaceStack.getNamespaceURI(elPrefix); if (elNamespaceURI.equals(namespaceURI)) { prefixIsDeclared = true; } } catch (IllegalArgumentException e) { //NOPMD // } if (!prefixIsDeclared) { printNamespaceDecl(node, namespaceStack, out); } } NamedNodeMap attrs = node.getAttributes(); int len = (attrs != null) ? attrs.getLength() : 0; for (int i = 0; i < len; i++) { Attr attr = (Attr) attrs.item(i); out.print(' ' + attr.getNodeName() + "=\""); normalize(attr.getValue(), out); out.print('\"'); String attrPrefix = attr.getPrefix(); String attrNamespaceURI = attr.getNamespaceURI(); if (attrPrefix != null && attrNamespaceURI != null) { boolean prefixIsDeclared = false; try { String namespaceURI = namespaceStack.getNamespaceURI(attrPrefix); if (attrNamespaceURI.equals(namespaceURI)) { prefixIsDeclared = true; } } catch (IllegalArgumentException e) { //NOPMD // } if (!prefixIsDeclared) { printNamespaceDecl(attr, namespaceStack, out); } } } child = node.getFirstChild(); if (child != null) { hasChildren = true; out.print('>'); if (pretty) { out.print(LS); } while (child != null) { print(child, namespaceStack, out, pretty, indent + 1); child = child.getNextSibling(); } } else { hasChildren = false; out.print("/>"); if (pretty) { out.print(LS); } } namespaceStack.pop(); break; case Node.ENTITY_REFERENCE_NODE: out.print('&'); out.print(node.getNodeName()); out.print(';'); break; case Node.CDATA_SECTION_NODE: out.print("<![CDATA["); out.print(node.getNodeValue()); out.print("]]>"); break; case Node.TEXT_NODE: normalize(node.getNodeValue(), out); break; case Node.COMMENT_NODE: out.print("<!--"); out.print(node.getNodeValue()); out.print("-->"); if (pretty) { out.print(LS); } break; case Node.PROCESSING_INSTRUCTION_NODE: out.print("<?"); out.print(node.getNodeName()); String data = node.getNodeValue(); if (data != null && data.length() > 0) { out.print(' '); out.print(data); } out.println("?>"); if (pretty) { out.print(LS); } break; } if (type == Node.ELEMENT_NODE && hasChildren) { if (pretty) { for (int i = 0; i < indent; i++) { out.print(' '); } } out.print("</"); out.print(node.getNodeName()); out.print('>'); if (pretty) { out.print(LS); } hasChildren = false; } } private static void printNamespaceDecl(Node node, NSStack namespaceStack, PrintWriter out) { switch (node.getNodeType()) { case Node.ATTRIBUTE_NODE: printNamespaceDecl(((Attr) node).getOwnerElement(), node, namespaceStack, out); break; case Node.ELEMENT_NODE: printNamespaceDecl((Element) node, node, namespaceStack, out); break; } } private static void printNamespaceDecl(Element owner, Node node, NSStack namespaceStack, PrintWriter out) { String namespaceURI = node.getNamespaceURI(); String prefix = node.getPrefix(); if (!(namespaceURI.equals(XMLNS_NS) && "xmlns".equals(prefix)) && !(namespaceURI.equals(XML_NS) && "xml".equals(prefix))) { if (getNamespace(prefix, owner) == null) { out.print(" xmlns:" + prefix + "=\"" + namespaceURI + '\"'); } } else { prefix = node.getLocalName(); namespaceURI = node.getNodeValue(); } namespaceStack.add(namespaceURI, prefix); } /** * Normalizes and prints the given string. */ public static void normalize(String s, PrintWriter fOut) { int len = (s != null) ? s.length() : 0; for (int i = 0; i < len; i++) { char c = s.charAt(i); switch (c) { case '<': fOut.print("<"); break; case '>': fOut.print(">"); break; case '&': fOut.print("&"); break; case '"': fOut.print("""); break; case '\r': case '\n': default: fOut.print(c); } } } private static String getNamespace(String prefix, Node e) { while (e != null && e.getNodeType() == Node.ELEMENT_NODE) { Attr attr = null; if (prefix == null) { attr = ((Element) e).getAttributeNode("xmlns"); } else { attr = ((Element) e).getAttributeNodeNS(XMLNS_NS, prefix); } if (attr != null) { return attr.getValue(); } e = e.getParentNode(); } return null; } }