comparison NGSrich_0.5.5/src/org/jdom/output/SAXOutputter.java @ 0:89ad0a9cca52 default tip

Uploaded
author pfrommolt
date Mon, 21 Nov 2011 08:12:19 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:89ad0a9cca52
1 /*--
2
3 $Id: SAXOutputter.java,v 1.40 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 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.40 $, $Date: 2007/11/10 05:29:01 $
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 CVS_ID =
94 "@(#) $RCSfile: SAXOutputter.java,v $ $Revision: 1.40 $ $Date: 2007/11/10 05:29:01 $ $Name: jdom_1_1_1 $";
95
96 /** Shortcut for SAX namespaces core feature */
97 private static final String NAMESPACES_SAX_FEATURE =
98 "http://xml.org/sax/features/namespaces";
99
100 /** Shortcut for SAX namespace-prefixes core feature */
101 private static final String 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 VALIDATION_SAX_FEATURE =
106 "http://xml.org/sax/features/validation";
107
108 /** Shortcut for SAX-ext. lexical handler property */
109 private static final String 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 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 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 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[] attrTypeToNameMap = new String[] {
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 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 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 name, Object 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 getProperty(String 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 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 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 publicID = null;
803 String 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 target = pi.getTarget();
865 String 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 if (locator != null) {
901 locator.setNode(element);
902 }
903
904 // contentHandler.endElement()
905 endElement(element);
906
907 // contentHandler.endPrefixMapping()
908 endPrefixMapping(namespaces, previouslyDeclaredNamespaces);
909 }
910
911 /**
912 * <p>
913 * This will invoke the <code>ContentHandler.startPrefixMapping</code>
914 * callback
915 * when a new namespace is encountered in the <code>Document</code>.
916 * </p>
917 *
918 * @param element <code>Element</code> used in callbacks.
919 * @param namespaces <code>List</code> stack of Namespaces in scope.
920 *
921 * @return <code>Attributes</code> declaring the namespaces local to
922 * <code>element</code> or <code>null</code>.
923 */
924 private Attributes startPrefixMapping(Element element,
925 NamespaceStack namespaces)
926 throws JDOMException {
927 AttributesImpl nsAtts = null; // The namespaces as xmlns attributes
928
929 Namespace ns = element.getNamespace();
930 if (ns != Namespace.XML_NAMESPACE) {
931 String prefix = ns.getPrefix();
932 String uri = namespaces.getURI(prefix);
933 if (!ns.getURI().equals(uri)) {
934 namespaces.push(ns);
935 nsAtts = this.addNsAttribute(nsAtts, ns);
936 try {
937 contentHandler.startPrefixMapping(prefix, ns.getURI());
938 }
939 catch (SAXException se) {
940 throw new JDOMException(
941 "Exception in startPrefixMapping", se);
942 }
943 }
944 }
945
946 // Fire additional namespace declarations
947 List additionalNamespaces = element.getAdditionalNamespaces();
948 if (additionalNamespaces != null) {
949 Iterator itr = additionalNamespaces.iterator();
950 while (itr.hasNext()) {
951 ns = (Namespace)itr.next();
952 String prefix = ns.getPrefix();
953 String uri = namespaces.getURI(prefix);
954 if (!ns.getURI().equals(uri)) {
955 namespaces.push(ns);
956 nsAtts = this.addNsAttribute(nsAtts, ns);
957 try {
958 contentHandler.startPrefixMapping(prefix, ns.getURI());
959 }
960 catch (SAXException se) {
961 throw new JDOMException(
962 "Exception in startPrefixMapping", se);
963 }
964 }
965 }
966 }
967 return nsAtts;
968 }
969
970 /**
971 * <p>
972 * This will invoke the <code>endPrefixMapping</code> callback in the
973 * <code>ContentHandler</code> when a namespace is goes out of scope
974 * in the <code>Document</code>.
975 * </p>
976 *
977 * @param namespaces <code>List</code> stack of Namespaces in scope.
978 * @param previouslyDeclaredNamespaces number of previously declared
979 * namespaces
980 */
981 private void endPrefixMapping(NamespaceStack namespaces,
982 int previouslyDeclaredNamespaces)
983 throws JDOMException {
984 while (namespaces.size() > previouslyDeclaredNamespaces) {
985 String prefix = namespaces.pop();
986 try {
987 contentHandler.endPrefixMapping(prefix);
988 }
989 catch (SAXException se) {
990 throw new JDOMException("Exception in endPrefixMapping", se);
991 }
992 }
993 }
994
995 /**
996 * <p>
997 * This will invoke the <code>startElement</code> callback
998 * in the <code>ContentHandler</code>.
999 * </p>
1000 *
1001 * @param element <code>Element</code> used in callbacks.
1002 * @param nsAtts <code>List</code> of namespaces to declare with
1003 * the element or <code>null</code>.
1004 */
1005 private void startElement(Element element, Attributes nsAtts)
1006 throws JDOMException {
1007 String namespaceURI = element.getNamespaceURI();
1008 String localName = element.getName();
1009 String rawName = element.getQualifiedName();
1010
1011 // Allocate attribute list.
1012 AttributesImpl atts = (nsAtts != null)?
1013 new AttributesImpl(nsAtts): new AttributesImpl();
1014
1015 List attributes = element.getAttributes();
1016 Iterator i = attributes.iterator();
1017 while (i.hasNext()) {
1018 Attribute a = (Attribute) i.next();
1019 atts.addAttribute(a.getNamespaceURI(),
1020 a.getName(),
1021 a.getQualifiedName(),
1022 getAttributeTypeName(a.getAttributeType()),
1023 a.getValue());
1024 }
1025
1026 try {
1027 contentHandler.startElement(namespaceURI, localName, rawName, atts);
1028 }
1029 catch (SAXException se) {
1030 throw new JDOMException("Exception in startElement", se);
1031 }
1032 }
1033
1034 /**
1035 * <p>
1036 * This will invoke the <code>endElement</code> callback
1037 * in the <code>ContentHandler</code>.
1038 * </p>
1039 *
1040 * @param element <code>Element</code> used in callbacks.
1041 */
1042 private void endElement(Element element) throws JDOMException {
1043 String namespaceURI = element.getNamespaceURI();
1044 String localName = element.getName();
1045 String rawName = element.getQualifiedName();
1046
1047 try {
1048 contentHandler.endElement(namespaceURI, localName, rawName);
1049 }
1050 catch (SAXException se) {
1051 throw new JDOMException("Exception in endElement", se);
1052 }
1053 }
1054
1055 /**
1056 * <p>
1057 * This will invoke the callbacks for the content of an element.
1058 * </p>
1059 *
1060 * @param content element content as a <code>List</code> of nodes.
1061 * @param namespaces <code>List</code> stack of Namespaces in scope.
1062 */
1063 private void elementContent(List content, NamespaceStack namespaces)
1064 throws JDOMException {
1065 for (Iterator i=content.iterator(); i.hasNext(); ) {
1066 Object obj = i.next();
1067
1068 if (obj instanceof Content) {
1069 this.elementContent((Content)obj, namespaces);
1070 }
1071 else {
1072 // Not a valid element child. This could happen with
1073 // application-provided lists which may contain non
1074 // JDOM objects.
1075 handleError(new JDOMException(
1076 "Invalid element content: " + obj));
1077 }
1078 }
1079 }
1080
1081 /**
1082 * <p>
1083 * This will invoke the callbacks for the content of an element.
1084 * </p>
1085 *
1086 * @param node a <code>Content</code> node.
1087 * @param namespaces <code>List</code> stack of Namespaces in scope.
1088 */
1089 private void elementContent(Content node, NamespaceStack namespaces)
1090 throws JDOMException {
1091 // update locator
1092 if (locator != null) {
1093 locator.setNode(node);
1094 }
1095
1096 if (node instanceof Element) {
1097 element((Element) node, namespaces);
1098 }
1099 else if (node instanceof CDATA) {
1100 cdata(((CDATA) node).getText());
1101 }
1102 else if (node instanceof Text) {
1103 // contentHandler.characters()
1104 characters(((Text) node).getText());
1105 }
1106 else if (node instanceof ProcessingInstruction) {
1107 // contentHandler.processingInstruction()
1108 processingInstruction((ProcessingInstruction) node);
1109 }
1110 else if (node instanceof Comment) {
1111 // lexicalHandler.comment()
1112 comment(((Comment) node).getText());
1113 }
1114 else if (node instanceof EntityRef) {
1115 // contentHandler.skippedEntity()
1116 entityRef((EntityRef) node);
1117 }
1118 else {
1119 // Not a valid element child. This could happen with
1120 // application-provided lists which may contain non
1121 // JDOM objects.
1122 handleError(new JDOMException("Invalid element content: " + node));
1123 }
1124 }
1125
1126 /**
1127 * <p>
1128 * This will be called for each chunk of CDATA section encountered.
1129 * </p>
1130 *
1131 * @param cdataText all text in the CDATA section, including whitespace.
1132 */
1133 private void cdata(String cdataText) throws JDOMException {
1134 try {
1135 if (lexicalHandler != null) {
1136 lexicalHandler.startCDATA();
1137 characters(cdataText);
1138 lexicalHandler.endCDATA();
1139 }
1140 else {
1141 characters(cdataText);
1142 }
1143 }
1144 catch (SAXException se) {
1145 throw new JDOMException("Exception in CDATA", se);
1146 }
1147 }
1148
1149 /**
1150 * <p>
1151 * This will be called for each chunk of character data encountered.
1152 * </p>
1153 *
1154 * @param elementText all text in an element, including whitespace.
1155 */
1156 private void characters(String elementText) throws JDOMException {
1157 char[] c = elementText.toCharArray();
1158 try {
1159 contentHandler.characters(c, 0, c.length);
1160 }
1161 catch (SAXException se) {
1162 throw new JDOMException("Exception in characters", se);
1163 }
1164 }
1165
1166 /**
1167 * <p>
1168 * This will be called for each chunk of comment data encontered.
1169 * </p>
1170 *
1171 * @param commentText all text in a comment, including whitespace.
1172 */
1173 private void comment(String commentText) throws JDOMException {
1174 if (lexicalHandler != null) {
1175 char[] c = commentText.toCharArray();
1176 try {
1177 lexicalHandler.comment(c, 0, c.length);
1178 } catch (SAXException se) {
1179 throw new JDOMException("Exception in comment", se);
1180 }
1181 }
1182 }
1183
1184 /**
1185 * <p>
1186 * This will invoke the <code>ContentHandler.skippedEntity</code>
1187 * callback when an entity reference is encountered.
1188 * </p>
1189 *
1190 * @param entity <code>EntityRef</code>.
1191 */
1192 private void entityRef(EntityRef entity) throws JDOMException {
1193 if (entity != null) {
1194 try {
1195 // No need to worry about appending a '%' character as
1196 // we do not support parameter entities
1197 contentHandler.skippedEntity(entity.getName());
1198 }
1199 catch (SAXException se) {
1200 throw new JDOMException("Exception in entityRef", se);
1201 }
1202 }
1203 }
1204
1205
1206 /**
1207 * <p>
1208 * Appends a namespace declaration in the form of a xmlns attribute to
1209 * an attribute list, crerating this latter if needed.
1210 * </p>
1211 *
1212 * @param atts <code>AttributeImpl</code> where to add the attribute.
1213 * @param ns <code>Namespace</code> the namespace to declare.
1214 *
1215 * @return <code>AttributeImpl</code> the updated attribute list.
1216 */
1217 private AttributesImpl addNsAttribute(AttributesImpl atts, Namespace ns) {
1218 if (this.declareNamespaces) {
1219 if (atts == null) {
1220 atts = new AttributesImpl();
1221 }
1222
1223 String prefix = ns.getPrefix();
1224 if (prefix.equals("")) {
1225 atts.addAttribute("", // namespace
1226 "", // local name
1227 "xmlns", // qualified name
1228 "CDATA", // type
1229 ns.getURI()); // value
1230 }
1231 else {
1232 atts.addAttribute("", // namespace
1233 "", // local name
1234 "xmlns:" + ns.getPrefix(), // qualified name
1235 "CDATA", // type
1236 ns.getURI()); // value
1237 }
1238 }
1239 return atts;
1240 }
1241
1242 /**
1243 * <p>
1244 * Returns the SAX 2.0 attribute type string from the type of
1245 * a JDOM Attribute.
1246 * </p>
1247 *
1248 * @param type <code>int</code> the type of the JDOM attribute.
1249 *
1250 * @return <code>String</code> the SAX 2.0 attribute type string.
1251 *
1252 * @see org.jdom.Attribute#getAttributeType
1253 * @see org.xml.sax.Attributes#getType
1254 */
1255 private static String getAttributeTypeName(int type) {
1256 if ((type < 0) || (type >= attrTypeToNameMap.length)) {
1257 type = Attribute.UNDECLARED_TYPE;
1258 }
1259 return attrTypeToNameMap[type];
1260 }
1261
1262 /**
1263 * <p>
1264 * Notifies the registered {@link ErrorHandler SAX error handler}
1265 * (if any) of an input processing error. The error handler can
1266 * choose to absorb the error and let the processing continue.
1267 * </p>
1268 *
1269 * @param exception <code>JDOMException</code> containing the
1270 * error information; will be wrapped in a
1271 * {@link SAXParseException} when reported to
1272 * the SAX error handler.
1273 *
1274 * @throws JDOMException if no error handler has been registered
1275 * or if the error handler fired a
1276 * {@link SAXException}.
1277 */
1278 private void handleError(JDOMException exception) throws JDOMException {
1279 if (errorHandler != null) {
1280 try {
1281 errorHandler.error(new SAXParseException(
1282 exception.getMessage(), null, exception));
1283 }
1284 catch (SAXException se) {
1285 if (se.getException() instanceof JDOMException) {
1286 throw (JDOMException)(se.getException());
1287 }
1288 else {
1289 throw new JDOMException(se.getMessage(), se);
1290 }
1291 }
1292 }
1293 else {
1294 throw exception;
1295 }
1296 }
1297
1298 /**
1299 * <p>
1300 * Creates a SAX XMLReader.
1301 * </p>
1302 *
1303 * @return <code>XMLReader</code> a SAX2 parser.
1304 *
1305 * @throws Exception if no parser can be created.
1306 */
1307 protected XMLReader createParser() throws Exception {
1308 XMLReader parser = null;
1309
1310 // Try using JAXP...
1311 // Note we need JAXP 1.1, and if JAXP 1.0 is all that's
1312 // available then the getXMLReader call fails and we skip
1313 // to the hard coded default parser
1314 try {
1315 Class factoryClass =
1316 Class.forName("javax.xml.parsers.SAXParserFactory");
1317
1318 // factory = SAXParserFactory.newInstance();
1319 Method newParserInstance =
1320 factoryClass.getMethod("newInstance", null);
1321 Object factory = newParserInstance.invoke(null, null);
1322
1323 // jaxpParser = factory.newSAXParser();
1324 Method newSAXParser = factoryClass.getMethod("newSAXParser", null);
1325 Object jaxpParser = newSAXParser.invoke(factory, null);
1326
1327 // parser = jaxpParser.getXMLReader();
1328 Class parserClass = jaxpParser.getClass();
1329 Method getXMLReader =
1330 parserClass.getMethod("getXMLReader", null);
1331 parser = (XMLReader)getXMLReader.invoke(jaxpParser, null);
1332 } catch (ClassNotFoundException e) {
1333 //e.printStackTrace();
1334 } catch (InvocationTargetException e) {
1335 //e.printStackTrace();
1336 } catch (NoSuchMethodException e) {
1337 //e.printStackTrace();
1338 } catch (IllegalAccessException e) {
1339 //e.printStackTrace();
1340 }
1341
1342 // Check to see if we got a parser yet, if not, try to use a
1343 // hard coded default
1344 if (parser == null) {
1345 parser = XMLReaderFactory.createXMLReader(
1346 "org.apache.xerces.parsers.SAXParser");
1347 }
1348 return parser;
1349 }
1350
1351 /**
1352 * <p>
1353 * This will create a SAX XMLReader capable of parsing a DTD and
1354 * configure it so that the DTD parsing events are routed to the
1355 * handlers registered onto this SAXOutputter.
1356 * </p>
1357 *
1358 * @return <code>XMLReader</code> a SAX2 parser.
1359 *
1360 * @throws JDOMException if no parser can be created.
1361 */
1362 private XMLReader createDTDParser() throws JDOMException {
1363 XMLReader parser = null;
1364
1365 // Get a parser instance
1366 try
1367 {
1368 parser = createParser();
1369 }
1370 catch (Exception ex1) {
1371 throw new JDOMException("Error in SAX parser allocation", ex1);
1372 }
1373
1374 // Register handlers
1375 if (this.getDTDHandler() != null) {
1376 parser.setDTDHandler(this.getDTDHandler());
1377 }
1378 if (this.getEntityResolver() != null) {
1379 parser.setEntityResolver(this.getEntityResolver());
1380 }
1381 if (this.getLexicalHandler() != null) {
1382 try {
1383 parser.setProperty(LEXICAL_HANDLER_SAX_PROPERTY,
1384 this.getLexicalHandler());
1385 }
1386 catch (SAXException ex1) {
1387 try {
1388 parser.setProperty(LEXICAL_HANDLER_ALT_PROPERTY,
1389 this.getLexicalHandler());
1390 } catch (SAXException ex2) {
1391 // Forget it!
1392 }
1393 }
1394 }
1395 if (this.getDeclHandler() != null) {
1396 try {
1397 parser.setProperty(DECL_HANDLER_SAX_PROPERTY,
1398 this.getDeclHandler());
1399 }
1400 catch (SAXException ex1) {
1401 try {
1402 parser.setProperty(DECL_HANDLER_ALT_PROPERTY,
1403 this.getDeclHandler());
1404 } catch (SAXException ex2) {
1405 // Forget it!
1406 }
1407 }
1408 }
1409
1410 // Absorb errors as much as possible, per Laurent
1411 parser.setErrorHandler(new DefaultHandler());
1412
1413 return parser;
1414 }
1415
1416 /**
1417 * Returns a JDOMLocator object referencing the node currently
1418 * being processed by this outputter. The returned object is a
1419 * snapshot of the location information and can thus safely be
1420 * memorized for later use.
1421 * <p>
1422 * This method allows direct access to the location information
1423 * maintained by SAXOutputter without requiring to implement
1424 * <code>XMLFilter</code>. (In SAX, locators are only available
1425 * though the <code>ContentHandler</code> interface).</p>
1426 * <p>
1427 * Note that location information is only available while
1428 * SAXOutputter is outputting nodes. Hence this method should
1429 * only be used by objects taking part in the output processing
1430 * such as <code>ErrorHandler</code>s.
1431 *
1432 * @return a JDOMLocator object referencing the node currently
1433 * being processed or <code>null</code> if no output
1434 * operation is being performed.
1435 */
1436 public JDOMLocator getLocator() {
1437 return (locator != null)? new JDOMLocator(locator): null;
1438 }
1439 }