KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > xml > DOMWriter


1 /*
2  * ====================================================================
3  *
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 1999 The Apache Software Foundation. All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowlegement:
23  * "This product includes software developed by the
24  * Apache Software Foundation (http://www.apache.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
29  * Foundation" must not be used to endorse or promote products derived
30  * from this software without prior written permission. For written
31  * permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache"
34  * nor may "Apache" appear in their names without prior written
35  * permission of the Apache Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation. For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  */

56 package org.jboss.util.xml;
57
58 // $Id: DOMWriter.java 2119 2006-10-10 17:06:03Z thomas.diesler@jboss.com $
59

60 import java.io.OutputStream JavaDoc;
61 import java.io.OutputStreamWriter JavaDoc;
62 import java.io.PrintWriter JavaDoc;
63 import java.io.StringWriter JavaDoc;
64 import java.io.UnsupportedEncodingException JavaDoc;
65 import java.io.Writer JavaDoc;
66 import java.util.HashMap JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.Map JavaDoc;
69
70 import org.w3c.dom.Attr JavaDoc;
71 import org.w3c.dom.Element JavaDoc;
72 import org.w3c.dom.NamedNodeMap JavaDoc;
73 import org.w3c.dom.Node JavaDoc;
74 import org.w3c.dom.NodeList JavaDoc;
75
76 /**
77  * Traverse a DOM tree in order to print a document that is parsed.
78  *
79  * @author Andy Clark, IBM
80  * @author Thomas.Diesler@jboss.org
81  * @version $Revision: 2119 $
82  */

83 public class DOMWriter
84 {
85    // Print writer
86
private PrintWriter JavaDoc out;
87    // True, if canonical output
88
private boolean canonical;
89    // True, if pretty printing should be used
90
private boolean prettyprint;
91    // True, if the XML declaration should be written
92
private boolean writeXMLDeclaration;
93    // Explicit character set encoding
94
private String JavaDoc charsetName;
95    // indent for the pretty printer
96
private int prettyIndent;
97    // True, if the XML declaration has been written
98
private boolean wroteXMLDeclaration;
99    // The node that started the write
100
private Node JavaDoc rootNode;
101    // True if we want namespace completion
102
private boolean completeNamespaces = true;
103
104    public DOMWriter(Writer JavaDoc w)
105    {
106       this.out = new PrintWriter JavaDoc(w);
107    }
108
109    public DOMWriter(Writer JavaDoc w, String JavaDoc charsetName)
110    {
111       this.out = new PrintWriter JavaDoc(w);
112       this.charsetName = charsetName;
113       this.writeXMLDeclaration = true;
114    }
115
116    public DOMWriter(OutputStream JavaDoc stream)
117    {
118       try
119       {
120          this.out = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(stream, "UTF-8"));
121       }
122       catch (UnsupportedEncodingException JavaDoc e)
123       {
124          // ignore, UTF-8 should be available
125
}
126    }
127
128    public DOMWriter(OutputStream JavaDoc stream, String JavaDoc charsetName)
129    {
130       try
131       {
132          this.out = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(stream, charsetName));
133          this.charsetName = charsetName;
134          this.writeXMLDeclaration = true;
135       }
136       catch (UnsupportedEncodingException JavaDoc e)
137       {
138          throw new IllegalArgumentException JavaDoc("Unsupported encoding: " + charsetName);
139       }
140    }
141
142    /**
143     * Print a node with explicit prettyprinting.
144     * The defaults for all other DOMWriter properties apply.
145     *
146     */

147    public static String JavaDoc printNode(Node JavaDoc node, boolean prettyprint)
148    {
149       StringWriter JavaDoc strw = new StringWriter JavaDoc();
150       new DOMWriter(strw).setPrettyprint(prettyprint).print(node);
151       return strw.toString();
152    }
153
154    public boolean isCanonical()
155    {
156       return canonical;
157    }
158
159    /**
160     * Set wheter entities should appear in their canonical form.
161     * The default is false.
162     */

163    public DOMWriter setCanonical(boolean canonical)
164    {
165       this.canonical = canonical;
166       return this;
167    }
168
169    /**
170     * Set wheter subelements should have their namespaces completed.
171     * Setting this to false may lead to invalid XML fragments.
172     * The default is true.
173     */

174    public DOMWriter setCompleteNamespaces(boolean complete)
175    {
176       this.completeNamespaces = complete;
177       return this;
178    }
179
180    public boolean isPrettyprint()
181    {
182       return prettyprint;
183    }
184
185    /**
186     * Set wheter element should be indented.
187     * The default is false.
188     */

189    public DOMWriter setPrettyprint(boolean prettyprint)
190    {
191       this.prettyprint = prettyprint;
192       return this;
193    }
194
195    public boolean isWriteXMLDeclaration()
196    {
197       return writeXMLDeclaration;
198    }
199
200    /**
201     * Set wheter the XML declaration should be written.
202     * The default is false.
203     */

204    public DOMWriter setWriteXMLDeclaration(boolean flag)
205    {
206       this.writeXMLDeclaration = flag;
207       return this;
208    }
209
210    public void print(Node JavaDoc node)
211    {
212       rootNode = node;
213       printInternal(node, false);
214    }
215
216    private void printInternal(Node JavaDoc node, boolean indentEndMarker)
217    {
218       // is there anything to do?
219
if (node == null)
220       {
221          return;
222       }
223
224       // JBAS-2117 - Don't skip the DOCUMENT_NODE
225
// if (node instanceof Document) node = ((Document)node).getDocumentElement();
226

227       if (wroteXMLDeclaration == false && writeXMLDeclaration == true && canonical == false)
228       {
229          out.print("<?xml version='1.0'");
230          if (charsetName != null)
231             out.print(" encoding='" + charsetName + "'");
232
233          out.print("?>");
234          if (prettyprint)
235             out.println();
236          
237          wroteXMLDeclaration = true;
238       }
239
240       int type = node.getNodeType();
241       boolean hasChildNodes = node.getChildNodes().getLength() > 0;
242
243       String JavaDoc nodeName = node.getNodeName();
244       switch (type)
245       {
246          // print document
247
case Node.DOCUMENT_NODE:
248          {
249             NodeList JavaDoc children = node.getChildNodes();
250             for (int iChild = 0; iChild < children.getLength(); iChild++)
251             {
252                printInternal(children.item(iChild), false);
253             }
254             out.flush();
255             break;
256          }
257
258             // print element with attributes
259
case Node.ELEMENT_NODE:
260          {
261             Element JavaDoc element = (Element JavaDoc)node;
262             if (prettyprint)
263             {
264                for (int i = 0; i < prettyIndent; i++)
265                {
266                   out.print(' ');
267                }
268                prettyIndent++;
269             }
270
271             out.print('<');
272             out.print(nodeName);
273
274             Map JavaDoc nsMap = new HashMap JavaDoc();
275             String JavaDoc elPrefix = node.getPrefix();
276             if (elPrefix != null)
277             {
278                String JavaDoc nsURI = getNamespaceURI(elPrefix, element, rootNode);
279                nsMap.put(elPrefix, nsURI);
280             }
281
282             Attr JavaDoc attrs[] = sortAttributes(node.getAttributes());
283             for (int i = 0; i < attrs.length; i++)
284             {
285                Attr JavaDoc attr = attrs[i];
286                String JavaDoc atPrefix = attr.getPrefix();
287                String JavaDoc atName = attr.getNodeName();
288                String JavaDoc atValue = normalize(attr.getNodeValue(), canonical);
289
290                if (atPrefix != null && (atPrefix.equals("xmlns") || atPrefix.equals("xml")) == false)
291                {
292                   String JavaDoc nsURI = getNamespaceURI(atPrefix, element, rootNode);
293                   nsMap.put(atPrefix, nsURI);
294                }
295
296                out.print(" " + atName + "='" + atValue + "'");
297             }
298
299             // Add missing namespace declaration
300
if (completeNamespaces)
301             {
302                Iterator JavaDoc itPrefix = nsMap.keySet().iterator();
303                while (itPrefix.hasNext())
304                {
305                   String JavaDoc prefix = (String JavaDoc)itPrefix.next();
306                   String JavaDoc nsURI = (String JavaDoc)nsMap.get(prefix);
307                   if (nsURI == null)
308                   {
309                      nsURI = getNamespaceURI(prefix, element, null);
310                      out.print(" xmlns:" + prefix + "='" + nsURI + "'");
311                   }
312                }
313             }
314
315             if (hasChildNodes)
316             {
317                out.print('>');
318             }
319
320             // Find out if the end marker is indented
321
indentEndMarker = isEndMarkerIndented(node);
322
323             if (indentEndMarker)
324             {
325                out.print('\n');
326             }
327
328             NodeList JavaDoc childNodes = node.getChildNodes();
329             int len = childNodes.getLength();
330             for (int i = 0; i < len; i++)
331             {
332                Node JavaDoc childNode = childNodes.item(i);
333                printInternal(childNode, false);
334             }
335             break;
336          }
337
338             // handle entity reference nodes
339
case Node.ENTITY_REFERENCE_NODE:
340          {
341             if (canonical)
342             {
343                NodeList JavaDoc children = node.getChildNodes();
344                if (children != null)
345                {
346                   int len = children.getLength();
347                   for (int i = 0; i < len; i++)
348                   {
349                      printInternal(children.item(i), false);
350                   }
351                }
352             }
353             else
354             {
355                out.print('&');
356                out.print(nodeName);
357                out.print(';');
358             }
359             break;
360          }
361
362             // print cdata sections
363
case Node.CDATA_SECTION_NODE:
364          {
365             if (canonical)
366             {
367                out.print(normalize(node.getNodeValue(), canonical));
368             }
369             else
370             {
371                out.print("<![CDATA[");
372                out.print(node.getNodeValue());
373                out.print("]]>");
374             }
375             break;
376          }
377
378             // print text
379
case Node.TEXT_NODE:
380          {
381             String JavaDoc text = normalize(node.getNodeValue(), canonical);
382             if (prettyprint == false || text.trim().length() > 0)
383                out.print(text);
384             break;
385          }
386
387             // print processing instruction
388
case Node.PROCESSING_INSTRUCTION_NODE:
389          {
390             out.print("<?");
391             out.print(nodeName);
392             String JavaDoc data = node.getNodeValue();
393             if (data != null && data.length() > 0)
394             {
395                out.print(' ');
396                out.print(data);
397             }
398             out.print("?>");
399             break;
400          }
401
402             // print comment
403
case Node.COMMENT_NODE:
404          {
405             for (int i = 0; i < prettyIndent; i++)
406             {
407                out.print(' ');
408             }
409
410             out.print("<!--");
411             String JavaDoc data = node.getNodeValue();
412             if (data != null)
413             {
414                out.print(data);
415             }
416             out.print("-->");
417
418             if (prettyprint)
419             {
420                out.print('\n');
421             }
422
423             break;
424          }
425       }
426
427       if (type == Node.ELEMENT_NODE)
428       {
429          if (prettyprint)
430             prettyIndent--;
431
432          if (hasChildNodes == false)
433          {
434             out.print("/>");
435          }
436          else
437          {
438             if (indentEndMarker)
439             {
440                for (int i = 0; i < prettyIndent; i++)
441                {
442                   out.print(' ');
443                }
444             }
445
446             out.print("</");
447             out.print(nodeName);
448             out.print('>');
449          }
450
451          if (prettyIndent > 0)
452          {
453             out.print('\n');
454          }
455       }
456       out.flush();
457    }
458
459    private String JavaDoc getNamespaceURI(String JavaDoc prefix, Element JavaDoc element, Node JavaDoc stopNode)
460    {
461       Node JavaDoc parent = element.getParentNode();
462       String JavaDoc nsURI = element.getAttribute("xmlns:" + prefix);
463       if (nsURI.length() == 0 && element != stopNode && parent instanceof Element JavaDoc)
464          return getNamespaceURI(prefix, (Element JavaDoc)parent, stopNode);
465
466       return (nsURI.length() > 0 ? nsURI : null);
467    }
468
469    private boolean isEndMarkerIndented(Node JavaDoc node)
470    {
471       if (prettyprint)
472       {
473          NodeList JavaDoc childNodes = node.getChildNodes();
474          int len = childNodes.getLength();
475          for (int i = 0; i < len; i++)
476          {
477             Node JavaDoc children = childNodes.item(i);
478             if (children.getNodeType() == Node.ELEMENT_NODE)
479             {
480                return true;
481             }
482          }
483       }
484       return false;
485    }
486
487    /** Returns a sorted list of attributes. */
488    private Attr JavaDoc[] sortAttributes(NamedNodeMap JavaDoc attrs)
489    {
490
491       int len = (attrs != null) ? attrs.getLength() : 0;
492       Attr JavaDoc array[] = new Attr JavaDoc[len];
493       for (int i = 0; i < len; i++)
494       {
495          array[i] = (Attr JavaDoc)attrs.item(i);
496       }
497       for (int i = 0; i < len - 1; i++)
498       {
499          String JavaDoc name = array[i].getNodeName();
500          int index = i;
501          for (int j = i + 1; j < len; j++)
502          {
503             String JavaDoc curName = array[j].getNodeName();
504             if (curName.compareTo(name) < 0)
505             {
506                name = curName;
507                index = j;
508             }
509          }
510          if (index != i)
511          {
512             Attr JavaDoc temp = array[i];
513             array[i] = array[index];
514             array[index] = temp;
515          }
516       }
517       return (array);
518    }
519
520    /** Normalizes the given string. */
521    public static String JavaDoc normalize(String JavaDoc s, boolean canonical)
522    {
523       StringBuffer JavaDoc str = new StringBuffer JavaDoc();
524
525       int len = (s != null) ? s.length() : 0;
526       for (int i = 0; i < len; i++)
527       {
528          char ch = s.charAt(i);
529          switch (ch)
530          {
531             case '<':
532             {
533                str.append("&lt;");
534                break;
535             }
536             case '>':
537             {
538                str.append("&gt;");
539                break;
540             }
541             case '&':
542             {
543                str.append("&amp;");
544                break;
545             }
546             case '"':
547             {
548                str.append("&quot;");
549                break;
550             }
551             case '\r':
552             case '\n':
553             {
554                if (canonical)
555                {
556                   str.append("&#");
557                   str.append(Integer.toString(ch));
558                   str.append(';');
559                   break;
560                }
561                // else, default append char
562
}
563             default:
564             {
565                str.append(ch);
566             }
567          }
568       }
569       return (str.toString());
570    }
571 }
572
Popular Tags