View Javadoc

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