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