View Javadoc

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