1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jelly;
18
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.io.UnsupportedEncodingException;
22 import java.io.Writer;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.dom4j.io.XMLWriter;
27 import org.xml.sax.Attributes;
28 import org.xml.sax.ContentHandler;
29 import org.xml.sax.Locator;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.XMLReader;
32 import org.xml.sax.ext.LexicalHandler;
33 import org.xml.sax.helpers.AttributesImpl;
34 import org.xml.sax.helpers.DefaultHandler;
35
36 /*** <p><code>XMLOutput</code> is used to output XML events
37 * in a SAX-like manner. This also allows pipelining to be done
38 * such as in the <a href="http://xml.apache.org/cocoon/">Cocoon</a> project.</p>
39 *
40 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
41 * @version $Revision: 155420 $
42 */
43
44 public class XMLOutput implements ContentHandler, LexicalHandler {
45
46 protected static final String[] LEXICAL_HANDLER_NAMES =
47 {
48 "http://xml.org/sax/properties/lexical-handler",
49 "http://xml.org/sax/handlers/LexicalHandler" };
50
51 /*** empty attributes */
52 private static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
53
54 /*** The Log to which logging calls will be made. */
55 private static final Log log = LogFactory.getLog(XMLOutput.class);
56
57 /*** the default for escaping of text */
58 private static final boolean DEFAULT_ESCAPE_TEXT = false;
59
60 /*** The SAX ContentHandler that output goes to */
61 private ContentHandler contentHandler;
62
63 /*** The SAX LexicalHandler that output goes to */
64 private LexicalHandler lexicalHandler;
65
66
67 public XMLOutput() {
68 }
69
70 public XMLOutput(ContentHandler contentHandler) {
71 this.contentHandler = contentHandler;
72
73 if (contentHandler instanceof LexicalHandler) {
74 this.lexicalHandler = (LexicalHandler) contentHandler;
75 }
76 }
77
78 public XMLOutput(
79 ContentHandler contentHandler,
80 LexicalHandler lexicalHandler) {
81 this.contentHandler = contentHandler;
82 this.lexicalHandler = lexicalHandler;
83 }
84
85 public String toString() {
86 return super.toString()
87 + "[contentHandler="
88 + contentHandler
89 + ";lexicalHandler="
90 + lexicalHandler
91 + "]";
92 }
93
94 /***
95 * Provides a useful hook that implementations can use to close the
96 * underlying OutputStream or Writer
97 */
98 public void close() throws IOException {
99 }
100
101 public void flush() throws IOException {
102 if( contentHandler instanceof XMLWriter )
103 {
104 ((XMLWriter)contentHandler).flush();
105 }
106 }
107
108
109
110
111 /***
112 * Creates an XMLOutput from an existing SAX XMLReader
113 */
114 public static XMLOutput createXMLOutput(XMLReader xmlReader) {
115 XMLOutput output = new XMLOutput(xmlReader.getContentHandler());
116
117
118 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
119 try {
120 Object value = xmlReader.getProperty(LEXICAL_HANDLER_NAMES[i]);
121 if (value instanceof LexicalHandler) {
122 output.setLexicalHandler((LexicalHandler) value);
123 break;
124 }
125 }
126 catch (Exception e) {
127
128 if (log.isDebugEnabled()) log.debug("error setting lexical handler properties", e);
129 }
130 }
131 return output;
132 }
133
134 /***
135 * Creates a text based XMLOutput which converts all XML events into
136 * text and writes to the underlying Writer.
137 */
138 public static XMLOutput createXMLOutput(Writer writer) {
139 return createXMLOutput(writer, DEFAULT_ESCAPE_TEXT);
140 }
141
142 /***
143 * Creates a text based XMLOutput which converts all XML events into
144 * text and writes to the underlying Writer.
145 *
146 * @param writer is the writer to output to
147 * @param escapeText is whether or not text output will be escaped. This must be true
148 * if the underlying output is XML or could be false if the underlying output is textual.
149 */
150 public static XMLOutput createXMLOutput(Writer writer, boolean escapeText)
151 {
152 XMLWriter xmlWriter = new XMLWriter(writer);
153 xmlWriter.setEscapeText(escapeText);
154 return createXMLOutput(xmlWriter);
155 }
156
157 /***
158 * Creates a text based XMLOutput which converts all XML events into
159 * text and writes to the underlying OutputStream.
160 */
161 public static XMLOutput createXMLOutput(OutputStream out) throws UnsupportedEncodingException {
162 return createXMLOutput(out, DEFAULT_ESCAPE_TEXT);
163 }
164
165 /***
166 * Creates a text based XMLOutput which converts all XML events into
167 * text and writes to the underlying OutputStream.
168 *
169 * @param out is the output stream to write
170 * @param escapeText is whether or not text output will be escaped. This must be true
171 * if the underlying output is XML or could be false if the underlying output is textual.
172 */
173 public static XMLOutput createXMLOutput(OutputStream out, boolean escapeText) throws UnsupportedEncodingException {
174 XMLWriter xmlWriter = new XMLWriter(out);
175 xmlWriter.setEscapeText(escapeText);
176 return createXMLOutput(xmlWriter);
177 }
178
179 /***
180 * returns an XMLOutput object that will discard all
181 * tag-generated XML events. Useful when tag output is not expected
182 * or not significant.
183 *
184 * @return a no-op XMLOutput
185 */
186 public static XMLOutput createDummyXMLOutput() {
187 return new XMLOutput(new DefaultHandler());
188 }
189
190
191
192
193 /***
194 * Outputs the given String as a piece of valid text in the
195 * XML event stream.
196 * Any special XML characters should be properly escaped.
197 */
198 public void write(String text) throws SAXException {
199 char[] ch = text.toCharArray();
200 characters(ch, 0, ch.length);
201 }
202
203 /***
204 * Outputs the given String as a piece of CDATA in the
205 * XML event stream.
206 */
207 public void writeCDATA(String text) throws SAXException {
208 startCDATA();
209 char[] ch = text.toCharArray();
210 characters(ch, 0, ch.length);
211 endCDATA();
212 }
213
214 /***
215 * Outputs a comment to the XML stream
216 */
217 public void writeComment(String text) throws SAXException {
218 char[] ch = text.toCharArray();
219 comment(ch, 0, ch.length);
220 }
221
222 /***
223 * Helper method for outputting a start element event for an element in no namespace
224 */
225 public void startElement(String localName) throws SAXException {
226 startElement("", localName, localName, EMPTY_ATTRIBUTES);
227 }
228
229 /***
230 * Helper method for outputting a start element event for an element in no namespace
231 */
232 public void startElement(String localName, Attributes attributes) throws SAXException {
233 startElement("", localName, localName, attributes);
234 }
235
236 /***
237 * Helper method for outputting an end element event for an element in no namespace
238 */
239 public void endElement(String localName) throws SAXException {
240 endElement("", localName, localName);
241 }
242
243
244
245
246
247 /***
248 * Receive an object for locating the origin of SAX document events.
249 *
250 * <p>SAX parsers are strongly encouraged (though not absolutely
251 * required) to supply a locator: if it does so, it must supply
252 * the locator to the application by invoking this method before
253 * invoking any of the other methods in the ContentHandler
254 * interface.</p>
255 *
256 * <p>The locator allows the application to determine the end
257 * position of any document-related event, even if the parser is
258 * not reporting an error. Typically, the application will
259 * use this information for reporting its own errors (such as
260 * character content that does not match an application's
261 * business rules). The information returned by the locator
262 * is probably not sufficient for use with a search engine.</p>
263 *
264 * <p>Note that the locator will return correct information only
265 * during the invocation of the events in this interface. The
266 * application should not attempt to use it at any other time.</p>
267 *
268 * @param locator An object that can return the location of
269 * any SAX document event.
270 * @see org.xml.sax.Locator
271 */
272 public void setDocumentLocator(Locator locator) {
273 contentHandler.setDocumentLocator(locator);
274 }
275
276 /***
277 * Receive notification of the beginning of a document.
278 *
279 * <p>The SAX parser will invoke this method only once, before any
280 * other event callbacks (except for {@link #setDocumentLocator
281 * setDocumentLocator}).</p>
282 *
283 * @exception org.xml.sax.SAXException Any SAX exception, possibly
284 * wrapping another exception.
285 * @see #endDocument
286 */
287 public void startDocument() throws SAXException {
288 contentHandler.startDocument();
289 }
290
291 /***
292 * Receive notification of the end of a document.
293 *
294 * <p>The SAX parser will invoke this method only once, and it will
295 * be the last method invoked during the parse. The parser shall
296 * not invoke this method until it has either abandoned parsing
297 * (because of an unrecoverable error) or reached the end of
298 * input.</p>
299 *
300 * @exception org.xml.sax.SAXException Any SAX exception, possibly
301 * wrapping another exception.
302 * @see #startDocument
303 */
304 public void endDocument() throws SAXException {
305 contentHandler.endDocument();
306 }
307
308 /***
309 * Begin the scope of a prefix-URI Namespace mapping.
310 *
311 * <p>The information from this event is not necessary for
312 * normal Namespace processing: the SAX XML reader will
313 * automatically replace prefixes for element and attribute
314 * names when the <code>http://xml.org/sax/features/namespaces</code>
315 * feature is <var>true</var> (the default).</p>
316 *
317 * <p>There are cases, however, when applications need to
318 * use prefixes in character data or in attribute values,
319 * where they cannot safely be expanded automatically; the
320 * start/endPrefixMapping event supplies the information
321 * to the application to expand prefixes in those contexts
322 * itself, if necessary.</p>
323 *
324 * <p>Note that start/endPrefixMapping events are not
325 * guaranteed to be properly nested relative to each other:
326 * all startPrefixMapping events will occur immediately before the
327 * corresponding {@link #startElement startElement} event,
328 * and all {@link #endPrefixMapping endPrefixMapping}
329 * events will occur immediately after the corresponding
330 * {@link #endElement endElement} event,
331 * but their order is not otherwise
332 * guaranteed.</p>
333 *
334 * <p>There should never be start/endPrefixMapping events for the
335 * "xml" prefix, since it is predeclared and immutable.</p>
336 *
337 * @param prefix The Namespace prefix being declared.
338 * An empty string is used for the default element namespace,
339 * which has no prefix.
340 * @param uri The Namespace URI the prefix is mapped to.
341 * @exception org.xml.sax.SAXException The client may throw
342 * an exception during processing.
343 * @see #endPrefixMapping
344 * @see #startElement
345 */
346 public void startPrefixMapping(String prefix, String uri) throws SAXException {
347 contentHandler.startPrefixMapping(prefix, uri);
348 }
349
350 /***
351 * End the scope of a prefix-URI mapping.
352 *
353 * <p>See {@link #startPrefixMapping startPrefixMapping} for
354 * details. These events will always occur immediately after the
355 * corresponding {@link #endElement endElement} event, but the order of
356 * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
357 * guaranteed.</p>
358 *
359 * @param prefix The prefix that was being mapped.
360 * This is the empty string when a default mapping scope ends.
361 * @exception org.xml.sax.SAXException The client may throw
362 * an exception during processing.
363 * @see #startPrefixMapping
364 * @see #endElement
365 */
366 public void endPrefixMapping(String prefix) throws SAXException {
367 contentHandler.endPrefixMapping(prefix);
368 }
369
370 /***
371 * Receive notification of the beginning of an element.
372 *
373 * <p>The Parser will invoke this method at the beginning of every
374 * element in the XML document; there will be a corresponding
375 * {@link #endElement endElement} event for every startElement event
376 * (even when the element is empty). All of the element's content will be
377 * reported, in order, before the corresponding endElement
378 * event.</p>
379 *
380 * <p>This event allows up to three name components for each
381 * element:</p>
382 *
383 * <ol>
384 * <li>the Namespace URI;</li>
385 * <li>the local name; and</li>
386 * <li>the qualified (prefixed) name.</li>
387 * </ol>
388 *
389 * <p>Any or all of these may be provided, depending on the
390 * values of the <var>http://xml.org/sax/features/namespaces</var>
391 * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
392 * properties:</p>
393 *
394 * <ul>
395 * <li>the Namespace URI and local name are required when
396 * the namespaces property is <var>true</var> (the default), and are
397 * optional when the namespaces property is <var>false</var> (if one is
398 * specified, both must be);</li>
399 * <li>the qualified name is required when the namespace-prefixes property
400 * is <var>true</var>, and is optional when the namespace-prefixes property
401 * is <var>false</var> (the default).</li>
402 * </ul>
403 *
404 * <p>Note that the attribute list provided will contain only
405 * attributes with explicit values (specified or defaulted):
406 * #IMPLIED attributes will be omitted. The attribute list
407 * will contain attributes used for Namespace declarations
408 * (xmlns* attributes) only if the
409 * <code>http://xml.org/sax/features/namespace-prefixes</code>
410 * property is true (it is false by default, and support for a
411 * true value is optional).</p>
412 *
413 * <p>Like {@link #characters characters()}, attribute values may have
414 * characters that need more than one <code>char</code> value. </p>
415 *
416 * @param uri The Namespace URI, or the empty string if the
417 * element has no Namespace URI or if Namespace
418 * processing is not being performed.
419 * @param localName The local name (without prefix), or the
420 * empty string if Namespace processing is not being
421 * performed.
422 * @param qName The qualified name (with prefix), or the
423 * empty string if qualified names are not available.
424 * @param atts The attributes attached to the element. If
425 * there are no attributes, it shall be an empty
426 * Attributes object.
427 * @exception org.xml.sax.SAXException Any SAX exception, possibly
428 * wrapping another exception.
429 * @see #endElement
430 * @see org.xml.sax.Attributes
431 */
432 public void startElement(
433 String uri,
434 String localName,
435 String qName,
436 Attributes atts)
437 throws SAXException {
438 contentHandler.startElement(uri, localName, qName, atts);
439 }
440
441 /***
442 * Receive notification of the end of an element.
443 *
444 * <p>The SAX parser will invoke this method at the end of every
445 * element in the XML document; there will be a corresponding
446 * {@link #startElement startElement} event for every endElement
447 * event (even when the element is empty).</p>
448 *
449 * <p>For information on the names, see startElement.</p>
450 *
451 * @param uri The Namespace URI, or the empty string if the
452 * element has no Namespace URI or if Namespace
453 * processing is not being performed.
454 * @param localName The local name (without prefix), or the
455 * empty string if Namespace processing is not being
456 * performed.
457 * @param qName The qualified XML 1.0 name (with prefix), or the
458 * empty string if qualified names are not available.
459 * @exception org.xml.sax.SAXException Any SAX exception, possibly
460 * wrapping another exception.
461 */
462 public void endElement(String uri, String localName, String qName)
463 throws SAXException {
464 contentHandler.endElement(uri, localName, qName);
465 }
466
467 /***
468 * Receive notification of character data.
469 *
470 * <p>The Parser will call this method to report each chunk of
471 * character data. SAX parsers may return all contiguous character
472 * data in a single chunk, or they may split it into several
473 * chunks; however, all of the characters in any single event
474 * must come from the same external entity so that the Locator
475 * provides useful information.</p>
476 *
477 * <p>The application must not attempt to read from the array
478 * outside of the specified range.</p>
479 *
480 * <p>Individual characters may consist of more than one Java
481 * <code>char</code> value. There are two important cases where this
482 * happens, because characters can't be represented in just sixteen bits.
483 * In one case, characters are represented in a <em>Surrogate Pair</em>,
484 * using two special Unicode values. Such characters are in the so-called
485 * "Astral Planes", with a code point above U+FFFF. A second case involves
486 * composite characters, such as a base character combining with one or
487 * more accent characters. </p>
488 *
489 * <p> Your code should not assume that algorithms using
490 * <code>char</code>-at-a-time idioms will be working in character
491 * units; in some cases they will split characters. This is relevant
492 * wherever XML permits arbitrary characters, such as attribute values,
493 * processing instruction data, and comments as well as in data reported
494 * from this method. It's also generally relevant whenever Java code
495 * manipulates internationalized text; the issue isn't unique to XML.</p>
496 *
497 * <p>Note that some parsers will report whitespace in element
498 * content using the {@link #ignorableWhitespace ignorableWhitespace}
499 * method rather than this one (validating parsers <em>must</em>
500 * do so).</p>
501 *
502 * @param ch The characters from the XML document.
503 * @param start The start position in the array.
504 * @param length The number of characters to read from the array.
505 * @exception org.xml.sax.SAXException Any SAX exception, possibly
506 * wrapping another exception.
507 * @see #ignorableWhitespace
508 * @see org.xml.sax.Locator
509 */
510 public void characters(char ch[], int start, int length) throws SAXException {
511 contentHandler.characters(ch, start, length);
512 }
513
514 /***
515 * Receive notification of ignorable whitespace in element content.
516 *
517 * <p>Validating Parsers must use this method to report each chunk
518 * of whitespace in element content (see the W3C XML 1.0 recommendation,
519 * section 2.10): non-validating parsers may also use this method
520 * if they are capable of parsing and using content models.</p>
521 *
522 * <p>SAX parsers may return all contiguous whitespace in a single
523 * chunk, or they may split it into several chunks; however, all of
524 * the characters in any single event must come from the same
525 * external entity, so that the Locator provides useful
526 * information.</p>
527 *
528 * <p>The application must not attempt to read from the array
529 * outside of the specified range.</p>
530 *
531 * @param ch The characters from the XML document.
532 * @param start The start position in the array.
533 * @param length The number of characters to read from the array.
534 * @exception org.xml.sax.SAXException Any SAX exception, possibly
535 * wrapping another exception.
536 * @see #characters
537 */
538 public void ignorableWhitespace(char ch[], int start, int length)
539 throws SAXException {
540 contentHandler.ignorableWhitespace(ch, start, length);
541 }
542
543 /***
544 * Receive notification of a processing instruction.
545 *
546 * <p>The Parser will invoke this method once for each processing
547 * instruction found: note that processing instructions may occur
548 * before or after the main document element.</p>
549 *
550 * <p>A SAX parser must never report an XML declaration (XML 1.0,
551 * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
552 * using this method.</p>
553 *
554 * <p>Like {@link #characters characters()}, processing instruction
555 * data may have characters that need more than one <code>char</code>
556 * value. </p>
557 *
558 * @param target The processing instruction target.
559 * @param data The processing instruction data, or null if
560 * none was supplied. The data does not include any
561 * whitespace separating it from the target.
562 * @exception org.xml.sax.SAXException Any SAX exception, possibly
563 * wrapping another exception.
564 */
565 public void processingInstruction(String target, String data)
566 throws SAXException {
567 contentHandler.processingInstruction(target, data);
568 }
569
570 /***
571 * Receive notification of a skipped entity.
572 * This is not called for entity references within markup constructs
573 * such as element start tags or markup declarations. (The XML
574 * recommendation requires reporting skipped external entities.
575 * SAX also reports internal entity expansion/non-expansion, except
576 * within markup constructs.)
577 *
578 * <p>The Parser will invoke this method each time the entity is
579 * skipped. Non-validating processors may skip entities if they
580 * have not seen the declarations (because, for example, the
581 * entity was declared in an external DTD subset). All processors
582 * may skip external entities, depending on the values of the
583 * <code>http://xml.org/sax/features/external-general-entities</code>
584 * and the
585 * <code>http://xml.org/sax/features/external-parameter-entities</code>
586 * properties.</p>
587 *
588 * @param name The name of the skipped entity. If it is a
589 * parameter entity, the name will begin with '%', and if
590 * it is the external DTD subset, it will be the string
591 * "[dtd]".
592 * @exception org.xml.sax.SAXException Any SAX exception, possibly
593 * wrapping another exception.
594 */
595 public void skippedEntity(String name) throws SAXException {
596 contentHandler.skippedEntity(name);
597 }
598
599
600
601
602
603 /***
604 * Report the start of DTD declarations, if any.
605 *
606 * <p>This method is intended to report the beginning of the
607 * DOCTYPE declaration; if the document has no DOCTYPE declaration,
608 * this method will not be invoked.</p>
609 *
610 * <p>All declarations reported through
611 * {@link org.xml.sax.DTDHandler DTDHandler} or
612 * {@link org.xml.sax.ext.DeclHandler DeclHandler} events must appear
613 * between the startDTD and {@link #endDTD endDTD} events.
614 * Declarations are assumed to belong to the internal DTD subset
615 * unless they appear between {@link #startEntity startEntity}
616 * and {@link #endEntity endEntity} events. Comments and
617 * processing instructions from the DTD should also be reported
618 * between the startDTD and endDTD events, in their original
619 * order of (logical) occurrence; they are not required to
620 * appear in their correct locations relative to DTDHandler
621 * or DeclHandler events, however.</p>
622 *
623 * <p>Note that the start/endDTD events will appear within
624 * the start/endDocument events from ContentHandler and
625 * before the first
626 * {@link org.xml.sax.ContentHandler#startElement startElement}
627 * event.</p>
628 *
629 * @param name The document type name.
630 * @param publicId The declared public identifier for the
631 * external DTD subset, or null if none was declared.
632 * @param systemId The declared system identifier for the
633 * external DTD subset, or null if none was declared.
634 * (Note that this is not resolved against the document
635 * base URI.)
636 * @exception SAXException The application may raise an
637 * exception.
638 * @see #endDTD
639 * @see #startEntity
640 */
641 public void startDTD(String name, String publicId, String systemId)
642 throws SAXException {
643 if (lexicalHandler != null) {
644 lexicalHandler.startDTD(name, publicId, systemId);
645 }
646 }
647
648 /***
649 * Report the end of DTD declarations.
650 *
651 * <p>This method is intended to report the end of the
652 * DOCTYPE declaration; if the document has no DOCTYPE declaration,
653 * this method will not be invoked.</p>
654 *
655 * @exception SAXException The application may raise an exception.
656 * @see #startDTD
657 */
658 public void endDTD() throws SAXException {
659 if (lexicalHandler != null) {
660 lexicalHandler.endDTD();
661 }
662 }
663
664 /***
665 * Report the beginning of some internal and external XML entities.
666 *
667 * <p>The reporting of parameter entities (including
668 * the external DTD subset) is optional, and SAX2 drivers that
669 * report LexicalHandler events may not implement it; you can use the
670 * <code
671 * >http://xml.org/sax/features/lexical-handler/parameter-entities</code>
672 * feature to query or control the reporting of parameter entities.</p>
673 *
674 * <p>General entities are reported with their regular names,
675 * parameter entities have '%' prepended to their names, and
676 * the external DTD subset has the pseudo-entity name "[dtd]".</p>
677 *
678 * <p>When a SAX2 driver is providing these events, all other
679 * events must be properly nested within start/end entity
680 * events. There is no additional requirement that events from
681 * {@link org.xml.sax.ext.DeclHandler DeclHandler} or
682 * {@link org.xml.sax.DTDHandler DTDHandler} be properly ordered.</p>
683 *
684 * <p>Note that skipped entities will be reported through the
685 * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
686 * event, which is part of the ContentHandler interface.</p>
687 *
688 * <p>Because of the streaming event model that SAX uses, some
689 * entity boundaries cannot be reported under any
690 * circumstances:</p>
691 *
692 * <ul>
693 * <li>general entities within attribute values</li>
694 * <li>parameter entities within declarations</li>
695 * </ul>
696 *
697 * <p>These will be silently expanded, with no indication of where
698 * the original entity boundaries were.</p>
699 *
700 * <p>Note also that the boundaries of character references (which
701 * are not really entities anyway) are not reported.</p>
702 *
703 * <p>All start/endEntity events must be properly nested.
704 *
705 * @param name The name of the entity. If it is a parameter
706 * entity, the name will begin with '%', and if it is the
707 * external DTD subset, it will be "[dtd]".
708 * @exception SAXException The application may raise an exception.
709 * @see #endEntity
710 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
711 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
712 */
713 public void startEntity(String name) throws SAXException {
714 if (lexicalHandler != null) {
715 lexicalHandler.startEntity(name);
716 }
717 }
718
719 /***
720 * Report the end of an entity.
721 *
722 * @param name The name of the entity that is ending.
723 * @exception SAXException The application may raise an exception.
724 * @see #startEntity
725 */
726 public void endEntity(String name) throws SAXException {
727 if (lexicalHandler != null) {
728 lexicalHandler.endEntity(name);
729 }
730 }
731
732 /***
733 * Report the start of a CDATA section.
734 *
735 * <p>The contents of the CDATA section will be reported through
736 * the regular {@link org.xml.sax.ContentHandler#characters
737 * characters} event; this event is intended only to report
738 * the boundary.</p>
739 *
740 * @exception SAXException The application may raise an exception.
741 * @see #endCDATA
742 */
743 public void startCDATA() throws SAXException {
744 if (lexicalHandler != null) {
745 lexicalHandler.startCDATA();
746 }
747 }
748
749 /***
750 * Report the end of a CDATA section.
751 *
752 * @exception SAXException The application may raise an exception.
753 * @see #startCDATA
754 */
755 public void endCDATA() throws SAXException {
756 if (lexicalHandler != null) {
757 lexicalHandler.endCDATA();
758 }
759 }
760
761 /***
762 * Report an XML comment anywhere in the document.
763 *
764 * <p>This callback will be used for comments inside or outside the
765 * document element, including comments in the external DTD
766 * subset (if read). Comments in the DTD must be properly
767 * nested inside start/endDTD and start/endEntity events (if
768 * used).</p>
769 *
770 * @param ch An array holding the characters in the comment.
771 * @param start The starting position in the array.
772 * @param length The number of characters to use from the array.
773 * @exception SAXException The application may raise an exception.
774 */
775 public void comment(char ch[], int start, int length) throws SAXException {
776 if (lexicalHandler != null) {
777 lexicalHandler.comment(ch, start, length);
778 }
779 }
780
781 /*** Pass data through the pipline.
782 * By default, this call is ignored.
783 * Subclasses are invited to use this as a way for children tags to
784 * pass data to their parent.
785 *
786 * @param object the data to pass
787 * @exception SAXException The application may raise an exception.
788 */
789 public void objectData(Object object) throws SAXException {
790 if(contentHandler instanceof XMLOutput)
791 ((XMLOutput) contentHandler).objectData(object);
792 else {
793 if(object!=null) {
794 String output=object.toString();
795 write(output);
796 } else {
797
798 write("null");
799 }
800 }
801 }
802
803
804
805 /***
806 * @return the SAX ContentHandler to use to pipe SAX events into
807 */
808 public ContentHandler getContentHandler() {
809 return contentHandler;
810 }
811
812 /***
813 * Sets the SAX ContentHandler to pipe SAX events into
814 *
815 * @param contentHandler is the new ContentHandler to use.
816 * This value cannot be null.
817 */
818 public void setContentHandler(ContentHandler contentHandler) {
819 if (contentHandler == null) {
820 throw new NullPointerException("ContentHandler cannot be null!");
821 }
822 this.contentHandler = contentHandler;
823 }
824
825 /***
826 * @return the SAX LexicalHandler to use to pipe SAX events into
827 */
828 public LexicalHandler getLexicalHandler() {
829 return lexicalHandler;
830 }
831
832 /***
833 * Sets the SAX LexicalHandler to pipe SAX events into
834 *
835 * @param lexicalHandler is the new LexicalHandler to use.
836 * This value can be null.
837 */
838 public void setLexicalHandler(LexicalHandler lexicalHandler) {
839 this.lexicalHandler = lexicalHandler;
840 }
841
842
843
844 /***
845 * Factory method to create a new XMLOutput from an XMLWriter
846 */
847 protected static XMLOutput createXMLOutput(final XMLWriter xmlWriter) {
848 XMLOutput answer = new XMLOutput() {
849 public void close() throws IOException {
850 xmlWriter.close();
851 }
852 };
853 answer.setContentHandler(xmlWriter);
854 answer.setLexicalHandler(xmlWriter);
855 return answer;
856 }
857
858 }