0
|
1 /*--
|
|
2
|
|
3 $Id: DOMOutputter.java,v 1.43 2007/11/10 05:29:01 jhunter Exp $
|
|
4
|
|
5 Copyright (C) 2000-2007 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.43 $, $Date: 2007/11/10 05:29:01 $
|
|
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.43 $ $Date: 2007/11/10 05:29:01 $ $Name: jdom_1_1_1 $";
|
|
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 /** Output a DOM with namespaces but just the empty namespace */
|
|
91 private boolean forceNamespaceAware;
|
|
92
|
|
93 /**
|
|
94 * This creates a new DOMOutputter which will attempt to first locate
|
|
95 * a DOM implementation to use via JAXP, and if JAXP does not exist or
|
|
96 * there's a problem, will fall back to the default parser.
|
|
97 */
|
|
98 public DOMOutputter() {
|
|
99 // nothing
|
|
100 }
|
|
101
|
|
102 /**
|
|
103 * This creates a new DOMOutputter using the specified DOMAdapter
|
|
104 * implementation as a way to choose the underlying parser.
|
|
105 *
|
|
106 * @param adapterClass <code>String</code> name of class
|
|
107 * to use for DOM output
|
|
108 */
|
|
109 public DOMOutputter(String adapterClass) {
|
|
110 this.adapterClass = adapterClass;
|
|
111 }
|
|
112
|
|
113 /**
|
|
114 * Controls how NO_NAMESPACE nodes are handeled. If true the outputter
|
|
115 * always creates a namespace aware DOM.
|
|
116 * @param flag
|
|
117 */
|
|
118 public void setForceNamespaceAware(boolean flag) {
|
|
119 this.forceNamespaceAware = flag;
|
|
120 }
|
|
121
|
|
122 /**
|
|
123 * Returns whether DOMs will be constructed with namespaces even when
|
|
124 * the source document has elements all in the empty namespace.
|
|
125 * @return the forceNamespaceAware flag value
|
|
126 */
|
|
127 public boolean getForceNamespaceAware() {
|
|
128 return forceNamespaceAware;
|
|
129 }
|
|
130
|
|
131 /**
|
|
132 * This converts the JDOM <code>Document</code> parameter to a
|
|
133 * DOM Document, returning the DOM version. The DOM implementation
|
|
134 * is the one chosen in the constructor.
|
|
135 *
|
|
136 * @param document <code>Document</code> to output.
|
|
137 * @return an <code>org.w3c.dom.Document</code> version
|
|
138 */
|
|
139 public org.w3c.dom.Document output(Document document)
|
|
140 throws JDOMException {
|
|
141 NamespaceStack namespaces = new NamespaceStack();
|
|
142
|
|
143 org.w3c.dom.Document domDoc = null;
|
|
144 try {
|
|
145 // Assign DOCTYPE during construction
|
|
146 DocType dt = document.getDocType();
|
|
147 domDoc = createDOMDocument(dt);
|
|
148
|
|
149 // Add content
|
|
150 Iterator itr = document.getContent().iterator();
|
|
151 while (itr.hasNext()) {
|
|
152 Object node = itr.next();
|
|
153
|
|
154 if (node instanceof Element) {
|
|
155 Element element = (Element) node;
|
|
156 org.w3c.dom.Element domElement =
|
|
157 output(element, domDoc, namespaces);
|
|
158 // Add the root element, first check for existing root
|
|
159 org.w3c.dom.Element root = domDoc.getDocumentElement();
|
|
160 if (root == null) {
|
|
161 // Normal case
|
|
162 domDoc.appendChild(domElement); // normal case
|
|
163 }
|
|
164 else {
|
|
165 // Xerces 1.3 creates new docs with a <root />
|
|
166 // XXX: Need to address DOCTYPE mismatch still
|
|
167 domDoc.replaceChild(domElement, root);
|
|
168 }
|
|
169 }
|
|
170 else if (node instanceof Comment) {
|
|
171 Comment comment = (Comment) node;
|
|
172 org.w3c.dom.Comment domComment =
|
|
173 domDoc.createComment(comment.getText());
|
|
174 domDoc.appendChild(domComment);
|
|
175 }
|
|
176 else if (node instanceof ProcessingInstruction) {
|
|
177 ProcessingInstruction pi =
|
|
178 (ProcessingInstruction) node;
|
|
179 org.w3c.dom.ProcessingInstruction domPI =
|
|
180 domDoc.createProcessingInstruction(
|
|
181 pi.getTarget(), pi.getData());
|
|
182 domDoc.appendChild(domPI);
|
|
183 }
|
|
184 else if (node instanceof DocType) {
|
|
185 // We already dealt with the DocType above
|
|
186 }
|
|
187 else {
|
|
188 throw new JDOMException(
|
|
189 "Document contained top-level content with type:" +
|
|
190 node.getClass().getName());
|
|
191 }
|
|
192 }
|
|
193 }
|
|
194 catch (Throwable e) {
|
|
195 throw new JDOMException("Exception outputting Document", e);
|
|
196 }
|
|
197
|
|
198 return domDoc;
|
|
199 }
|
|
200
|
|
201 private org.w3c.dom.Document createDOMDocument(DocType dt)
|
|
202 throws JDOMException {
|
|
203 if (adapterClass != null) {
|
|
204 // The user knows that they want to use a particular impl
|
|
205 try {
|
|
206 DOMAdapter adapter =
|
|
207 (DOMAdapter)Class.forName(adapterClass).newInstance();
|
|
208 // System.out.println("using specific " + adapterClass);
|
|
209 return adapter.createDocument(dt);
|
|
210 }
|
|
211 catch (ClassNotFoundException e) {
|
|
212 // e.printStackTrace();
|
|
213 }
|
|
214 catch (IllegalAccessException e) {
|
|
215 // e.printStackTrace();
|
|
216 }
|
|
217 catch (InstantiationException e) {
|
|
218 // e.printStackTrace();
|
|
219 }
|
|
220 }
|
|
221 else {
|
|
222 // Try using JAXP...
|
|
223 try {
|
|
224 DOMAdapter adapter =
|
|
225 (DOMAdapter)Class.forName(
|
|
226 "org.jdom.adapters.JAXPDOMAdapter").newInstance();
|
|
227 // System.out.println("using JAXP");
|
|
228 return adapter.createDocument(dt);
|
|
229 }
|
|
230 catch (ClassNotFoundException e) {
|
|
231 // e.printStackTrace();
|
|
232 }
|
|
233 catch (IllegalAccessException e) {
|
|
234 // e.printStackTrace();
|
|
235 }
|
|
236 catch (InstantiationException e) {
|
|
237 // e.printStackTrace();
|
|
238 }
|
|
239 }
|
|
240
|
|
241 // If no DOM doc yet, try to use a hard coded default
|
|
242 try {
|
|
243 DOMAdapter adapter = (DOMAdapter)
|
|
244 Class.forName(DEFAULT_ADAPTER_CLASS).newInstance();
|
|
245 return adapter.createDocument(dt);
|
|
246 // System.out.println("Using default " +
|
|
247 // DEFAULT_ADAPTER_CLASS);
|
|
248 }
|
|
249 catch (ClassNotFoundException e) {
|
|
250 // e.printStackTrace();
|
|
251 }
|
|
252 catch (IllegalAccessException e) {
|
|
253 // e.printStackTrace();
|
|
254 }
|
|
255 catch (InstantiationException e) {
|
|
256 // e.printStackTrace();
|
|
257 }
|
|
258
|
|
259 throw new JDOMException("No JAXP or default parser available");
|
|
260
|
|
261 }
|
|
262
|
|
263 private org.w3c.dom.Element output(Element element,
|
|
264 org.w3c.dom.Document domDoc,
|
|
265 NamespaceStack namespaces)
|
|
266 throws JDOMException {
|
|
267 try {
|
|
268 int previouslyDeclaredNamespaces = namespaces.size();
|
|
269
|
|
270 org.w3c.dom.Element domElement = null;
|
|
271 if (element.getNamespace() == Namespace.NO_NAMESPACE) {
|
|
272 // No namespace, use createElement
|
|
273 domElement = forceNamespaceAware ?
|
|
274 domDoc.createElementNS(null, element.getQualifiedName())
|
|
275 : domDoc.createElement(element.getQualifiedName()); }
|
|
276 else {
|
|
277 domElement = domDoc.createElementNS(
|
|
278 element.getNamespaceURI(),
|
|
279 element.getQualifiedName());
|
|
280 }
|
|
281
|
|
282 // Add namespace attributes, beginning with the element's own
|
|
283 // Do this only if it's not the XML namespace and it's
|
|
284 // not the NO_NAMESPACE with the prefix "" not yet mapped
|
|
285 // (we do output xmlns="" if the "" prefix was already used
|
|
286 // and we need to reclaim it for the NO_NAMESPACE)
|
|
287 Namespace ns = element.getNamespace();
|
|
288 if (ns != Namespace.XML_NAMESPACE &&
|
|
289 !(ns == Namespace.NO_NAMESPACE &&
|
|
290 namespaces.getURI("") == null)) {
|
|
291 String prefix = ns.getPrefix();
|
|
292 String uri = namespaces.getURI(prefix);
|
|
293 if (!ns.getURI().equals(uri)) { // output a new namespace decl
|
|
294 namespaces.push(ns);
|
|
295 String attrName = getXmlnsTagFor(ns);
|
|
296 domElement.setAttribute(attrName, ns.getURI());
|
|
297 }
|
|
298 }
|
|
299
|
|
300 // Add additional namespaces also
|
|
301 Iterator itr = element.getAdditionalNamespaces().iterator();
|
|
302 while (itr.hasNext()) {
|
|
303 Namespace additional = (Namespace)itr.next();
|
|
304 String prefix = additional.getPrefix();
|
|
305 String uri = namespaces.getURI(prefix);
|
|
306 if (!additional.getURI().equals(uri)) {
|
|
307 String attrName = getXmlnsTagFor(additional);
|
|
308 domElement.setAttribute(attrName, additional.getURI());
|
|
309 namespaces.push(additional);
|
|
310 }
|
|
311 }
|
|
312
|
|
313 // Add attributes to the DOM element
|
|
314 itr = element.getAttributes().iterator();
|
|
315 while (itr.hasNext()) {
|
|
316 Attribute attribute = (Attribute) itr.next();
|
|
317 domElement.setAttributeNode(output(attribute, domDoc));
|
|
318 Namespace ns1 = attribute.getNamespace();
|
|
319 if ((ns1 != Namespace.NO_NAMESPACE) &&
|
|
320 (ns1 != Namespace.XML_NAMESPACE)) {
|
|
321 String prefix = ns1.getPrefix();
|
|
322 String uri = namespaces.getURI(prefix);
|
|
323 if (!ns1.getURI().equals(uri)) { // output a new decl
|
|
324 String attrName = getXmlnsTagFor(ns1);
|
|
325 domElement.setAttribute(attrName, ns1.getURI());
|
|
326 namespaces.push(ns1);
|
|
327 }
|
|
328 }
|
|
329 // Crimson doesn't like setAttributeNS() for non-NS attribs
|
|
330 if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
|
|
331 // No namespace, use setAttribute
|
|
332 if (forceNamespaceAware) {
|
|
333 domElement.setAttributeNS(null,
|
|
334 attribute.getQualifiedName(),
|
|
335 attribute.getValue());
|
|
336 } else {
|
|
337 domElement.setAttribute(attribute.getQualifiedName(),
|
|
338 attribute.getValue());
|
|
339 }
|
|
340 }
|
|
341 else {
|
|
342 domElement.setAttributeNS(attribute.getNamespaceURI(),
|
|
343 attribute.getQualifiedName(),
|
|
344 attribute.getValue());
|
|
345 }
|
|
346 }
|
|
347
|
|
348 // Add content to the DOM element
|
|
349 itr = element.getContent().iterator();
|
|
350 while (itr.hasNext()) {
|
|
351 Object node = itr.next();
|
|
352
|
|
353 if (node instanceof Element) {
|
|
354 Element e = (Element) node;
|
|
355 org.w3c.dom.Element domElt = output(e, domDoc, namespaces);
|
|
356 domElement.appendChild(domElt);
|
|
357 }
|
|
358 else if (node instanceof String) {
|
|
359 String str = (String) node;
|
|
360 org.w3c.dom.Text domText = domDoc.createTextNode(str);
|
|
361 domElement.appendChild(domText);
|
|
362 }
|
|
363 else if (node instanceof CDATA) {
|
|
364 CDATA cdata = (CDATA) node;
|
|
365 org.w3c.dom.CDATASection domCdata =
|
|
366 domDoc.createCDATASection(cdata.getText());
|
|
367 domElement.appendChild(domCdata);
|
|
368 }
|
|
369 else if (node instanceof Text) {
|
|
370 Text text = (Text) node;
|
|
371 org.w3c.dom.Text domText =
|
|
372 domDoc.createTextNode(text.getText());
|
|
373 domElement.appendChild(domText);
|
|
374 }
|
|
375 else if (node instanceof Comment) {
|
|
376 Comment comment = (Comment) node;
|
|
377 org.w3c.dom.Comment domComment =
|
|
378 domDoc.createComment(comment.getText());
|
|
379 domElement.appendChild(domComment);
|
|
380 }
|
|
381 else if (node instanceof ProcessingInstruction) {
|
|
382 ProcessingInstruction pi =
|
|
383 (ProcessingInstruction) node;
|
|
384 org.w3c.dom.ProcessingInstruction domPI =
|
|
385 domDoc.createProcessingInstruction(
|
|
386 pi.getTarget(), pi.getData());
|
|
387 domElement.appendChild(domPI);
|
|
388 }
|
|
389 else if (node instanceof EntityRef) {
|
|
390 EntityRef entity = (EntityRef) node;
|
|
391 org.w3c.dom.EntityReference domEntity =
|
|
392 domDoc.createEntityReference(entity.getName());
|
|
393 domElement.appendChild(domEntity);
|
|
394 }
|
|
395 else {
|
|
396 throw new JDOMException(
|
|
397 "Element contained content with type:" +
|
|
398 node.getClass().getName());
|
|
399 }
|
|
400 }
|
|
401
|
|
402 // Remove declared namespaces from stack
|
|
403 while (namespaces.size() > previouslyDeclaredNamespaces) {
|
|
404 namespaces.pop();
|
|
405 }
|
|
406
|
|
407 return domElement;
|
|
408 }
|
|
409 catch (Exception e) {
|
|
410 throw new JDOMException("Exception outputting Element " +
|
|
411 element.getQualifiedName(), e);
|
|
412 }
|
|
413 }
|
|
414
|
|
415 private org.w3c.dom.Attr output(Attribute attribute,
|
|
416 org.w3c.dom.Document domDoc)
|
|
417 throws JDOMException {
|
|
418 org.w3c.dom.Attr domAttr = null;
|
|
419 try {
|
|
420 if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
|
|
421 // No namespace, use createAttribute
|
|
422 if (forceNamespaceAware) {
|
|
423 domAttr = domDoc.createAttributeNS(null, attribute.getQualifiedName());
|
|
424 } else {
|
|
425 domAttr = domDoc.createAttribute(attribute.getQualifiedName());
|
|
426 }
|
|
427 }
|
|
428 else {
|
|
429 domAttr = domDoc.createAttributeNS(attribute.getNamespaceURI(),
|
|
430 attribute.getQualifiedName());
|
|
431 }
|
|
432 domAttr.setValue(attribute.getValue());
|
|
433 } catch (Exception e) {
|
|
434 throw new JDOMException("Exception outputting Attribute " +
|
|
435 attribute.getQualifiedName(), e);
|
|
436 }
|
|
437 return domAttr;
|
|
438 }
|
|
439
|
|
440 /**
|
|
441 * This will handle adding any <code>{@link Namespace}</code>
|
|
442 * attributes to the DOM tree.
|
|
443 *
|
|
444 * @param ns <code>Namespace</code> to add definition of
|
|
445 */
|
|
446 private static String getXmlnsTagFor(Namespace ns) {
|
|
447 String attrName = "xmlns";
|
|
448 if (!ns.getPrefix().equals("")) {
|
|
449 attrName += ":";
|
|
450 attrName += ns.getPrefix();
|
|
451 }
|
|
452 return attrName;
|
|
453 }
|
|
454 }
|