001: /*--
002:
003: $Id: DOMOutputter.java,v 1.1 2005/04/27 09:32:42 wittek Exp $
004:
005: Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
006: All rights reserved.
007:
008: Redistribution and use in source and binary forms, with or without
009: modification, are permitted provided that the following conditions
010: are met:
011:
012: 1. Redistributions of source code must retain the above copyright
013: notice, this list of conditions, and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright
016: notice, this list of conditions, and the disclaimer that follows
017: these conditions in the documentation and/or other materials
018: provided with the distribution.
019:
020: 3. The name "JDOM" must not be used to endorse or promote products
021: derived from this software without prior written permission. For
022: written permission, please contact <request_AT_jdom_DOT_org>.
023:
024: 4. Products derived from this software may not be called "JDOM", nor
025: may "JDOM" appear in their name, without prior written permission
026: from the JDOM Project Management <request_AT_jdom_DOT_org>.
027:
028: In addition, we request (but do not require) that you include in the
029: end-user documentation provided with the redistribution and/or in the
030: software itself an acknowledgement equivalent to the following:
031: "This product includes software developed by the
032: JDOM Project (http://www.jdom.org/)."
033: Alternatively, the acknowledgment may be graphical using the logos
034: available at http://www.jdom.org/images/logos.
035:
036: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
040: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: SUCH DAMAGE.
048:
049: This software consists of voluntary contributions made by many
050: individuals on behalf of the JDOM Project and was originally
051: created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
052: Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
053: on the JDOM Project, please see <http://www.jdom.org/>.
054:
055: */
056:
057: package org.jdom.output;
058:
059: import java.util.*;
060:
061: import org.jdom.*;
062: import org.jdom.adapters.*;
063:
064: /**
065: * Outputs a JDOM {@link org.jdom.Document org.jdom.Document} as a DOM {@link
066: * org.w3c.dom.Document org.w3c.dom.Document}.
067: *
068: * @version $Revision: 1.1 $, $Date: 2005/04/27 09:32:42 $
069: * @author Brett McLaughlin
070: * @author Jason Hunter
071: * @author Matthew Merlo
072: * @author Dan Schaffer
073: * @author Yusuf Goolamabbas
074: * @author Bradley S. Huffman
075: */
076: public class DOMOutputter {
077:
078: private static final String CVS_ID = "@(#) $RCSfile: DOMOutputter.java,v $ $Revision: 1.1 $ $Date: 2005/04/27 09:32:42 $ $Name: $";
079:
080: /** Default adapter class */
081: private static final String DEFAULT_ADAPTER_CLASS = "org.jdom.adapters.XercesDOMAdapter";
082:
083: /** Adapter to use for interfacing with the DOM implementation */
084: private String adapterClass;
085:
086: /**
087: * This creates a new DOMOutputter which will attempt to first locate
088: * a DOM implementation to use via JAXP, and if JAXP does not exist or
089: * there's a problem, will fall back to the default parser.
090: */
091: public DOMOutputter() {
092: // nothing
093: }
094:
095: /**
096: * This creates a new DOMOutputter using the specified DOMAdapter
097: * implementation as a way to choose the underlying parser.
098: *
099: * @param adapterClass <code>String</code> name of class
100: * to use for DOM output
101: */
102: public DOMOutputter(String adapterClass) {
103: this .adapterClass = adapterClass;
104: }
105:
106: /**
107: * This converts the JDOM <code>Document</code> parameter to a
108: * DOM Document, returning the DOM version. The DOM implementation
109: * is the one chosen in the constructor.
110: *
111: * @param document <code>Document</code> to output.
112: * @return an <code>org.w3c.dom.Document</code> version
113: */
114: public org.w3c.dom.Document output(Document document)
115: throws JDOMException {
116: NamespaceStack namespaces = new NamespaceStack();
117:
118: org.w3c.dom.Document domDoc = null;
119: try {
120: // Assign DOCTYPE during construction
121: DocType dt = document.getDocType();
122: domDoc = createDOMDocument(dt);
123:
124: // Add content
125: Iterator itr = document.getContent().iterator();
126: while (itr.hasNext()) {
127: Object node = itr.next();
128:
129: if (node instanceof Element) {
130: Element element = (Element) node;
131: org.w3c.dom.Element domElement = output(element,
132: domDoc, namespaces);
133: // Add the root element, first check for existing root
134: org.w3c.dom.Element root = domDoc
135: .getDocumentElement();
136: if (root == null) {
137: // Normal case
138: domDoc.appendChild(domElement); // normal case
139: } else {
140: // Xerces 1.3 creates new docs with a <root />
141: // XXX: Need to address DOCTYPE mismatch still
142: domDoc.replaceChild(domElement, root);
143: }
144: } else if (node instanceof Comment) {
145: Comment comment = (Comment) node;
146: org.w3c.dom.Comment domComment = domDoc
147: .createComment(comment.getText());
148: domDoc.appendChild(domComment);
149: } else if (node instanceof ProcessingInstruction) {
150: ProcessingInstruction pi = (ProcessingInstruction) node;
151: org.w3c.dom.ProcessingInstruction domPI = domDoc
152: .createProcessingInstruction(
153: pi.getTarget(), pi.getData());
154: domDoc.appendChild(domPI);
155: } else if (node instanceof DocType) {
156: // We already dealt with the DocType above
157: } else {
158: throw new JDOMException(
159: "Document contained top-level content with type:"
160: + node.getClass().getName());
161: }
162: }
163: } catch (Throwable e) {
164: throw new JDOMException("Exception outputting Document", e);
165: }
166:
167: return domDoc;
168: }
169:
170: private org.w3c.dom.Document createDOMDocument(DocType dt)
171: throws JDOMException {
172: if (adapterClass != null) {
173: // The user knows that they want to use a particular impl
174: try {
175: DOMAdapter adapter = (DOMAdapter) Class.forName(
176: adapterClass).newInstance();
177: // System.out.println("using specific " + adapterClass);
178: return adapter.createDocument(dt);
179: } catch (ClassNotFoundException e) {
180: // e.printStackTrace();
181: } catch (IllegalAccessException e) {
182: // e.printStackTrace();
183: } catch (InstantiationException e) {
184: // e.printStackTrace();
185: }
186: } else {
187: // Try using JAXP...
188: try {
189: DOMAdapter adapter = (DOMAdapter) Class.forName(
190: "org.jdom.adapters.JAXPDOMAdapter")
191: .newInstance();
192: // System.out.println("using JAXP");
193: return adapter.createDocument(dt);
194: } catch (ClassNotFoundException e) {
195: // e.printStackTrace();
196: } catch (IllegalAccessException e) {
197: // e.printStackTrace();
198: } catch (InstantiationException e) {
199: // e.printStackTrace();
200: }
201: }
202:
203: // If no DOM doc yet, try to use a hard coded default
204: try {
205: DOMAdapter adapter = (DOMAdapter) Class.forName(
206: DEFAULT_ADAPTER_CLASS).newInstance();
207: return adapter.createDocument(dt);
208: // System.out.println("Using default " +
209: // DEFAULT_ADAPTER_CLASS);
210: } catch (ClassNotFoundException e) {
211: // e.printStackTrace();
212: } catch (IllegalAccessException e) {
213: // e.printStackTrace();
214: } catch (InstantiationException e) {
215: // e.printStackTrace();
216: }
217:
218: throw new JDOMException("No JAXP or default parser available");
219:
220: }
221:
222: private org.w3c.dom.Element output(Element element,
223: org.w3c.dom.Document domDoc, NamespaceStack namespaces)
224: throws JDOMException {
225: try {
226: int previouslyDeclaredNamespaces = namespaces.size();
227:
228: org.w3c.dom.Element domElement = null;
229: if (element.getNamespace() == Namespace.NO_NAMESPACE) {
230: // No namespace, use createElement
231: domElement = domDoc.createElement(element
232: .getQualifiedName());
233: } else {
234: domElement = domDoc.createElementNS(element
235: .getNamespaceURI(), element.getQualifiedName());
236: }
237:
238: // Add namespace attributes, beginning with the element's own
239: // Do this only if it's not the XML namespace and it's
240: // not the NO_NAMESPACE with the prefix "" not yet mapped
241: // (we do output xmlns="" if the "" prefix was already used
242: // and we need to reclaim it for the NO_NAMESPACE)
243: Namespace ns = element.getNamespace();
244: if (ns != Namespace.XML_NAMESPACE
245: && !(ns == Namespace.NO_NAMESPACE && namespaces
246: .getURI("") == null)) {
247: String prefix = ns.getPrefix();
248: String uri = namespaces.getURI(prefix);
249: if (!ns.getURI().equals(uri)) { // output a new namespace decl
250: namespaces.push(ns);
251: String attrName = getXmlnsTagFor(ns);
252: domElement.setAttribute(attrName, ns.getURI());
253: }
254: }
255:
256: // Add additional namespaces also
257: Iterator itr = element.getAdditionalNamespaces().iterator();
258: while (itr.hasNext()) {
259: Namespace additional = (Namespace) itr.next();
260: String prefix = additional.getPrefix();
261: String uri = namespaces.getURI(prefix);
262: if (!additional.getURI().equals(uri)) {
263: String attrName = getXmlnsTagFor(additional);
264: domElement.setAttribute(attrName, additional
265: .getURI());
266: namespaces.push(additional);
267: }
268: }
269:
270: // Add attributes to the DOM element
271: itr = element.getAttributes().iterator();
272: while (itr.hasNext()) {
273: Attribute attribute = (Attribute) itr.next();
274: domElement.setAttributeNode(output(attribute, domDoc));
275: Namespace ns1 = attribute.getNamespace();
276: if ((ns1 != Namespace.NO_NAMESPACE)
277: && (ns1 != Namespace.XML_NAMESPACE)) {
278: String prefix = ns1.getPrefix();
279: String uri = namespaces.getURI(prefix);
280: if (!ns1.getURI().equals(uri)) { // output a new decl
281: String attrName = getXmlnsTagFor(ns1);
282: domElement.setAttribute(attrName, ns1.getURI());
283: namespaces.push(ns1);
284: }
285: }
286: // Crimson doesn't like setAttributeNS() for non-NS attribs
287: if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
288: // No namespace, use setAttribute
289: domElement.setAttribute(attribute
290: .getQualifiedName(), attribute.getValue());
291: } else {
292: domElement.setAttributeNS(attribute
293: .getNamespaceURI(), attribute
294: .getQualifiedName(), attribute.getValue());
295: }
296: }
297:
298: // Add content to the DOM element
299: itr = element.getContent().iterator();
300: while (itr.hasNext()) {
301: Object node = itr.next();
302:
303: if (node instanceof Element) {
304: Element e = (Element) node;
305: org.w3c.dom.Element domElt = output(e, domDoc,
306: namespaces);
307: domElement.appendChild(domElt);
308: } else if (node instanceof String) {
309: String str = (String) node;
310: org.w3c.dom.Text domText = domDoc
311: .createTextNode(str);
312: domElement.appendChild(domText);
313: } else if (node instanceof CDATA) {
314: CDATA cdata = (CDATA) node;
315: org.w3c.dom.CDATASection domCdata = domDoc
316: .createCDATASection(cdata.getText());
317: domElement.appendChild(domCdata);
318: } else if (node instanceof Text) {
319: Text text = (Text) node;
320: org.w3c.dom.Text domText = domDoc
321: .createTextNode(text.getText());
322: domElement.appendChild(domText);
323: } else if (node instanceof Comment) {
324: Comment comment = (Comment) node;
325: org.w3c.dom.Comment domComment = domDoc
326: .createComment(comment.getText());
327: domElement.appendChild(domComment);
328: } else if (node instanceof ProcessingInstruction) {
329: ProcessingInstruction pi = (ProcessingInstruction) node;
330: org.w3c.dom.ProcessingInstruction domPI = domDoc
331: .createProcessingInstruction(
332: pi.getTarget(), pi.getData());
333: domElement.appendChild(domPI);
334: } else if (node instanceof EntityRef) {
335: EntityRef entity = (EntityRef) node;
336: org.w3c.dom.EntityReference domEntity = domDoc
337: .createEntityReference(entity.getName());
338: domElement.appendChild(domEntity);
339: } else {
340: throw new JDOMException(
341: "Element contained content with type:"
342: + node.getClass().getName());
343: }
344: }
345:
346: // Remove declared namespaces from stack
347: while (namespaces.size() > previouslyDeclaredNamespaces) {
348: namespaces.pop();
349: }
350:
351: return domElement;
352: } catch (Exception e) {
353: throw new JDOMException("Exception outputting Element "
354: + element.getQualifiedName(), e);
355: }
356: }
357:
358: private org.w3c.dom.Attr output(Attribute attribute,
359: org.w3c.dom.Document domDoc) throws JDOMException {
360: org.w3c.dom.Attr domAttr = null;
361: try {
362: if (attribute.getNamespace() == Namespace.NO_NAMESPACE) {
363: // No namespace, use createAttribute
364: domAttr = domDoc.createAttribute(attribute
365: .getQualifiedName());
366: } else {
367: domAttr = domDoc.createAttributeNS(attribute
368: .getNamespaceURI(), attribute
369: .getQualifiedName());
370: }
371: domAttr.setValue(attribute.getValue());
372: } catch (Exception e) {
373: throw new JDOMException("Exception outputting Attribute "
374: + attribute.getQualifiedName(), e);
375: }
376: return domAttr;
377: }
378:
379: /**
380: * This will handle adding any <code>{@link Namespace}</code>
381: * attributes to the DOM tree.
382: *
383: * @param ns <code>Namespace</code> to add definition of
384: */
385: private static String getXmlnsTagFor(Namespace ns) {
386: String attrName = "xmlns";
387: if (!ns.getPrefix().equals("")) {
388: attrName += ":";
389: attrName += ns.getPrefix();
390: }
391: return attrName;
392: }
393: }
|