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