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