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  */