001/*
002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
003 * 
004 * This software is open source. 
005 * See the bottom of this file for the licence.
006 * 
007 * $Id: DOMWriter.java,v 1.5 2001/08/09 13:35:14 jstrachan Exp $
008 */
009
010package org.dom4j.io;
011
012import java.util.HashSet;
013import java.util.Iterator;
014import java.util.List;
015import java.util.Set;
016   
017import org.dom4j.Attribute;
018import org.dom4j.CDATA;
019import org.dom4j.Comment;
020import org.dom4j.DocumentType;
021import org.dom4j.Document;
022import org.dom4j.DocumentException;
023import org.dom4j.Element;
024import org.dom4j.Entity;
025import org.dom4j.Namespace;
026import org.dom4j.Node;
027import org.dom4j.ProcessingInstruction;
028import org.dom4j.Text;
029import org.dom4j.tree.NamespaceStack;
030
031/** <p><code>DOMWriter</code> takes a DOM4J tree and outputs
032  * it as a W3C DOM object</p>
033  *
034  * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
035  * @version $Revision: 1.5 $
036  */
037public class DOMWriter {
038
039    private static boolean loggedWarning = false;
040    private static final String[] DEFAULT_DOM_DOCUMENT_CLASSES = {
041        "org.apache.xerces.dom.DocumentImpl", // Xerces
042        "org.apache.crimson.tree.XmlDocument", // Crimson
043        "com.sun.xml.tree.XmlDocument", // Sun's Project X
044        "oracle.xml.parser.v2.XMLDocument", // Oracle V2
045        "oracle.xml.parser.XMLDocument" // Oracle V1
046    };
047
048    // the Class used to create new DOM Document instances
049    private Class domDocumentClass;
050    
051    /** stack of <code>Namespace</code> objects */
052    private NamespaceStack namespaceStack = new NamespaceStack();
053
054     
055    public DOMWriter() {
056    }
057    
058    public DOMWriter(Class domDocumentClass) {
059        this.domDocumentClass = domDocumentClass;
060    }
061
062    public Class getDomDocumentClass() throws DocumentException {
063        if ( domDocumentClass == null ) {
064            // lets try and find one in the classpath
065            int size = DEFAULT_DOM_DOCUMENT_CLASSES.length;
066            for ( int i = 0; i < size; i++ ) {
067                try {
068                    String name = DEFAULT_DOM_DOCUMENT_CLASSES[i];
069                    domDocumentClass = Class.forName( 
070                        name,
071                        true,
072                        DOMWriter.class.getClassLoader()
073                    );
074                    if ( domDocumentClass != null ) {
075                        break;
076                    }
077                }
078                catch (Exception e) {
079                    // could not load class correctly
080                    // lets carry on to the next one
081                }
082            }
083        }
084        return domDocumentClass;
085    }
086    
087    /** Sets the DOM {@link org.w3c.dom.Document} implementation
088      * class used by the writer when creating DOM documents.
089      *
090      * @param domDocumentClass is the Class implementing
091      * the {@link org.w3c.dom.Document} interface
092      */
093    public void setDomDocumentClass(Class domDocumentClass) {
094        this.domDocumentClass = domDocumentClass;
095    }
096    
097    /** Sets the DOM {@link org.w3c.dom.Document} implementation
098      * class name used by the writer when creating DOM documents.
099      *
100      * @param className is the name of the Class implementing
101      * the {@link org.w3c.dom.Document} interface
102      * @throws DocumentException if the class could not be loaded
103      */
104    public void setDomDocumentClassName(String className) throws DocumentException {
105        try {
106            this.domDocumentClass = Class.forName( 
107                className,
108                true,
109                DOMWriter.class.getClassLoader()
110            );
111        }
112        catch (Exception e) {
113            throw new DocumentException( 
114                "Could not load the DOM Document class: "  + className, e 
115            );
116        }
117    }
118
119    
120    public org.w3c.dom.Document write(Document document) throws DocumentException {
121        if ( document instanceof org.w3c.dom.Document ) {
122            return (org.w3c.dom.Document) document;
123        }
124        resetNamespaceStack();
125        org.w3c.dom.Document domDocument = createDomDocument(document);
126        appendDOMTree(domDocument, domDocument, document.content());
127        namespaceStack.clear();
128        return domDocument;
129    }
130    
131    public org.w3c.dom.Document write(
132        Document document, 
133        org.w3c.dom.DOMImplementation domImplementation
134    ) throws DocumentException {
135        if ( document instanceof org.w3c.dom.Document ) {
136            return (org.w3c.dom.Document) document;
137        }
138        resetNamespaceStack();
139        org.w3c.dom.Document domDocument = createDomDocument(document, domImplementation);
140        appendDOMTree(domDocument, domDocument, document.content());
141        namespaceStack.clear();
142        return domDocument;
143    }
144    
145    protected void appendDOMTree( 
146        org.w3c.dom.Document domDocument, 
147        org.w3c.dom.Node domCurrent,
148        List content
149    ) {
150        int size = content.size();
151        for ( int i = 0; i < size; i++ ) {
152            Object object = content.get(i);
153            if (object instanceof Element) {
154                appendDOMTree( domDocument, domCurrent, (Element) object);
155            }
156            else if ( object instanceof String ) {
157                appendDOMTree( domDocument, domCurrent, (String) object );
158            }
159            else if ( object instanceof Text ) {
160                Text text = (Text) object;
161                appendDOMTree( domDocument, domCurrent, text.getText() );
162            }
163            else if ( object instanceof CDATA ) {
164                appendDOMTree( domDocument, domCurrent, (CDATA) object );
165            }
166            else if ( object instanceof Comment ) {
167                appendDOMTree( domDocument, domCurrent, (Comment) object );
168            }
169            else if ( object instanceof Entity ) {
170                appendDOMTree( domDocument, domCurrent, (Entity) object );
171            }
172            else if ( object instanceof ProcessingInstruction ) {
173                appendDOMTree( domDocument, domCurrent, (ProcessingInstruction) object );
174            }
175        }
176    }
177        
178    protected void appendDOMTree( 
179        org.w3c.dom.Document domDocument, 
180        org.w3c.dom.Node domCurrent,
181        Element element
182    ) {        
183        org.w3c.dom.Element domElement = null;
184        String elementUri = element.getNamespaceURI();
185        if (elementUri != null && elementUri.length() > 0 ) {            
186            domElement = domDocument.createElementNS( elementUri, element.getQualifiedName() );
187        }
188        else {
189            domElement = domDocument.createElement( element.getQualifiedName() );
190        }
191        
192        int stackSize = namespaceStack.size();
193        List declaredNamespaces = element.declaredNamespaces();
194        for ( int i = 0, size = declaredNamespaces.size(); i < size ; i++ ) {
195            Namespace namespace = (Namespace) declaredNamespaces.get(i);
196            if ( isNamespaceDeclaration( namespace ) ) {
197                namespaceStack.push( namespace );     
198                writeNamespace( domElement, namespace );
199            }
200        }
201        
202        // add the attributes
203        for ( int i = 0, size = element.attributeCount(); i < size ; i++ ) {
204            Attribute attribute = (Attribute) element.attribute(i);
205            String uri = attribute.getNamespaceURI();
206            if ( uri != null && uri.length() > 0 ) {
207                //writeNamespace( domElement, attribute.getNamespace() );
208                domElement.setAttributeNS( uri, attribute.getQualifiedName(), attribute.getValue() );
209            }
210            else {
211                domElement.setAttribute( attribute.getName(), attribute.getValue() );
212            }
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( 
226        org.w3c.dom.Document domDocument, 
227        org.w3c.dom.Node domCurrent,
228        CDATA cdata
229    ) {
230        org.w3c.dom.CDATASection domCDATA = 
231            domDocument.createCDATASection(cdata.getText());        
232        domCurrent.appendChild(domCDATA);
233    }
234        
235    protected void appendDOMTree( 
236        org.w3c.dom.Document domDocument, 
237        org.w3c.dom.Node domCurrent,
238        Comment comment
239    ) {
240        org.w3c.dom.Comment domComment = 
241            domDocument.createComment(comment.getText());
242        domCurrent.appendChild(domComment);
243    }
244        
245    protected void appendDOMTree( 
246        org.w3c.dom.Document domDocument, 
247        org.w3c.dom.Node domCurrent,
248        String text
249    ) {
250        org.w3c.dom.Text domText = domDocument.createTextNode(text);
251        domCurrent.appendChild(domText);
252    }
253        
254    protected void appendDOMTree( 
255        org.w3c.dom.Document domDocument, 
256        org.w3c.dom.Node domCurrent,
257        Entity entity
258    ) {
259        org.w3c.dom.EntityReference domEntity = 
260            domDocument.createEntityReference(entity.getName());
261        domCurrent.appendChild(domEntity);
262    }
263        
264    protected void appendDOMTree( 
265        org.w3c.dom.Document domDocument, 
266        org.w3c.dom.Node domCurrent,
267        ProcessingInstruction pi
268    ) {
269        org.w3c.dom.ProcessingInstruction domPI =
270            domDocument.createProcessingInstruction(pi.getTarget(), pi.getText());
271        domCurrent.appendChild(domPI);
272    }
273    
274    /** @return the new local namespace set which may be different from the input
275      * set if a new namespace is added to the set
276      */
277    protected void writeNamespace( 
278        org.w3c.dom.Element domElement, 
279        Namespace namespace
280    ) {
281        String attributeName = attributeNameForNamespace(namespace);
282        //domElement.setAttributeNS("", attributeName, namespace.getURI());
283        domElement.setAttribute(attributeName, namespace.getURI());
284    }
285    
286    protected String attributeNameForNamespace(Namespace namespace) {
287        String xmlns = "xmlns";
288        String prefix = namespace.getPrefix();
289        if ( prefix.length() > 0 ) {
290            return xmlns + ":" + prefix;
291        }
292        return xmlns;
293    }
294    
295    protected org.w3c.dom.Document createDomDocument(
296        Document document
297    ) throws DocumentException {
298        // lets try JAXP first
299        org.w3c.dom.Document answer = createDomDocumentViaJAXP();
300        if ( answer != null ) {
301            return answer;
302        }
303        Class theClass = getDomDocumentClass();
304        try {
305            return (org.w3c.dom.Document) theClass.newInstance();
306        }
307        catch (Exception e) {
308            throw new DocumentException( 
309                "Could not instantiate an instance of DOM Document wtih class: " 
310                + theClass.getName(), e 
311            );
312        }
313    }
314    
315    protected org.w3c.dom.Document createDomDocumentViaJAXP() throws DocumentException {
316        if ( ! SAXHelper.classNameAvailable( "javax.xml.parsers.DocumentBuilderFactory" ) ) {
317            // don't attempt to use JAXP if it is not in the ClassPath
318            return null;
319        }
320        
321        // try use JAXP to load the XMLReader...
322        try {
323            return JAXPHelper.createDocument( false, true );
324        }
325        catch (Throwable e) {
326            if ( ! loggedWarning ) {                    
327                loggedWarning = true;
328                if ( SAXHelper.isVerboseErrorReporting() ) {
329                    // log all exceptions as warnings and carry
330                    // on as we have a default SAX parser we can use
331                    System.out.println( 
332                        "Warning: Caught exception attempting to use JAXP to "
333                         + "create a W3C DOM document" 
334                    );
335                    System.out.println( "Warning: Exception was: " + e );
336                    e.printStackTrace();
337                }
338                else {
339                    System.out.println( 
340                        "Warning: Error occurred using JAXP to create a DOM document." 
341                    );
342                }
343            }
344        }
345        return null;
346    }
347    protected org.w3c.dom.Document createDomDocument(
348        Document document, 
349        org.w3c.dom.DOMImplementation domImplementation
350    ) throws DocumentException {
351        
352        String namespaceURI = null;
353        String qualifiedName = null;
354        org.w3c.dom.DocumentType docType = null;
355        return domImplementation.createDocument( 
356            namespaceURI, qualifiedName, docType 
357        );
358    }
359
360    protected boolean isNamespaceDeclaration( Namespace ns ) {
361        if (ns != null && ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) {
362            String uri = ns.getURI();
363            if ( uri != null && uri.length() > 0 ) {
364                if ( ! namespaceStack.contains( ns ) ) {
365                    return true;
366
367                }
368            }
369        }
370        return false;
371    }
372    
373    protected void resetNamespaceStack() {
374        namespaceStack.clear();
375        namespaceStack.push( Namespace.XML_NAMESPACE );
376    }
377}
378
379
380
381
382/*
383 * Redistribution and use of this software and associated documentation
384 * ("Software"), with or without modification, are permitted provided
385 * that the following conditions are met:
386 *
387 * 1. Redistributions of source code must retain copyright
388 *    statements and notices.  Redistributions must also contain a
389 *    copy of this document.
390 *
391 * 2. Redistributions in binary form must reproduce the
392 *    above copyright notice, this list of conditions and the
393 *    following disclaimer in the documentation and/or other
394 *    materials provided with the distribution.
395 *
396 * 3. The name "DOM4J" must not be used to endorse or promote
397 *    products derived from this Software without prior written
398 *    permission of MetaStuff, Ltd.  For written permission,
399 *    please contact dom4j-info@metastuff.com.
400 *
401 * 4. Products derived from this Software may not be called "DOM4J"
402 *    nor may "DOM4J" appear in their names without prior written
403 *    permission of MetaStuff, Ltd. DOM4J is a registered
404 *    trademark of MetaStuff, Ltd.
405 *
406 * 5. Due credit should be given to the DOM4J Project
407 *    (http://dom4j.org/).
408 *
409 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
410 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
411 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
412 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
413 * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
414 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
415 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
416 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
417 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
418 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
419 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
420 * OF THE POSSIBILITY OF SUCH DAMAGE.
421 *
422 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
423 *
424 * $Id: DOMWriter.java,v 1.5 2001/08/09 13:35:14 jstrachan Exp $
425 */