View Javadoc

1   /*
2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3    *
4    * This software is open source.
5    * See the bottom of this file for the licence.
6    */
7   
8   package org.dom4j.io;
9   
10  import java.util.List;
11  
12  import org.dom4j.Attribute;
13  import org.dom4j.CDATA;
14  import org.dom4j.Comment;
15  import org.dom4j.Document;
16  import org.dom4j.DocumentException;
17  import org.dom4j.Element;
18  import org.dom4j.Entity;
19  import org.dom4j.Namespace;
20  import org.dom4j.ProcessingInstruction;
21  import org.dom4j.Text;
22  import org.dom4j.tree.NamespaceStack;
23  
24  import org.w3c.dom.DOMImplementation;
25  
26  /***
27   * <p>
28   * <code>DOMWriter</code> takes a DOM4J tree and outputs it as a W3C DOM
29   * object
30   * </p>
31   * 
32   * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
33   * @version $Revision: 1.17 $
34   */
35  public class DOMWriter {
36      private static boolean loggedWarning = false;
37  
38      private static final String[] DEFAULT_DOM_DOCUMENT_CLASSES = {
39              "org.apache.xerces.dom.DocumentImpl", // Xerces
40              "gnu.xml.dom.DomDocument", // GNU JAXP
41              "org.apache.crimson.tree.XmlDocument", // Crimson
42              "com.sun.xml.tree.XmlDocument", // Sun's Project X
43              "oracle.xml.parser.v2.XMLDocument", // Oracle V2
44              "oracle.xml.parser.XMLDocument", // Oracle V1
45              "org.dom4j.dom.DOMDocument" // Internal DOM implementation
46      };
47  
48      // the Class used to create new DOM Document instances
49      private Class domDocumentClass;
50  
51      /*** stack of <code>Namespace</code> objects */
52      private NamespaceStack namespaceStack = new NamespaceStack();
53  
54      public DOMWriter() {
55      }
56  
57      public DOMWriter(Class domDocumentClass) {
58          this.domDocumentClass = domDocumentClass;
59      }
60  
61      public Class getDomDocumentClass() throws DocumentException {
62          Class result = domDocumentClass;
63  
64          if (result == null) {
65              // lets try and find one in the classpath
66              int size = DEFAULT_DOM_DOCUMENT_CLASSES.length;
67  
68              for (int i = 0; i < size; i++) {
69                  try {
70                      String name = DEFAULT_DOM_DOCUMENT_CLASSES[i];
71                      result = Class.forName(name, true, DOMWriter.class
72                              .getClassLoader());
73  
74                      if (result != null) {
75                          break;
76                      }
77                  } catch (Exception e) {
78                      // could not load class correctly
79                      // lets carry on to the next one
80                  }
81              }
82          }
83  
84          return result;
85      }
86  
87      /***
88       * Sets the DOM {@link org.w3c.dom.Document}implementation class used by
89       * the writer when creating DOM documents.
90       * 
91       * @param domDocumentClass
92       *            is the Class implementing the {@linkorg.w3c.dom.Document}
93       *            interface
94       */
95      public void setDomDocumentClass(Class domDocumentClass) {
96          this.domDocumentClass = domDocumentClass;
97      }
98  
99      /***
100      * Sets the DOM {@link org.w3c.dom.Document}implementation class name used
101      * by the writer when creating DOM documents.
102      * 
103      * @param name
104      *            is the name of the Class implementing the {@link
105      *            org.w3c.dom.Document} interface
106      * 
107      * @throws DocumentException
108      *             if the class could not be loaded
109      */
110     public void setDomDocumentClassName(String name) throws DocumentException {
111         try {
112             this.domDocumentClass = Class.forName(name, true, DOMWriter.class
113                     .getClassLoader());
114         } catch (Exception e) {
115             throw new DocumentException("Could not load the DOM Document "
116                     + "class: " + name, e);
117         }
118     }
119 
120     public org.w3c.dom.Document write(Document document)
121             throws DocumentException {
122         if (document instanceof org.w3c.dom.Document) {
123             return (org.w3c.dom.Document) document;
124         }
125 
126         resetNamespaceStack();
127 
128         org.w3c.dom.Document domDocument = createDomDocument(document);
129         appendDOMTree(domDocument, domDocument, document.content());
130         namespaceStack.clear();
131 
132         return domDocument;
133     }
134 
135     public org.w3c.dom.Document write(Document document,
136             org.w3c.dom.DOMImplementation domImpl) throws DocumentException {
137         if (document instanceof org.w3c.dom.Document) {
138             return (org.w3c.dom.Document) document;
139         }
140 
141         resetNamespaceStack();
142 
143         org.w3c.dom.Document domDocument = createDomDocument(document, domImpl);
144         appendDOMTree(domDocument, domDocument, document.content());
145         namespaceStack.clear();
146 
147         return domDocument;
148     }
149 
150     protected void appendDOMTree(org.w3c.dom.Document domDocument,
151             org.w3c.dom.Node domCurrent, List content) {
152         int size = content.size();
153 
154         for (int i = 0; i < size; i++) {
155             Object object = content.get(i);
156 
157             if (object instanceof Element) {
158                 appendDOMTree(domDocument, domCurrent, (Element) object);
159             } else if (object instanceof String) {
160                 appendDOMTree(domDocument, domCurrent, (String) object);
161             } else if (object instanceof Text) {
162                 Text text = (Text) object;
163                 appendDOMTree(domDocument, domCurrent, text.getText());
164             } else if (object instanceof CDATA) {
165                 appendDOMTree(domDocument, domCurrent, (CDATA) object);
166             } else if (object instanceof Comment) {
167                 appendDOMTree(domDocument, domCurrent, (Comment) object);
168             } else if (object instanceof Entity) {
169                 appendDOMTree(domDocument, domCurrent, (Entity) object);
170             } else if (object instanceof ProcessingInstruction) {
171                 appendDOMTree(domDocument, domCurrent,
172                         (ProcessingInstruction) object);
173             }
174         }
175     }
176 
177     protected void appendDOMTree(org.w3c.dom.Document domDocument,
178             org.w3c.dom.Node domCurrent, Element element) {
179         String elUri = element.getNamespaceURI();
180         String elName = element.getQualifiedName();
181         org.w3c.dom.Element domElement = domDocument.createElementNS(elUri,
182                 elName);
183 
184         int stackSize = namespaceStack.size();
185 
186         // add the namespace of the element first
187         Namespace elementNamespace = element.getNamespace();
188 
189         if (isNamespaceDeclaration(elementNamespace)) {
190             namespaceStack.push(elementNamespace);
191             writeNamespace(domElement, elementNamespace);
192         }
193 
194         // add the additional declared namespaces
195         List declaredNamespaces = element.declaredNamespaces();
196 
197         for (int i = 0, size = declaredNamespaces.size(); i < size; i++) {
198             Namespace namespace = (Namespace) declaredNamespaces.get(i);
199 
200             if (isNamespaceDeclaration(namespace)) {
201                 namespaceStack.push(namespace);
202                 writeNamespace(domElement, namespace);
203             }
204         }
205 
206         // add the attributes
207         for (int i = 0, size = element.attributeCount(); i < size; i++) {
208             Attribute attribute = (Attribute) element.attribute(i);
209             String attUri = attribute.getNamespaceURI();
210             String attName = attribute.getQualifiedName();
211             String value = attribute.getValue();
212             domElement.setAttributeNS(attUri, attName, value);
213         }
214 
215         // add content
216         appendDOMTree(domDocument, domElement, element.content());
217 
218         domCurrent.appendChild(domElement);
219 
220         while (namespaceStack.size() > stackSize) {
221             namespaceStack.pop();
222         }
223     }
224 
225     protected void appendDOMTree(org.w3c.dom.Document domDocument,
226             org.w3c.dom.Node domCurrent, CDATA cdata) {
227         org.w3c.dom.CDATASection domCDATA = domDocument
228                 .createCDATASection(cdata.getText());
229         domCurrent.appendChild(domCDATA);
230     }
231 
232     protected void appendDOMTree(org.w3c.dom.Document domDocument,
233             org.w3c.dom.Node domCurrent, Comment comment) {
234         org.w3c.dom.Comment domComment = domDocument.createComment(comment
235                 .getText());
236         domCurrent.appendChild(domComment);
237     }
238 
239     protected void appendDOMTree(org.w3c.dom.Document domDocument,
240             org.w3c.dom.Node domCurrent, String text) {
241         org.w3c.dom.Text domText = domDocument.createTextNode(text);
242         domCurrent.appendChild(domText);
243     }
244 
245     protected void appendDOMTree(org.w3c.dom.Document domDocument,
246             org.w3c.dom.Node domCurrent, Entity entity) {
247         org.w3c.dom.EntityReference domEntity = domDocument
248                 .createEntityReference(entity.getName());
249         domCurrent.appendChild(domEntity);
250     }
251 
252     protected void appendDOMTree(org.w3c.dom.Document domDoc,
253             org.w3c.dom.Node domCurrent, ProcessingInstruction pi) {
254         org.w3c.dom.ProcessingInstruction domPI = domDoc
255                 .createProcessingInstruction(pi.getTarget(), pi.getText());
256         domCurrent.appendChild(domPI);
257     }
258 
259     protected void writeNamespace(org.w3c.dom.Element domElement,
260             Namespace namespace) {
261         String attributeName = attributeNameForNamespace(namespace);
262 
263         // domElement.setAttributeNS("", attributeName, namespace.getURI());
264         domElement.setAttribute(attributeName, namespace.getURI());
265     }
266 
267     protected String attributeNameForNamespace(Namespace namespace) {
268         String xmlns = "xmlns";
269         String prefix = namespace.getPrefix();
270 
271         if (prefix.length() > 0) {
272             return xmlns + ":" + prefix;
273         }
274 
275         return xmlns;
276     }
277 
278     protected org.w3c.dom.Document createDomDocument(Document document)
279             throws DocumentException {
280         org.w3c.dom.Document result = null;
281 
282         // use the given domDocumentClass (if not null)
283         if (domDocumentClass != null) {
284             try {
285                 result = (org.w3c.dom.Document) domDocumentClass.newInstance();
286             } catch (Exception e) {
287                 throw new DocumentException(
288                         "Could not instantiate an instance "
289                                 + "of DOM Document with class: "
290                                 + domDocumentClass.getName(), e);
291             }
292         } else {
293             // lets try JAXP first before using the hardcoded default parsers
294             result = createDomDocumentViaJAXP();
295 
296             if (result == null) {
297                 Class theClass = getDomDocumentClass();
298 
299                 try {
300                     result = (org.w3c.dom.Document) theClass.newInstance();
301                 } catch (Exception e) {
302                     throw new DocumentException("Could not instantiate an "
303                             + "instance of DOM Document " + "with class: "
304                             + theClass.getName(), e);
305                 }
306             }
307         }
308 
309         return result;
310     }
311 
312     protected org.w3c.dom.Document createDomDocumentViaJAXP()
313             throws DocumentException {
314         try {
315             return JAXPHelper.createDocument(false, true);
316         } catch (Throwable e) {
317             if (!loggedWarning) {
318                 loggedWarning = true;
319 
320                 if (SAXHelper.isVerboseErrorReporting()) {
321                     // log all exceptions as warnings and carry
322                     // on as we have a default SAX parser we can use
323                     System.out.println("Warning: Caught exception attempting "
324                             + "to use JAXP to create a W3C DOM " + "document");
325                     System.out.println("Warning: Exception was: " + e);
326                     e.printStackTrace();
327                 } else {
328                     System.out.println("Warning: Error occurred using JAXP to "
329                             + "create a DOM document.");
330                 }
331             }
332         }
333 
334         return null;
335     }
336 
337     protected org.w3c.dom.Document createDomDocument(Document document,
338             DOMImplementation domImpl) throws DocumentException {
339         String namespaceURI = null;
340         String qualifiedName = null;
341         org.w3c.dom.DocumentType docType = null;
342 
343         return domImpl.createDocument(namespaceURI, qualifiedName, docType);
344     }
345 
346     protected boolean isNamespaceDeclaration(Namespace ns) {
347         if ((ns != null) && (ns != Namespace.NO_NAMESPACE)
348                 && (ns != Namespace.XML_NAMESPACE)) {
349             String uri = ns.getURI();
350 
351             if ((uri != null) && (uri.length() > 0)) {
352                 if (!namespaceStack.contains(ns)) {
353                     return true;
354                 }
355             }
356         }
357 
358         return false;
359     }
360 
361     protected void resetNamespaceStack() {
362         namespaceStack.clear();
363         namespaceStack.push(Namespace.XML_NAMESPACE);
364     }
365 }
366 
367 /*
368  * Redistribution and use of this software and associated documentation
369  * ("Software"), with or without modification, are permitted provided that the
370  * following conditions are met:
371  * 
372  * 1. Redistributions of source code must retain copyright statements and
373  * notices. Redistributions must also contain a copy of this document.
374  * 
375  * 2. Redistributions in binary form must reproduce the above copyright notice,
376  * this list of conditions and the following disclaimer in the documentation
377  * and/or other materials provided with the distribution.
378  * 
379  * 3. The name "DOM4J" must not be used to endorse or promote products derived
380  * from this Software without prior written permission of MetaStuff, Ltd. For
381  * written permission, please contact dom4j-info@metastuff.com.
382  * 
383  * 4. Products derived from this Software may not be called "DOM4J" nor may
384  * "DOM4J" appear in their names without prior written permission of MetaStuff,
385  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
386  * 
387  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
388  * 
389  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
390  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
391  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
392  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
393  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
394  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
395  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
396  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
397  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
398  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
399  * POSSIBILITY OF SUCH DAMAGE.
400  * 
401  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
402  */