1
2
3
4
5
6
7
8
9
10
11
12
13
14
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("<");
282 break;
283 }
284 case '>':
285 {
286 fOut.print(">");
287 break;
288 }
289 case '&':
290 {
291 fOut.print("&");
292 break;
293 }
294 case '"':
295 {
296 fOut.print(""");
297 break;
298 }
299 case '\r':
300 case '\n':
301 default:
302 {
303 fOut.print(c);
304 }
305 }
306 }
307 }
308 }