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.ws.security.util;
21  
22  import org.apache.ws.security.WSConstants;
23  import org.w3c.dom.Attr;
24  import org.w3c.dom.Element;
25  import org.w3c.dom.NamedNodeMap;
26  import org.w3c.dom.Node;
27  
28  import java.io.PrintWriter;
29  import java.io.StringWriter;
30  import java.io.Writer;
31  
32  /**
33   * This class is a utility to serialize a DOM node as XML. This class
34   * uses the <code>DOM Level 2</code> APIs.
35   * The main difference between this class and DOMWriter is that this class
36   * generates and prints out namespace declarations.
37   *
38   * @author Matthew J. Duftler (duftler@us.ibm.com)
39   * @author Joseph Kesselman
40   */
41  public final class DOM2Writer {
42      public static final char NL = '\n';
43      public static final String LS = System.getProperty("line.separator",
44              (Character.valueOf(NL)).toString());
45      
46      private DOM2Writer() {
47          // Complete
48      }
49  
50      /**
51       * Return a string containing this node serialized as XML.
52       */
53      public static String nodeToString(Node node) {
54          StringWriter sw = new StringWriter();
55          serializeAsXML(node, sw, true);
56          return sw.toString();
57      }
58  
59      /**
60       * Return a string containing this node serialized as XML.
61       */
62      public static String nodeToString(Node node, boolean omitXMLDecl) {
63          StringWriter sw = new StringWriter();
64          serializeAsXML(node, sw, omitXMLDecl);
65          return sw.toString();
66      }
67  
68      /**
69       * Serialize this node into the writer as XML.
70       */
71      public static void serializeAsXML(Node node, Writer writer,
72                                        boolean omitXMLDecl) {
73          serializeAsXML(node, writer, omitXMLDecl, false);
74      }
75  
76      /**
77       * Serialize this node into the writer as XML.
78       */
79      public static void serializeAsXML(Node node, Writer writer,
80                                        boolean omitXMLDecl,
81                                        boolean pretty) {
82          PrintWriter out = new PrintWriter(writer);
83          if (!omitXMLDecl) {
84              out.print("<?xml version=\"1.0\" encoding=UTF-8 ?>");
85          }
86          NSStack namespaceStack = new NSStack();
87          print(node, namespaceStack, out, pretty, 0);
88          out.flush();
89      }
90  
91      private static void print(Node node, NSStack namespaceStack,
92                                PrintWriter out, boolean pretty,
93                                int indent) {
94          if (node == null) {
95              return;
96          }
97          boolean hasChildren = false;
98          int type = node.getNodeType();
99          switch (type) {
100             case Node.DOCUMENT_NODE:
101                 {
102                     Node child = node.getFirstChild();
103                     while (child != null) {
104                         print(child, namespaceStack, out, pretty, indent);
105                         child = child.getNextSibling();
106                     }
107                     break;
108                 }
109             case Node.ELEMENT_NODE:
110                 {
111                     namespaceStack.push();
112                     if (pretty) {
113                         for (int i = 0; i < indent; i++) {
114                             out.print(' ');
115                         }
116                     }
117                     out.print('<' + node.getNodeName());
118                     String elPrefix = node.getPrefix();
119                     String elNamespaceURI = node.getNamespaceURI();
120                     if (elPrefix != null &&
121                             elNamespaceURI != null &&
122                             elPrefix.length() > 0) {
123                         boolean prefixIsDeclared = false;
124                         try {
125                             String namespaceURI = namespaceStack.getNamespaceURI(elPrefix);
126                             if (elNamespaceURI.equals(namespaceURI)) {
127                                 prefixIsDeclared = true;
128                             }
129                         } catch (IllegalArgumentException e) {
130                             //
131                         }
132                         if (!prefixIsDeclared) {
133                             printNamespaceDecl(node, namespaceStack, out);
134                         }
135                     }
136                     NamedNodeMap attrs = node.getAttributes();
137                     int len = (attrs != null) ? attrs.getLength() : 0;
138                     for (int i = 0; i < len; i++) {
139                         Attr attr = (Attr) attrs.item(i);
140                         out.print(' ' + attr.getNodeName() + "=\"");
141                         normalize(attr.getValue(), out);
142                         out.print('\"');
143                         String attrPrefix = attr.getPrefix();
144                         String attrNamespaceURI = attr.getNamespaceURI();
145                         if (attrPrefix != null && attrNamespaceURI != null) {
146                             boolean prefixIsDeclared = false;
147                             try {
148                                 String namespaceURI = namespaceStack.getNamespaceURI(attrPrefix);
149                                 if (attrNamespaceURI.equals(namespaceURI)) {
150                                     prefixIsDeclared = true;
151                                 }
152                             } catch (IllegalArgumentException e) {
153                                 //
154                             }
155                             if (!prefixIsDeclared) {
156                                 printNamespaceDecl(attr, namespaceStack, out);
157                             }
158                         }
159                     }
160                     Node child = node.getFirstChild();
161                     if (child != null) {
162                         hasChildren = true;
163                         out.print('>');
164                         if (pretty) {
165                             out.print(LS);
166                         }
167                         while (child != null) {
168                             print(child, namespaceStack, out, pretty, indent + 1);
169                             child = child.getNextSibling();
170                         }
171                     } else {
172                         hasChildren = false;
173                         out.print("/>");
174                         if (pretty) {
175                             out.print(LS);
176                         }
177                     }
178                     namespaceStack.pop();
179                     break;
180                 }
181             case Node.ENTITY_REFERENCE_NODE:
182                 {
183                     out.print('&');
184                     out.print(node.getNodeName());
185                     out.print(';');
186                     break;
187                 }
188             case Node.CDATA_SECTION_NODE:
189                 {
190                     out.print("<![CDATA[");
191                     out.print(node.getNodeValue());
192                     out.print("]]>");
193                     break;
194                 }
195             case Node.TEXT_NODE:
196                 {
197                     normalize(node.getNodeValue(), out);
198                     break;
199                 }
200             case Node.COMMENT_NODE:
201                 {
202                     out.print("<!--");
203                     out.print(node.getNodeValue());
204                     out.print("-->");
205                     if (pretty) {
206                         out.print(LS);
207                     }
208                     break;
209                 }
210             case Node.PROCESSING_INSTRUCTION_NODE:
211                 {
212                     out.print("<?");
213                     out.print(node.getNodeName());
214                     String data = node.getNodeValue();
215                     if (data != null && data.length() > 0) {
216                         out.print(' ');
217                         out.print(data);
218                     }
219                     out.println("?>");
220                     if (pretty) {
221                         out.print(LS);
222                     }
223                     break;
224                 }
225         }
226         if (type == Node.ELEMENT_NODE && hasChildren) {
227             if (pretty) {
228                 for (int i = 0; i < indent; i++) {
229                     out.print(' ');
230                 }
231             }
232             out.print("</");
233             out.print(node.getNodeName());
234             out.print('>');
235             if (pretty) {
236                 out.print(LS);
237             }
238             hasChildren = false;
239         }
240     }
241 
242     private static void printNamespaceDecl(Node node,
243                                            NSStack namespaceStack,
244                                            PrintWriter out) {
245         switch (node.getNodeType()) {
246             case Node.ATTRIBUTE_NODE:
247                 {
248                     printNamespaceDecl(((Attr) node).getOwnerElement(), node,
249                             namespaceStack, out);
250                     break;
251                 }
252             case Node.ELEMENT_NODE:
253                 {
254                     printNamespaceDecl((Element) node, node, namespaceStack, out);
255                     break;
256                 }
257         }
258     }
259 
260     private static void printNamespaceDecl(Element owner, Node node,
261                                            NSStack namespaceStack,
262                                            PrintWriter out) {
263         String namespaceURI = node.getNamespaceURI();
264         String prefix = node.getPrefix();
265         if (!(namespaceURI.equals(WSConstants.XMLNS_NS) && prefix.equals("xmlns")) &&
266                 !(namespaceURI.equals(WSConstants.XML_NS) && prefix.equals("xml"))) {
267             if (WSSecurityUtil.getNamespace(prefix, owner) == null) {
268                 out.print(" xmlns:" + prefix + "=\"" + namespaceURI + '\"');
269             }
270         } else {
271             prefix = node.getLocalName();
272             namespaceURI = node.getNodeValue();
273         }
274         namespaceStack.add(namespaceURI, prefix);
275     }
276 
277     /**
278      * Normalizes and prints the given string.
279      */
280     public static void normalize(String s, PrintWriter fOut) {
281         int len = (s != null) ? s.length() : 0;
282         for (int i = 0; i < len; i++) {
283             char c = s.charAt(i);
284             switch (c) {
285                 case '<':
286                     {
287                         fOut.print("&lt;");
288                         break;
289                     }
290                 case '>':
291                     {
292                         fOut.print("&gt;");
293                         break;
294                     }
295                 case '&':
296                     {
297                         fOut.print("&amp;");
298                         break;
299                     }
300                 case '"':
301                     {
302                         fOut.print("&quot;");
303                         break;
304                     }
305                 case '\r':
306                 case '\n':
307                 default:
308                     {
309                         fOut.print(c);
310                     }
311             }
312         }
313     }
314 }