View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.policy.model;
21  
22  import org.w3c.dom.Attr;
23  import org.w3c.dom.Element;
24  import org.w3c.dom.NamedNodeMap;
25  import org.w3c.dom.Node;
26  
27  import java.io.PrintWriter;
28  import java.io.StringWriter;
29  import java.io.Writer;
30  
31  /**
32   * This class is a utility to serialize a DOM node as XML. This class
33   * uses the <code>DOM Level 2</code> APIs.
34   * The main difference between this class and DOMWriter is that this class
35   * generates and prints out namespace declarations.
36   */
37  final class DOM2Writer {
38      public static final char NL = '\n';
39      public static final String LS = System.getProperty("line.separator",
40              Character.valueOf(NL).toString());
41  
42      public static final String XMLNS_NS = "http://www.w3.org/2000/xmlns/";
43      public static final String XML_NS = "http://www.w3.org/XML/1998/namespace";
44  
45      private DOM2Writer() {
46          // Complete
47      }
48  
49      /**
50       * Return a string containing this node serialized as XML.
51       */
52      public static String nodeToString(Node node) {
53          StringWriter sw = new StringWriter();
54          serializeAsXML(node, sw, true);
55          return sw.toString();
56      }
57  
58      /**
59       * Return a string containing this node serialized as XML.
60       */
61      public static String nodeToString(Node node, boolean omitXMLDecl) {
62          StringWriter sw = new StringWriter();
63          serializeAsXML(node, sw, omitXMLDecl);
64          return sw.toString();
65      }
66  
67      /**
68       * Serialize this node into the writer as XML.
69       */
70      public static void serializeAsXML(Node node, Writer writer,
71                                        boolean omitXMLDecl) {
72          serializeAsXML(node, writer, omitXMLDecl, false);
73      }
74  
75      /**
76       * Serialize this node into the writer as XML.
77       */
78      public static void serializeAsXML(Node node, Writer writer,
79                                        boolean omitXMLDecl,
80                                        boolean pretty) {
81          PrintWriter out = new PrintWriter(writer);
82          if (!omitXMLDecl) {
83              out.print("<?xml version=\"1.0\" encoding=UTF-8 ?>");
84          }
85          NSStack namespaceStack = new NSStack();
86          print(node, namespaceStack, out, pretty, 0);
87          out.flush();
88      }
89  
90      private static void print(Node node, NSStack namespaceStack,
91                                PrintWriter out, boolean pretty,
92                                int indent) {
93          if (node == null) {
94              return;
95          }
96          boolean hasChildren = false;
97          int type = node.getNodeType();
98          switch (type) {
99              case Node.DOCUMENT_NODE:
100                 Node child = node.getFirstChild();
101                 while (child != null) {
102                     print(child, namespaceStack, out, pretty, indent);
103                     child = child.getNextSibling();
104                 }
105                 break;
106             case Node.ELEMENT_NODE:
107                 namespaceStack.push();
108                 if (pretty) {
109                     for (int i = 0; i < indent; i++) {
110                         out.print(' ');
111                     }
112                 }
113                 out.print('<' + node.getNodeName());
114                 String elPrefix = node.getPrefix();
115                 String elNamespaceURI = node.getNamespaceURI();
116                 if (elPrefix != null && elNamespaceURI != null && elPrefix.length() > 0) {
117                     boolean prefixIsDeclared = false;
118                     try {
119                         String namespaceURI = namespaceStack.getNamespaceURI(elPrefix);
120                         if (elNamespaceURI.equals(namespaceURI)) {
121                             prefixIsDeclared = true;
122                         }
123                     } catch (IllegalArgumentException e) { //NOPMD
124                         //
125                     }
126                     if (!prefixIsDeclared) {
127                         printNamespaceDecl(node, namespaceStack, out);
128                     }
129                 }
130                 NamedNodeMap attrs = node.getAttributes();
131                 int len = (attrs != null) ? attrs.getLength() : 0;
132                 for (int i = 0; i < len; i++) {
133                     Attr attr = (Attr) attrs.item(i);
134                     out.print(' ' + attr.getNodeName() + "=\"");
135                     normalize(attr.getValue(), out);
136                     out.print('\"');
137                     String attrPrefix = attr.getPrefix();
138                     String attrNamespaceURI = attr.getNamespaceURI();
139                     if (attrPrefix != null && attrNamespaceURI != null) {
140                         boolean prefixIsDeclared = false;
141                         try {
142                             String namespaceURI = namespaceStack.getNamespaceURI(attrPrefix);
143                             if (attrNamespaceURI.equals(namespaceURI)) {
144                                 prefixIsDeclared = true;
145                             }
146                         } catch (IllegalArgumentException e) { //NOPMD
147                             //
148                         }
149                         if (!prefixIsDeclared) {
150                             printNamespaceDecl(attr, namespaceStack, out);
151                         }
152                     }
153                 }
154                 child = node.getFirstChild();
155                 if (child != null) {
156                     hasChildren = true;
157                     out.print('>');
158                     if (pretty) {
159                         out.print(LS);
160                     }
161                     while (child != null) {
162                         print(child, namespaceStack, out, pretty, indent + 1);
163                         child = child.getNextSibling();
164                     }
165                 } else {
166                     hasChildren = false;
167                     out.print("/>");
168                     if (pretty) {
169                         out.print(LS);
170                     }
171                 }
172                 namespaceStack.pop();
173                 break;
174             case Node.ENTITY_REFERENCE_NODE:
175                 out.print('&');
176                 out.print(node.getNodeName());
177                 out.print(';');
178                 break;
179             case Node.CDATA_SECTION_NODE:
180                 out.print("<![CDATA[");
181                 out.print(node.getNodeValue());
182                 out.print("]]>");
183                 break;
184             case Node.TEXT_NODE:
185                 normalize(node.getNodeValue(), out);
186                 break;
187             case Node.COMMENT_NODE:
188                 out.print("<!--");
189                 out.print(node.getNodeValue());
190                 out.print("-->");
191                 if (pretty) {
192                     out.print(LS);
193                 }
194                 break;
195             case Node.PROCESSING_INSTRUCTION_NODE:
196                 out.print("<?");
197                 out.print(node.getNodeName());
198                 String data = node.getNodeValue();
199                 if (data != null && data.length() > 0) {
200                     out.print(' ');
201                     out.print(data);
202                 }
203                 out.println("?>");
204                 if (pretty) {
205                     out.print(LS);
206                 }
207                 break;
208         }
209         if (type == Node.ELEMENT_NODE && hasChildren) {
210             if (pretty) {
211                 for (int i = 0; i < indent; i++) {
212                     out.print(' ');
213                 }
214             }
215             out.print("</");
216             out.print(node.getNodeName());
217             out.print('>');
218             if (pretty) {
219                 out.print(LS);
220             }
221             hasChildren = false;
222         }
223     }
224 
225     private static void printNamespaceDecl(Node node,
226                                            NSStack namespaceStack,
227                                            PrintWriter out) {
228         switch (node.getNodeType()) {   //NOPMD
229             case Node.ATTRIBUTE_NODE:
230                 printNamespaceDecl(((Attr) node).getOwnerElement(), node,
231                                    namespaceStack, out);
232                 break;
233             case Node.ELEMENT_NODE:
234                 printNamespaceDecl((Element) node, node, namespaceStack, out);
235                 break;
236         }
237     }
238 
239     private static void printNamespaceDecl(Element owner, Node node,
240                                            NSStack namespaceStack,
241                                            PrintWriter out) {
242         String namespaceURI = node.getNamespaceURI();
243         String prefix = node.getPrefix();
244         if (!(namespaceURI.equals(XMLNS_NS) && "xmlns".equals(prefix))
245             && !(namespaceURI.equals(XML_NS) && "xml".equals(prefix))) {
246             if (getNamespace(prefix, owner) == null) {
247                 out.print(" xmlns:" + prefix + "=\"" + namespaceURI + '\"');
248             }
249         } else {
250             prefix = node.getLocalName();
251             namespaceURI = node.getNodeValue();
252         }
253         namespaceStack.add(namespaceURI, prefix);
254     }
255 
256     /**
257      * Normalizes and prints the given string.
258      */
259     public static void normalize(String s, PrintWriter fOut) {
260         int len = (s != null) ? s.length() : 0;
261         for (int i = 0; i < len; i++) {
262             char c = s.charAt(i);
263             switch (c) {
264                 case '<':
265                     fOut.print("&lt;");
266                     break;
267                 case '>':
268                     fOut.print("&gt;");
269                     break;
270                 case '&':
271                     fOut.print("&amp;");
272                     break;
273                 case '"':
274                     fOut.print("&quot;");
275                     break;
276                 default:
277                     fOut.print(c);
278                     break;
279             }
280         }
281     }
282 
283     private static String getNamespace(String prefix, Node e) {
284         while (e != null && e.getNodeType() == Node.ELEMENT_NODE) {
285             Attr attr = null;
286             if (prefix == null) {
287                 attr = ((Element) e).getAttributeNode("xmlns");
288             } else {
289                 attr = ((Element) e).getAttributeNodeNS(XMLNS_NS, prefix);
290             }
291             if (attr != null) {
292                 return attr.getValue();
293             }
294             e = e.getParentNode();
295         }
296         return null;
297     }
298 }