KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > output > SAXOutputter


1 /*--
2
3  $Id: SAXOutputter.java,v 1.38 2004/12/11 00:15:24 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 package org.jdom.output;
58
59 import java.io.*;
60 import java.lang.reflect.*;
61 import java.util.*;
62
63 import org.jdom.*;
64 import org.xml.sax.*;
65 import org.xml.sax.ext.*;
66 import org.xml.sax.helpers.*;
67
68 /**
69  * Outputs a JDOM document as a stream of SAX2 events.
70  * <p>
71  * Most ContentHandler callbacks are supported. Both
72  * <code>ignorableWhitespace()</code> and <code>skippedEntity()</code> have not
73  * been implemented. The <code>{@link JDOMLocator}</code> class returned by
74  * <code>{@link #getLocator}</code> exposes the current node being operated
75  * upon.
76  * <p>
77  * At this time, it is not possible to access notations and unparsed entity
78  * references in a DTD from JDOM. Therefore, <code>DTDHandler</code> callbacks
79  * have not been implemented yet.
80  * <p>
81  * The <code>ErrorHandler</code> callbacks have not been implemented, since
82  * these are supposed to be invoked when the document is parsed and at this
83  * point the document exists in memory and is known to have no errors. </p>
84  *
85  * @version $Revision: 1.38 $, $Date: 2004/12/11 00:15:24 $
86  * @author Brett McLaughlin
87  * @author Jason Hunter
88  * @author Fred Trimble
89  * @author Bradley S. Huffman
90  */

91 public class SAXOutputter {
92
93     private static final String JavaDoc CVS_ID =
94       "@(#) $RCSfile: SAXOutputter.java,v $ $Revision: 1.38 $ $Date: 2004/12/11 00:15:24 $ $Name: $";
95
96     /** Shortcut for SAX namespaces core feature */
97     private static final String JavaDoc NAMESPACES_SAX_FEATURE =
98                         "http://xml.org/sax/features/namespaces";
99
100     /** Shortcut for SAX namespace-prefixes core feature */
101     private static final String JavaDoc NS_PREFIXES_SAX_FEATURE =
102                         "http://xml.org/sax/features/namespace-prefixes";
103
104     /** Shortcut for SAX validation core feature */
105     private static final String JavaDoc VALIDATION_SAX_FEATURE =
106                         "http://xml.org/sax/features/validation";
107
108     /** Shortcut for SAX-ext. lexical handler property */
109     private static final String JavaDoc LEXICAL_HANDLER_SAX_PROPERTY =
110                         "http://xml.org/sax/properties/lexical-handler";
111
112     /** Shortcut for SAX-ext. declaration handler property */
113     private static final String JavaDoc DECL_HANDLER_SAX_PROPERTY =
114                         "http://xml.org/sax/properties/declaration-handler";
115
116     /**
117      * Shortcut for SAX-ext. lexical handler alternate property.
118      * Although this property URI is not the one defined by the SAX
119      * "standard", some parsers use it instead of the official one.
120      */

121     private static final String JavaDoc LEXICAL_HANDLER_ALT_PROPERTY =
122                         "http://xml.org/sax/handlers/LexicalHandler";
123
124     /** Shortcut for SAX-ext. declaration handler alternate property */
125     private static final String JavaDoc DECL_HANDLER_ALT_PROPERTY =
126                         "http://xml.org/sax/handlers/DeclHandler";
127
128     /**
129      * Array to map JDOM attribute type (as entry index) to SAX
130      * attribute type names.
131      */

132     private static final String JavaDoc[] attrTypeToNameMap = new String JavaDoc[] {
133         "CDATA", // Attribute.UNDEFINED_ATTRIBUTE, as per SAX 2.0 spec.
134
"CDATA", // Attribute.CDATA_TYPE
135
"ID", // Attribute.ID_TYPE
136
"IDREF", // Attribute.IDREF_TYPE
137
"IDREFS", // Attribute.IDREFS_TYPE
138
"ENTITY", // Attribute.ENTITY_TYPE
139
"ENTITIES", // Attribute.ENTITIES_TYPE
140
"NMTOKEN", // Attribute.NMTOKEN_TYPE
141
"NMTOKENS", // Attribute.NMTOKENS_TYPE
142
"NOTATION", // Attribute.NOTATION_TYPE
143
"NMTOKEN", // Attribute.ENUMERATED_TYPE, as per SAX 2.0 spec.
144
};
145
146     /** registered <code>ContentHandler</code> */
147     private ContentHandler contentHandler;
148
149     /** registered <code>ErrorHandler</code> */
150     private ErrorHandler errorHandler;
151
152     /** registered <code>DTDHandler</code> */
153     private DTDHandler dtdHandler;
154
155     /** registered <code>EntityResolver</code> */
156     private EntityResolver entityResolver;
157
158     /** registered <code>LexicalHandler</code> */
159     private LexicalHandler lexicalHandler;
160
161     /** registered <code>DeclHandler</code> */
162     private DeclHandler declHandler;
163
164     /**
165      * Whether to report attribute namespace declarations as xmlns attributes.
166      * Defaults to <code>false</code> as per SAX specifications.
167      *
168      * @see <a HREF="http://www.megginson.com/SAX/Java/namespaces.html">
169      * SAX namespace specifications</a>
170      */

171     private boolean declareNamespaces = false;
172
173     /**
174      * Whether to report DTD events to DeclHandlers and LexicalHandlers.
175      * Defaults to <code>true</code>.
176      */

177     private boolean reportDtdEvents = true;
178
179     /**
180      * A SAX Locator that points at the JDOM node currently being
181      * outputted.
182      */

183     private JDOMLocator locator = null;
184
185     /**
186      * This will create a <code>SAXOutputter</code> without any
187      * registered handler. The application is then responsible for
188      * registering them using the <code>setXxxHandler()</code> methods.
189      */

190     public SAXOutputter() {
191     }
192
193     /**
194      * This will create a <code>SAXOutputter</code> with the
195      * specified <code>ContentHandler</code>.
196      *
197      * @param contentHandler contains <code>ContentHandler</code>
198      * callback methods
199      */

200     public SAXOutputter(ContentHandler contentHandler) {
201         this(contentHandler, null, null, null, null);
202     }
203
204     /**
205      * This will create a <code>SAXOutputter</code> with the
206      * specified SAX2 handlers. At this time, only <code>ContentHandler</code>
207      * and <code>EntityResolver</code> are supported.
208      *
209      * @param contentHandler contains <code>ContentHandler</code>
210      * callback methods
211      * @param errorHandler contains <code>ErrorHandler</code> callback methods
212      * @param dtdHandler contains <code>DTDHandler</code> callback methods
213      * @param entityResolver contains <code>EntityResolver</code>
214      * callback methods
215      */

216     public SAXOutputter(ContentHandler contentHandler,
217                         ErrorHandler errorHandler,
218                         DTDHandler dtdHandler,
219                         EntityResolver entityResolver) {
220         this(contentHandler, errorHandler, dtdHandler, entityResolver, null);
221     }
222
223     /**
224      * This will create a <code>SAXOutputter</code> with the
225      * specified SAX2 handlers. At this time, only <code>ContentHandler</code>
226      * and <code>EntityResolver</code> are supported.
227      *
228      * @param contentHandler contains <code>ContentHandler</code>
229      * callback methods
230      * @param errorHandler contains <code>ErrorHandler</code> callback methods
231      * @param dtdHandler contains <code>DTDHandler</code> callback methods
232      * @param entityResolver contains <code>EntityResolver</code>
233      * callback methods
234      * @param lexicalHandler contains <code>LexicalHandler</code> callbacks.
235      */

236     public SAXOutputter(ContentHandler contentHandler,
237                         ErrorHandler errorHandler,
238                         DTDHandler dtdHandler,
239                         EntityResolver entityResolver,
240                         LexicalHandler lexicalHandler) {
241         this.contentHandler = contentHandler;
242         this.errorHandler = errorHandler;
243         this.dtdHandler = dtdHandler;
244         this.entityResolver = entityResolver;
245         this.lexicalHandler = lexicalHandler;
246     }
247
248     /**
249      * This will set the <code>ContentHandler</code>.
250      *
251      * @param contentHandler contains <code>ContentHandler</code>
252      * callback methods.
253      */

254     public void setContentHandler(ContentHandler contentHandler) {
255         this.contentHandler = contentHandler;
256     }
257
258     /**
259      * Returns the registered <code>ContentHandler</code>.
260      *
261      * @return the current <code>ContentHandler</code> or
262      * <code>null</code> if none was registered.
263      */

264     public ContentHandler getContentHandler() {
265         return this.contentHandler;
266     }
267
268     /**
269      * This will set the <code>ErrorHandler</code>.
270      *
271      * @param errorHandler contains <code>ErrorHandler</code> callback methods.
272      */

273     public void setErrorHandler(ErrorHandler errorHandler) {
274         this.errorHandler = errorHandler;
275     }
276
277     /**
278      * Return the registered <code>ErrorHandler</code>.
279      *
280      * @return the current <code>ErrorHandler</code> or
281      * <code>null</code> if none was registered.
282      */

283     public ErrorHandler getErrorHandler() {
284         return this.errorHandler;
285     }
286
287     /**
288      * This will set the <code>DTDHandler</code>.
289      *
290      * @param dtdHandler contains <code>DTDHandler</code> callback methods.
291      */

292     public void setDTDHandler(DTDHandler dtdHandler) {
293         this.dtdHandler = dtdHandler;
294     }
295
296     /**
297      * Return the registered <code>DTDHandler</code>.
298      *
299      * @return the current <code>DTDHandler</code> or
300      * <code>null</code> if none was registered.
301      */

302     public DTDHandler getDTDHandler() {
303         return this.dtdHandler;
304     }
305
306     /**
307      * This will set the <code>EntityResolver</code>.
308      *
309      * @param entityResolver contains EntityResolver callback methods.
310      */

311     public void setEntityResolver(EntityResolver entityResolver) {
312         this.entityResolver = entityResolver;
313     }
314
315     /**
316      * Return the registered <code>EntityResolver</code>.
317      *
318      * @return the current <code>EntityResolver</code> or
319      * <code>null</code> if none was registered.
320      */

321     public EntityResolver getEntityResolver() {
322         return this.entityResolver;
323     }
324
325     /**
326      * This will set the <code>LexicalHandler</code>.
327      *
328      * @param lexicalHandler contains lexical callback methods.
329      */

330     public void setLexicalHandler(LexicalHandler lexicalHandler) {
331         this.lexicalHandler = lexicalHandler;
332     }
333
334     /**
335      * Return the registered <code>LexicalHandler</code>.
336      *
337      * @return the current <code>LexicalHandler</code> or
338      * <code>null</code> if none was registered.
339      */

340     public LexicalHandler getLexicalHandler() {
341         return this.lexicalHandler;
342     }
343
344     /**
345      * This will set the <code>DeclHandler</code>.
346      *
347      * @param declHandler contains declaration callback methods.
348      */

349     public void setDeclHandler(DeclHandler declHandler) {
350         this.declHandler = declHandler;
351     }
352
353     /**
354      * Return the registered <code>DeclHandler</code>.
355      *
356      * @return the current <code>DeclHandler</code> or
357      * <code>null</code> if none was registered.
358      */

359     public DeclHandler getDeclHandler() {
360         return this.declHandler;
361     }
362
363     /**
364      * Returns whether attribute namespace declarations shall be reported as
365      * "xmlns" attributes.
366      *
367      * @return whether attribute namespace declarations shall be reported as
368      * "xmlns" attributes.
369      */

370     public boolean getReportNamespaceDeclarations() {
371         return declareNamespaces;
372     }
373
374     /**
375      * This will define whether attribute namespace declarations shall be
376      * reported as "xmlns" attributes. This flag defaults to <code>false</code>
377      * and behaves as the "namespace-prefixes" SAX core feature.
378      *
379      * @param declareNamespaces whether attribute namespace declarations
380      * shall be reported as "xmlns" attributes.
381      */

382     public void setReportNamespaceDeclarations(boolean declareNamespaces) {
383         this.declareNamespaces = declareNamespaces;
384     }
385
386     /**
387      * Returns whether DTD events will be reported.
388      *
389      * @return whether DTD events will be reported
390      */

391     public boolean getReportDTDEvents() {
392         return reportDtdEvents;
393     }
394
395     /**
396      * This will define whether to report DTD events to SAX DeclHandlers
397      * and LexicalHandlers if these handlers are registered and the
398      * document to output includes a DocType declaration.
399      *
400      * @param reportDtdEvents whether to notify DTD events.
401      */

402     public void setReportDTDEvents(boolean reportDtdEvents) {
403         this.reportDtdEvents = reportDtdEvents;
404     }
405
406     /**
407      * This will set the state of a SAX feature.
408      * <p>
409      * All XMLReaders are required to support setting to true and to false.
410      * </p>
411      * <p>
412      * SAXOutputter currently supports the following SAX core features:
413      * <dl>
414      * <dt><code>http://xml.org/sax/features/namespaces</code></dt>
415      * <dd><strong>description:</strong> <code>true</code> indicates
416      * namespace URIs and unprefixed local names for element and
417      * attribute names will be available</dd>
418      * <dd><strong>access:</strong> read/write, but always
419      * <code>true</code>!</dd>
420      * <dt><code>http://xml.org/sax/features/namespace-prefixes</code></dt>
421      * <dd><strong>description:</strong> <code>true</code> indicates
422      * XML 1.0 names (with prefixes) and attributes (including xmlns*
423      * attributes) will be available</dd>
424      * <dd><strong>access:</strong> read/write</dd>
425      * <dt><code>http://xml.org/sax/features/validation</code></dt>
426      * <dd><strong>description:</strong> controls whether SAXOutputter
427      * is reporting DTD-related events; if <code>true</code>, the
428      * DocType internal subset will be parsed to fire DTD events</dd>
429      * <dd><strong>access:</strong> read/write, defaults to
430      * <code>true</code></dd>
431      * </dl>
432      * </p>
433      *
434      * @param name <code>String</code> the feature name, which is a
435      * fully-qualified URI.
436      * @param value <code>boolean</code> the requested state of the
437      * feature (true or false).
438      *
439      * @throws SAXNotRecognizedException when SAXOutputter does not
440      * recognize the feature name.
441      * @throws SAXNotSupportedException when SAXOutputter recognizes
442      * the feature name but cannot set the requested value.
443      */

444     public void setFeature(String JavaDoc name, boolean value)
445                 throws SAXNotRecognizedException, SAXNotSupportedException {
446         if (NS_PREFIXES_SAX_FEATURE.equals(name)) {
447             // Namespace prefix declarations.
448
this.setReportNamespaceDeclarations(value);
449         }
450         else {
451             if (NAMESPACES_SAX_FEATURE.equals(name)) {
452                 if (value != true) {
453                     // Namespaces feature always supported by SAXOutputter.
454
throw new SAXNotSupportedException(name);
455                 }
456                 // Else: true is OK!
457
}
458             else {
459                 if (VALIDATION_SAX_FEATURE.equals(name)) {
460                     // Report DTD events.
461
this.setReportDTDEvents(value);
462                 }
463                 else {
464                     // Not a supported feature.
465
throw new SAXNotRecognizedException(name);
466                 }
467             }
468         }
469     }
470
471     /**
472      * This will look up the value of a SAX feature.
473      *
474      * @param name <code>String</code> the feature name, which is a
475      * fully-qualified URI.
476      * @return <code>boolean</code> the current state of the feature
477      * (true or false).
478      *
479      * @throws SAXNotRecognizedException when SAXOutputter does not
480      * recognize the feature name.
481      * @throws SAXNotSupportedException when SAXOutputter recognizes
482      * the feature name but determine its value at this time.
483      */

484     public boolean getFeature(String JavaDoc name)
485                 throws SAXNotRecognizedException, SAXNotSupportedException {
486         if (NS_PREFIXES_SAX_FEATURE.equals(name)) {
487             // Namespace prefix declarations.
488
return (this.declareNamespaces);
489         }
490         else {
491             if (NAMESPACES_SAX_FEATURE.equals(name)) {
492                 // Namespaces feature always supported by SAXOutputter.
493
return (true);
494             }
495             else {
496                 if (VALIDATION_SAX_FEATURE.equals(name)) {
497                     // Report DTD events.
498
return (this.reportDtdEvents);
499                 }
500                 else {
501                     // Not a supported feature.
502
throw new SAXNotRecognizedException(name);
503                 }
504             }
505         }
506     }
507
508     /**
509      * This will set the value of a SAX property.
510      * This method is also the standard mechanism for setting extended
511      * handlers.
512      * <p>
513      * SAXOutputter currently supports the following SAX properties:
514      * <dl>
515      * <dt><code>http://xml.org/sax/properties/lexical-handler</code></dt>
516      * <dd><strong>data type:</strong>
517      * <code>org.xml.sax.ext.LexicalHandler</code></dd>
518      * <dd><strong>description:</strong> An optional extension handler for
519      * lexical events like comments.</dd>
520      * <dd><strong>access:</strong> read/write</dd>
521      * <dt><code>http://xml.org/sax/properties/declaration-handler</code></dt>
522      * <dd><strong>data type:</strong>
523      * <code>org.xml.sax.ext.DeclHandler</code></dd>
524      * <dd><strong>description:</strong> An optional extension handler for
525      * DTD-related events other than notations and unparsed entities.</dd>
526      * <dd><strong>access:</strong> read/write</dd>
527      * </dl>
528      * </p>
529      *
530      * @param name <code>String</code> the property name, which is a
531      * fully-qualified URI.
532      * @param value <code>Object</code> the requested value for the property.
533      *
534      * @throws SAXNotRecognizedException when SAXOutputter does not recognize
535      * the property name.
536      * @throws SAXNotSupportedException when SAXOutputter recognizes the
537      * property name but cannot set the requested value.
538      */

539     public void setProperty(String JavaDoc name, Object JavaDoc value)
540                 throws SAXNotRecognizedException, SAXNotSupportedException {
541         if ((LEXICAL_HANDLER_SAX_PROPERTY.equals(name)) ||
542             (LEXICAL_HANDLER_ALT_PROPERTY.equals(name))) {
543             this.setLexicalHandler((LexicalHandler)value);
544         }
545         else {
546             if ((DECL_HANDLER_SAX_PROPERTY.equals(name)) ||
547                 (DECL_HANDLER_ALT_PROPERTY.equals(name))) {
548                 this.setDeclHandler((DeclHandler)value);
549             }
550             else {
551                 throw new SAXNotRecognizedException(name);
552             }
553         }
554     }
555
556     /**
557      * This will look up the value of a SAX property.
558      *
559      * @param name <code>String</code> the property name, which is a
560      * fully-qualified URI.
561      * @return <code>Object</code> the current value of the property.
562      *
563      * @throws SAXNotRecognizedException when SAXOutputter does not recognize
564      * the property name.
565      * @throws SAXNotSupportedException when SAXOutputter recognizes the
566      * property name but cannot determine its value at this time.
567      */

568     public Object JavaDoc getProperty(String JavaDoc name)
569                 throws SAXNotRecognizedException, SAXNotSupportedException {
570         if ((LEXICAL_HANDLER_SAX_PROPERTY.equals(name)) ||
571             (LEXICAL_HANDLER_ALT_PROPERTY.equals(name))) {
572             return this.getLexicalHandler();
573         }
574         else {
575             if ((DECL_HANDLER_SAX_PROPERTY.equals(name)) ||
576                 (DECL_HANDLER_ALT_PROPERTY.equals(name))) {
577                 return this.getDeclHandler();
578             }
579             else {
580                 throw new SAXNotRecognizedException(name);
581             }
582         }
583     }
584
585
586     /**
587      * This will output the <code>JDOM Document</code>, firing off the
588      * SAX events that have been registered.
589      *
590      * @param document <code>JDOM Document</code> to output.
591      *
592      * @throws JDOMException if any error occurred.
593      */

594     public void output(Document document) throws JDOMException {
595         if (document == null) {
596             return;
597         }
598
599         // contentHandler.setDocumentLocator()
600
documentLocator(document);
601
602         // contentHandler.startDocument()
603
startDocument();
604
605         // Fire DTD events
606
if (this.reportDtdEvents) {
607            dtdEvents(document);
608         }
609
610         // Handle root element, as well as any root level
611
// processing instructions and comments
612
Iterator i = document.getContent().iterator();
613         while (i.hasNext()) {
614             Object JavaDoc obj = i.next();
615
616             // update locator
617
locator.setNode(obj);
618
619             if (obj instanceof Element) {
620                 // process root element and its content
621
element(document.getRootElement(), new NamespaceStack());
622             }
623             else if (obj instanceof ProcessingInstruction) {
624                 // contentHandler.processingInstruction()
625
processingInstruction((ProcessingInstruction) obj);
626             }
627             else if (obj instanceof Comment) {
628                 // lexicalHandler.comment()
629
comment(((Comment) obj).getText());
630             }
631         }
632
633         // contentHandler.endDocument()
634
endDocument();
635     }
636
637     /**
638      * This will output a list of JDOM nodes as a document, firing
639      * off the SAX events that have been registered.
640      * <p>
641      * <strong>Warning</strong>: This method may output ill-formed XML
642      * documents if the list contains top-level objects that are not
643      * legal at the document level (e.g. Text or CDATA nodes, multiple
644      * Element nodes, etc.). Thus, it should only be used to output
645      * document portions towards ContentHandlers capable of accepting
646      * such ill-formed documents (such as XSLT processors).</p>
647      *
648      * @param nodes <code>List</code> of JDOM nodes to output.
649      *
650      * @throws JDOMException if any error occurred.
651      *
652      * @see #output(org.jdom.Document)
653      */

654     public void output(List nodes) throws JDOMException {
655         if ((nodes == null) || (nodes.size() == 0)) {
656             return;
657         }
658
659         // contentHandler.setDocumentLocator()
660
documentLocator(null);
661
662         // contentHandler.startDocument()
663
startDocument();
664
665         // Process node list.
666
elementContent(nodes, new NamespaceStack());
667
668         // contentHandler.endDocument()
669
endDocument();
670     }
671
672     /**
673      * This will output a single JDOM element as a document, firing
674      * off the SAX events that have been registered.
675      *
676      * @param node the <code>Element</code> node to output.
677      *
678      * @throws JDOMException if any error occurred.
679      */

680     public void output(Element node) throws JDOMException {
681         if (node == null) {
682             return;
683         }
684
685         // contentHandler.setDocumentLocator()
686
documentLocator(null);
687
688         // contentHandler.startDocument()
689
startDocument();
690
691         // Output node.
692
elementContent(node, new NamespaceStack());
693
694         // contentHandler.endDocument()
695
endDocument();
696     }
697
698     /**
699      * This will output a list of JDOM nodes as a fragment of an XML
700      * document, firing off the SAX events that have been registered.
701      * <p>
702      * <strong>Warning</strong>: This method does not call the
703      * {@link ContentHandler#setDocumentLocator},
704      * {@link ContentHandler#startDocument} and
705      * {@link ContentHandler#endDocument} callbacks on the
706      * {@link #setContentHandler ContentHandler}. The user shall
707      * invoke these methods directly prior/after outputting the
708      * document fragments.</p>
709      *
710      * @param nodes <code>List</code> of JDOM nodes to output.
711      *
712      * @throws JDOMException if any error occurred.
713      *
714      * @see #outputFragment(org.jdom.Content)
715      */

716     public void outputFragment(List nodes) throws JDOMException {
717         if ((nodes == null) || (nodes.size() == 0)) {
718             return;
719         }
720
721         // Output node list as a document fragment.
722
elementContent(nodes, new NamespaceStack());
723     }
724
725     /**
726      * This will output a single JDOM nodes as a fragment of an XML
727      * document, firing off the SAX events that have been registered.
728      * <p>
729      * <strong>Warning</strong>: This method does not call the
730      * {@link ContentHandler#setDocumentLocator},
731      * {@link ContentHandler#startDocument} and
732      * {@link ContentHandler#endDocument} callbacks on the
733      * {@link #setContentHandler ContentHandler}. The user shall
734      * invoke these methods directly prior/after outputting the
735      * document fragments.</p>
736      *
737      * @param node the <code>Content</code> node to output.
738      *
739      * @throws JDOMException if any error occurred.
740      *
741      * @see #outputFragment(java.util.List)
742      */

743     public void outputFragment(Content node) throws JDOMException {
744         if (node == null) {
745             return;
746         }
747
748         // Output single node as a document fragment.
749
elementContent(node, new NamespaceStack());
750     }
751
752     /**
753      * This parses a DTD declaration to fire the related events towards
754      * the registered handlers.
755      *
756      * @param document <code>JDOM Document</code> the DocType is to
757      * process.
758      */

759     private void dtdEvents(Document document) throws JDOMException {
760         DocType docType = document.getDocType();
761
762         // Fire DTD-related events only if handlers have been registered.
763
if ((docType != null) &&
764             ((dtdHandler != null) || (declHandler != null))) {
765
766             // Build a dummy XML document that only references the DTD...
767
String JavaDoc dtdDoc = new XMLOutputter().outputString(docType);
768
769             try {
770                 // And parse it to fire DTD events.
771
createDTDParser().parse(new InputSource(
772                                                 new StringReader(dtdDoc)));
773
774                 // We should never reach this point as the document is
775
// ill-formed; it does not have any root element.
776
}
777             catch (SAXParseException e) {
778                 // Expected exception: There's no root element in document.
779
}
780             catch (SAXException e) {
781                 throw new JDOMException("DTD parsing error", e);
782             }
783             catch (IOException e) {
784                 throw new JDOMException("DTD parsing error", e);
785             }
786         }
787     }
788
789     /**
790      * <p>
791      * This method tells you the line of the XML file being parsed.
792      * For an in-memory document, it's meaningless. The location
793      * is only valid for the current parsing lifecycle, but
794      * the document has already been parsed. Therefore, it returns
795      * -1 for both line and column numbers.
796      * </p>
797      *
798      * @param document JDOM <code>Document</code>.
799      */

800     private void documentLocator(Document document) {
801         locator = new JDOMLocator();
802         String JavaDoc publicID = null;
803         String JavaDoc systemID = null;
804
805         if (document != null) {
806             DocType docType = document.getDocType();
807             if (docType != null) {
808                 publicID = docType.getPublicID();
809                 systemID = docType.getSystemID();
810             }
811         }
812         locator.setPublicId(publicID);
813         locator.setSystemId(systemID);
814         locator.setLineNumber(-1);
815         locator.setColumnNumber(-1);
816
817         contentHandler.setDocumentLocator(locator);
818     }
819
820     /**
821      * <p>
822      * This method is always the second method of all callbacks in
823      * all handlers to be invoked (setDocumentLocator is always first).
824      * </p>
825      */

826     private void startDocument() throws JDOMException {
827         try {
828             contentHandler.startDocument();
829         }
830         catch (SAXException se) {
831             throw new JDOMException("Exception in startDocument", se);
832         }
833     }
834
835     /**
836      * <p>
837      * Always the last method of all callbacks in all handlers
838      * to be invoked.
839      * </p>
840      */

841     private void endDocument() throws JDOMException {
842         try {
843             contentHandler.endDocument();
844
845             // reset locator
846
locator = null;
847         }
848         catch (SAXException se) {
849             throw new JDOMException("Exception in endDocument", se);
850         }
851     }
852
853     /**
854      * <p>
855      * This will invoke the <code>ContentHandler.processingInstruction</code>
856      * callback when a processing instruction is encountered.
857      * </p>
858      *
859      * @param pi <code>ProcessingInstruction</code> containing target and data.
860      */

861     private void processingInstruction(ProcessingInstruction pi)
862                            throws JDOMException {
863         if (pi != null) {
864             String JavaDoc target = pi.getTarget();
865             String JavaDoc data = pi.getData();
866             try {
867                 contentHandler.processingInstruction(target, data);
868             }
869             catch (SAXException se) {
870                 throw new JDOMException(
871                     "Exception in processingInstruction", se);
872             }
873         }
874     }
875
876     /**
877      * <p>
878      * This will recursively invoke all of the callbacks for a particular
879      * element.
880      * </p>
881      *
882      * @param element <code>Element</code> used in callbacks.
883      * @param namespaces <code>List</code> stack of Namespaces in scope.
884      */

885     private void element(Element element, NamespaceStack namespaces)
886                            throws JDOMException {
887         // used to check endPrefixMapping
888
int previouslyDeclaredNamespaces = namespaces.size();
889
890         // contentHandler.startPrefixMapping()
891
Attributes nsAtts = startPrefixMapping(element, namespaces);
892
893         // contentHandler.startElement()
894
startElement(element, nsAtts);
895
896         // handle content in the element
897
elementContent(element.getContent(), namespaces);
898
899         // update locator
900
locator.setNode(element);
901
902         // contentHandler.endElement()
903
endElement(element);
904
905         // contentHandler.endPrefixMapping()
906
endPrefixMapping(namespaces, previouslyDeclaredNamespaces);
907     }
908
909     /**
910      * <p>
911      * This will invoke the <code>ContentHandler.startPrefixMapping</code>
912      * callback
913      * when a new namespace is encountered in the <code>Document</code>.
914      * </p>
915      *
916      * @param element <code>Element</code> used in callbacks.
917      * @param namespaces <code>List</code> stack of Namespaces in scope.
918      *
919      * @return <code>Attributes</code> declaring the namespaces local to
920      * <code>element</code> or <code>null</code>.
921      */

922     private Attributes startPrefixMapping(Element element,
923                                           NamespaceStack namespaces)
924                                                    throws JDOMException {
925         AttributesImpl nsAtts = null; // The namespaces as xmlns attributes
926

927         Namespace ns = element.getNamespace();
928         if (ns != Namespace.XML_NAMESPACE) {
929             String JavaDoc prefix = ns.getPrefix();
930             String JavaDoc uri = namespaces.getURI(prefix);
931             if (!ns.getURI().equals(uri)) {
932                 namespaces.push(ns);
933                 nsAtts = this.addNsAttribute(nsAtts, ns);
934                 try {
935                     contentHandler.startPrefixMapping(prefix, ns.getURI());
936                 }
937                 catch (SAXException se) {
938                    throw new JDOMException(
939                        "Exception in startPrefixMapping", se);
940                 }
941             }
942         }
943
944         // Fire additional namespace declarations
945
List additionalNamespaces = element.getAdditionalNamespaces();
946         if (additionalNamespaces != null) {
947             Iterator itr = additionalNamespaces.iterator();
948             while (itr.hasNext()) {
949                 ns = (Namespace)itr.next();
950                 String JavaDoc prefix = ns.getPrefix();
951                 String JavaDoc uri = namespaces.getURI(prefix);
952                 if (!ns.getURI().equals(uri)) {
953                     namespaces.push(ns);
954                     nsAtts = this.addNsAttribute(nsAtts, ns);
955                     try {
956                         contentHandler.startPrefixMapping(prefix, ns.getURI());
957                     }
958                     catch (SAXException se) {
959                         throw new JDOMException(
960                             "Exception in startPrefixMapping", se);
961                     }
962                 }
963             }
964         }
965         return nsAtts;
966     }
967
968     /**
969      * <p>
970      * This will invoke the <code>endPrefixMapping</code> callback in the
971      * <code>ContentHandler</code> when a namespace is goes out of scope
972      * in the <code>Document</code>.
973      * </p>
974      *
975      * @param namespaces <code>List</code> stack of Namespaces in scope.
976      * @param previouslyDeclaredNamespaces number of previously declared
977      * namespaces
978      */

979     private void