Source code: org/jdom/output/DOMOutputter.java
1 /*--
2
3 $Id: DOMOutputter.java,v 1.41 2004/09/03 06:03:42 jhunter Exp $
4
5 Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 1. Redistributions of source code must retain the above copyright
13 notice, this list of conditions, and the following disclaimer.
14
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions, and the disclaimer that follows
17 these conditions in the documentation and/or other materials
18 provided with the distribution.
19
20 3. The name "JDOM" must not be used to endorse or promote products
21 derived from this software without prior written permission. For
22 written permission, please contact <request_AT_jdom_DOT_org>.
23
24 4. Products derived from this software may not be called "JDOM", nor
25 may "JDOM" appear in their name, without prior written permission
26 from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28 In addition, we request (but do not require) that you include in the
29 end-user documentation provided with the redistribution and/or in the
30 software itself an acknowledgement equivalent to the following:
31 "This product includes software developed by the
32 JDOM Project (http://www.jdom.org/)."
33 Alternatively, the acknowledgment may be graphical using the logos
34 available at http://www.jdom.org/images/logos.
35
36 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 SUCH DAMAGE.
48
49 This software consists of voluntary contributions made by many
50 individuals on behalf of the JDOM Project and was originally
51 created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52 Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53 on the JDOM Project, please see <http://www.jdom.org/>.
54
55 */
56
57
58 package org.jdom.output;
59
60 import java.util.*;
61
62 import org.jdom.*;
63 import org.jdom.adapters.*;
64
65
66 /**
67 * Outputs a JDOM {@link org.jdom.Document org.jdom.Document} as a DOM {@link
68 * org.w3c.dom.Document org.w3c.dom.Document}.
69 *
70 * @version $Revision: 1.41 $, $Date: 2004/09/03 06:03:42 $
71 * @author Brett McLaughlin
72 * @author Jason Hunter
73 * @author Matthew Merlo
74 * @author Dan Schaffer
75 * @author Yusuf Goolamabbas
76 * @author Bradley S. Huffman
77 */
78 public class DOMOutputter {
79
80 private static final String CVS_ID =
81 "@(#) $RCSfile: DOMOutputter.java,v $ $Revision: 1.41 $ $Date: 2004/09/03 06:03:42 $ $Name: jdom_1_0 $";
82
83 /** Default adapter class */
84 private static final String DEFAULT_ADAPTER_CLASS =
85 "org.jdom.adapters.XercesDOMAdapter";
86
87 /** Adapter to use for interfacing with the DOM implementation */
88 private String adapterClass;
89
90 /**
91 * This creates a new DOMOutputter which will attempt to first locate
92 * a DOM implementation to use via JAXP, and if JAXP does not exist or
93 * there's a problem, will fall back to the default parser.
94 */
95 public DOMOutputter() {
96 // nothing
97 }
98
99 /**
100 * This creates a new DOMOutputter using the specified DOMAdapter
101 * implementation as a way to choose the underlying parser.
102 *
103 * @param adapterClass <code>String</code> name of class
104 * to use for DOM output
105 */
106 public DOMOutputter(String adapterClass) {
107 this.adapterClass = adapterClass;
108 }
109
110
111 /**
112 * This converts the JDOM <code>Document</code> parameter to a
113 * DOM Document, returning the DOM version. The DOM implementation
114 * is the one chosen in the constructor.
115 *
116 * @param document <code>Document</code> to output.
117 * @return an <code>org.w3c.dom.Document</code> version
118 */
119 public org.w3c.dom.Document output(Document document)
120 throws JDOMException {
121 NamespaceStack namespaces = new NamespaceStack();
122
123 org.w3c.dom.Document domDoc = null;
124 try {
125 // Assign DOCTYPE during construction
126 DocType dt = document.getDocType();
127 domDoc = createDOMDocument(dt);
128
129 // Add content
130 Iterator itr = document.getContent().iterator();
131 while (itr.hasNext()) {
132 Object node = itr.next();
133
134 if (node instanceof Element) {
135 Element element = (Element) node;
136 org.w3c.dom.Element domElement =
137 output(element, domDoc, namespaces);
138 // Add the root element, first check for existing root
139 org.w3c.dom.Element root = domDoc.getDocumentElement();
140 if (root == null) {
141 // Normal case
142 domDoc.appendChild(domElement); // normal case
143 }
144 else {
145 // Xerces 1.3 creates new docs with a <root />
146 // XXX: Need to address DOCTYPE mismatch still
147 domDoc.replaceChild(domElement, root);
148 }
149 }
150 else if (node instanceof Comment) {
151 Comment comment = (Comment) node;
152 org.w3c.dom.Comment domComment =
153 domDoc.createComment(comment.getText());
154 domDoc.appendChild(domComment);
155 }
156 else if (node instanceof ProcessingInstruction) {
157 ProcessingInstruction pi =
158 (ProcessingInstruction) node;
159 org.w3c.dom.ProcessingInstruction domPI =
160 domDoc.createProcessingInstruction(
161 pi.getTarget(), pi.getData());
162 domDoc.appendChild(domPI);
163 }
164 else if (node instanceof DocType) {
165 // We already dealt with the DocType above
166 }
167 else {
168 throw new JDOMException(
169 "Document contained top-level content with type:" +
170 node.getClass().getName());
171 }
172 }
173 }
174 catch (Throwable e) {
175 throw new JDOMException("Exception outputting Document", e);
176 }
177
178 return domDoc;
179 }
180
181 private org.w3c.dom.Document createDOMDocument(DocType dt)
182 throws JDOMException {
183 if (adapterClass != null) {
184 // The user knows that they want to use a particular impl
185 try {
186 DOMAdapter adapter =
187 (DOMAdapter)Class.forName(adapterClass).newInstance();
188 // System.out.println("using specific " + adapterClass);
189 return adapter.createDocument(dt);
190 }
191 catch (ClassNotFoundException e) {
192 // e.printStackTrace();
193 }
194 catch (IllegalAccessException e) {
195 // e.printStackTrace();
196 }
197 catch (InstantiationException e) {
198 // e.printStackTrace();
199 }
200 }
201 else {
202 // Try using JAXP...
203 try {
204 DOMAdapter adapter =
205 (DOMAdapter)Class.forName(
206 "org.jdom.adapters.JAXPDOMAdapter").newInstance();
207 // System.out.println("using JAXP");
208 return adapter.createDocument(dt);
209 }
210 catch (ClassNotFoundException e) {
211 // e.printStackTrace();
212 }
213 catch (IllegalAccessException e) {
214 // e.printStackTrace();
215 }
216 catch (InstantiationException e) {
217 // e.printStackTrace();
218 }
219 }
220
221 // If no DOM doc yet, try to use a hard coded default
222 try {
223 DOMAdapter adapter = (DOMAdapter)
224 Class.forName(DEFAULT_ADAPTER_CLASS).newInstance();
225 return adapter.createDocument(dt);
226 // System.out.println("Using default " +
227 // DEFAULT_ADAPTER_CLASS);
228 }
229 catch (ClassNotFoundException e) {
230 // e.printStackTrace();
231 }
232 catch (IllegalAccessException e) {
233 // e.printStackTrace();
234 }
235 catch (InstantiationException e) {
236 // e.printStackTrace();
237 }
238
239 throw new JDOMException("No JAXP or default parser available");
240
241 }
242
243 private org.w3c.dom.Element output(Element element,
244 org.w3c.dom.Document domDoc,
245 NamespaceStack namespaces)
246 throws JDOMException {
247 try {
248 int previouslyDeclaredNamespaces = namespaces.size();
249
250 org.w3c.dom.Element domElement = null;
251 if (element.getNamespace() == Namespace.NO_NAMESPACE) {
252 // No namespace, use createElement
253 domElement = domDoc.createElement(element.getQualifiedName());
254 }
255 else {
256 domElement = domDoc.createElementNS(
257 element.getNamespaceURI(),
258 element.getQualifiedName());
259 }
260
261 // Add namespace attributes, beginning with the element's own
262 // Do this only if it's not the XML namespace and it's
263 // not the NO_NAMESPACE with the prefix "" not yet mapped
264 // (we do output xmlns="" if the "" prefix was already used
265 // and we need to reclaim it for the NO_NAMESPACE)
266 Namespace ns = element.getNamespace();
267 if (ns != Namespace.XML_NAMESPACE &&
268 !(ns == Namespace.NO_NAMESPACE &&
269 namespaces.getURI("") == null)) {
270 String prefix = ns.getPrefix();
271 String uri = namespaces.getURI(prefix);
272 if (!ns.getURI().equals(uri)) { // output a new namespace decl
273 namespaces.push(ns);
274 String attrName = getXmlnsTagFor(ns);
275 domElement.setAttribute(attrName, ns.getURI());
276 }
277 }
278
279 // Add additional namespaces also
280 Iterator itr = element.getAdditionalNamespaces().iterator();
281 while (itr.hasNext()) {
282 Namespace additional = (Namespace)itr.next();
283 String prefix = additional.getPrefix();
284 String uri = namespaces.getURI(prefix);
285 if (!additional.getURI().equals(uri)) {
286 String attrName = getXmlnsTagFor(additional);
287 domElement.setAttribute(attrName, additional.getURI());
288 namespaces.push(additional);
289 }
290 }
291
292 // Add attributes to the DOM element
293 itr = element.getAttributes().iterator();
294 while (itr.hasNext()) {
295 Attribute attribute = (Attribute) itr.next();
296 domElement.setAttributeNode(output(attribute, domDoc));
297 Namespace ns1 = attribute.getNamespace();
298 if ((ns1 != Namespace.NO_NAMESPACE) &&
299 (ns1 != Namespace.XML_NAMESPACE)) {
300 String prefix = ns1.getPrefix();
301 String uri = namespaces.getURI(prefix);
302 if (!ns1.getURI().equals(uri)) { // output a new decl
303 String attrName = getXmlnsTagFor(ns1);
304 domElement.setAttribute(attrName, ns1.getURI());
305 namespaces.push(ns1);
306 }
307 }
308 // Crimson doesn't like setAttributeNS() for non-NS attribs
309 if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
310 // No namespace, use setAttribute
311 domElement.setAttribute(attribute.getQualifiedName(),
312 attribute.getValue());
313 }
314 else {
315 domElement.setAttributeNS(attribute.getNamespaceURI(),
316 attribute.getQualifiedName(),
317 attribute.getValue());
318 }
319 }
320
321 // Add content to the DOM element
322 itr = element.getContent().iterator();
323 while (itr.hasNext()) {
324 Object node = itr.next();
325
326 if (node instanceof Element) {
327 Element e = (Element) node;
328 org.w3c.dom.Element domElt = output(e, domDoc, namespaces);
329 domElement.appendChild(domElt);
330 }
331 else if (node instanceof String) {
332 String str = (String) node;
333 org.w3c.dom.Text domText = domDoc.createTextNode(str);
334 domElement.appendChild(domText);
335 }
336 else if (node instanceof CDATA) {
337 CDATA cdata = (CDATA) node;
338 org.w3c.dom.CDATASection domCdata =
339 domDoc.createCDATASection(cdata.getText());
340 domElement.appendChild(domCdata);
341 }
342 else if (node instanceof Text) {
343 Text text = (Text) node;
344 org.w3c.dom.Text domText =
345 domDoc.createTextNode(text.getText());
346 domElement.appendChild(domText);
347 }
348 else if (node instanceof Comment) {
349 Comment comment = (Comment) node;
350 org.w3c.dom.Comment domComment =
351 domDoc.createComment(comment.getText());
352 domElement.appendChild(domComment);
353 }
354 else if (node instanceof ProcessingInstruction) {
355 ProcessingInstruction pi =
356 (ProcessingInstruction) node;
357 org.w3c.dom.ProcessingInstruction domPI =
358 domDoc.createProcessingInstruction(
359 pi.getTarget(), pi.getData());
360 domElement.appendChild(domPI);
361 }
362 else if (node instanceof EntityRef) {
363 EntityRef entity = (EntityRef) node;
364 org.w3c.dom.EntityReference domEntity =
365 domDoc.createEntityReference(entity.getName());
366 domElement.appendChild(domEntity);
367 }
368 else {
369 throw new JDOMException(
370 "Element contained content with type:" +
371 node.getClass().getName());
372 }
373 }
374
375 // Remove declared namespaces from stack
376 while (namespaces.size() > previouslyDeclaredNamespaces) {
377 namespaces.pop();
378 }
379
380 return domElement;
381 }
382 catch (Exception e) {
383 throw new JDOMException("Exception outputting Element " +
384 element.getQualifiedName(), e);
385 }
386 }
387
388 private org.w3c.dom.Attr output(Attribute attribute,
389 org.w3c.dom.Document domDoc)
390 throws JDOMException {
391 org.w3c.dom.Attr domAttr = null;
392 try {
393 if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
394 // No namespace, use createAttribute
395 domAttr = domDoc.createAttribute(attribute.getQualifiedName());
396 }
397 else {
398 domAttr = domDoc.createAttributeNS(attribute.getNamespaceURI(),
399 attribute.getQualifiedName());
400 }
401 domAttr.setValue(attribute.getValue());
402 } catch (Exception e) {
403 throw new JDOMException("Exception outputting Attribute " +
404 attribute.getQualifiedName(), e);
405 }
406 return domAttr;
407 }
408
409 /**
410 * This will handle adding any <code>{@link Namespace}</code>
411 * attributes to the DOM tree.
412 *
413 * @param ns <code>Namespace</code> to add definition of
414 */
415 private static String getXmlnsTagFor(Namespace ns) {
416 String attrName = "xmlns";
417 if (!ns.getPrefix().equals("")) {
418 attrName += ":";
419 attrName += ns.getPrefix();
420 }
421 return attrName;
422 }
423 }