KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jelly > XMLOutput


1 /*
2  * Copyright 2002,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.commons.jelly;
18
19 import java.io.IOException JavaDoc;
20 import java.io.OutputStream JavaDoc;
21 import java.io.UnsupportedEncodingException JavaDoc;
22 import java.io.Writer JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.dom4j.io.XMLWriter;
32 import org.xml.sax.Attributes JavaDoc;
33 import org.xml.sax.ContentHandler JavaDoc;
34 import org.xml.sax.Locator JavaDoc;
35 import org.xml.sax.SAXException JavaDoc;
36 import org.xml.sax.XMLReader JavaDoc;
37 import org.xml.sax.ext.LexicalHandler JavaDoc;
38 import org.xml.sax.helpers.AttributesImpl JavaDoc;
39 import org.xml.sax.helpers.DefaultHandler JavaDoc;
40
41 /** <p><code>XMLOutput</code> is used to output XML events
42   * in a SAX-like manner. This also allows pipelining to be done
43   * such as in the <a HREF="http://xml.apache.org/cocoon/">Cocoon</a> project.</p>
44   *
45   * @author <a HREF="mailto:jstrachan@apache.org">James Strachan</a>
46   * @version $Revision: 225599 $
47   */

48
49 public class XMLOutput implements ContentHandler JavaDoc, LexicalHandler JavaDoc {
50
51     protected static final String JavaDoc[] LEXICAL_HANDLER_NAMES =
52      {
53         "http://xml.org/sax/properties/lexical-handler",
54         "http://xml.org/sax/handlers/LexicalHandler" };
55
56     /** Empty attributes. */
57     private static final Attributes JavaDoc EMPTY_ATTRIBUTES = new AttributesImpl JavaDoc();
58
59     /** The Log to which logging calls will be made. */
60     private static final Log log = LogFactory.getLog(XMLOutput.class);
61
62     /** the default for escaping of text. */
63     private static final boolean DEFAULT_ESCAPE_TEXT = false;
64
65     /** The SAX ContentHandler that output goes to. */
66     private ContentHandler JavaDoc contentHandler;
67
68     /** The SAX LexicalHandler that output goes to. */
69     private LexicalHandler JavaDoc lexicalHandler;
70
71     /** Stack of kown namespaces. */
72     private NamespaceStack namespaceStack = new NamespaceStack();
73
74     public XMLOutput() {
75     }
76
77     /** The XML-output will relay the SAX events to the indicated
78      * contentHandler.
79      * @param contentHandler
80      */

81     public XMLOutput(ContentHandler JavaDoc contentHandler) {
82         this.contentHandler = contentHandler;
83         // often classes will implement LexicalHandler as well
84
if (contentHandler instanceof LexicalHandler JavaDoc) {
85             this.lexicalHandler = (LexicalHandler JavaDoc) contentHandler;
86         }
87     }
88
89     /** The XML-output will relay the SAX events to the indicated
90      * content-handler lexical-handler.
91      * @param contentHandler
92      * @param lexicalHandler
93      */

94     public XMLOutput(
95         ContentHandler JavaDoc contentHandler,
96         LexicalHandler JavaDoc lexicalHandler) {
97         this.contentHandler = contentHandler;
98         this.lexicalHandler = lexicalHandler;
99     }
100
101     public String JavaDoc toString() {
102         return super.toString()
103             + "[contentHandler="
104             + contentHandler
105             + ";lexicalHandler="
106             + lexicalHandler
107             + "]";
108     }
109
110     /**
111      * Provides a useful hook that implementations
112      * can use to close the
113      * underlying OutputStream or Writer.
114      *
115      * @throws IOException
116      */

117     public void close() throws IOException JavaDoc {
118     }
119
120     /** Flushes the underlying stream if {@link XMLWriter}
121      * or {@link XMLOutput}.
122      *
123      * @throws IOException
124      */

125     public void flush() throws IOException JavaDoc {
126         if (contentHandler instanceof XMLWriter) {
127             ((XMLWriter)contentHandler).flush();
128         } else if (contentHandler instanceof XMLOutput) {
129             ((XMLOutput)contentHandler).flush();
130         }
131     }
132
133     // Static helper methods
134
//-------------------------------------------------------------------------
135

136     /**
137      * Creates an XMLOutput from an existing SAX XMLReader.
138      */

139     public static XMLOutput createXMLOutput(XMLReader JavaDoc xmlReader) {
140         XMLOutput output = new XMLOutput(xmlReader.getContentHandler());
141
142         // isn't it lovely what we've got to do to find the LexicalHandler... ;-)
143
for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
144             try {
145                 Object JavaDoc value = xmlReader.getProperty(LEXICAL_HANDLER_NAMES[i]);
146                 if (value instanceof LexicalHandler JavaDoc) {
147                     output.setLexicalHandler((LexicalHandler JavaDoc) value);
148                     break;
149                 }
150             } catch (Exception JavaDoc e) {
151                 // ignore any unsupported-operation exceptions
152
if (log.isDebugEnabled()) {
153                     log.debug("error setting lexical handler properties", e);
154                 }
155             }
156         }
157         return output;
158     }
159
160     /**
161      * Creates a text based XMLOutput which converts all XML events into
162      * text and writes to the underlying Writer.
163      */

164     public static XMLOutput createXMLOutput(Writer JavaDoc writer) {
165         return createXMLOutput(writer, DEFAULT_ESCAPE_TEXT);
166     }
167
168     /**
169      * Creates a text based XMLOutput which converts all XML events into
170      * text and writes to the underlying Writer.
171      *
172      * @param writer is the writer to output to
173      * @param escapeText is whether or not text output will be escaped. This must be true
174      * if the underlying output is XML or could be false if the underlying output is textual.
175      */

176     public static XMLOutput createXMLOutput(Writer JavaDoc writer, boolean escapeText) {
177         XMLWriter xmlWriter = new XMLWriter(writer);
178         xmlWriter.setEscapeText(escapeText);
179         return createXMLOutput(xmlWriter);
180     }
181
182     /**
183      * Creates a text based XMLOutput which converts all XML events into
184      * text and writes to the underlying OutputStream.
185      */

186     public static XMLOutput createXMLOutput(OutputStream JavaDoc out) throws UnsupportedEncodingException JavaDoc {
187         return createXMLOutput(out, DEFAULT_ESCAPE_TEXT);
188     }
189
190     /**
191      * Creates a text based XMLOutput which converts all XML events into
192      * text and writes to the underlying OutputStream.
193      *
194      * @param out is the output stream to write
195      * @param escapeText is whether or not text output will be escaped. This must be true
196      * if the underlying output is XML or could be false if the underlying output is textual.
197      * @throws UnsupportedEncodingException if the underlying write could not
198      * be created.
199      */

200     public static XMLOutput createXMLOutput(OutputStream JavaDoc out, boolean escapeText)
201             throws UnsupportedEncodingException JavaDoc {
202         XMLWriter xmlWriter = new XMLWriter(out);
203         xmlWriter.setEscapeText(escapeText);
204         return createXMLOutput(xmlWriter);
205     }
206
207     /**
208      * returns an XMLOutput object that will discard all
209      * tag-generated XML events. Useful when tag output is not expected
210      * or not significant.
211      *
212      * @return a no-op XMLOutput
213      */

214     public static XMLOutput createDummyXMLOutput() {
215         return new XMLOutput(new DefaultHandler JavaDoc());
216     }
217
218     // Extra helper methods provided for tag authors
219
//-------------------------------------------------------------------------
220

221     /**
222      * Outputs the given String as a piece of valid text in the
223      * XML event stream.
224      * Any special XML characters should come out properly escaped.
225      */

226     public void write(String JavaDoc text) throws SAXException JavaDoc {
227         char[] ch = text.toCharArray();
228         characters(ch, 0, ch.length);
229     }
230
231     /**
232      * Outputs the given String as a piece of CDATA in the
233      * XML event stream.
234      */

235     public void writeCDATA(String JavaDoc text) throws SAXException JavaDoc {
236         startCDATA();
237         char[] ch = text.toCharArray();
238         characters(ch, 0, ch.length);
239         endCDATA();
240     }
241
242     /**
243      * Outputs a comment to the XML stream.
244      */

245     public void writeComment(String JavaDoc text) throws SAXException JavaDoc {
246         char[] ch = text.toCharArray();
247         comment(ch, 0, ch.length);
248     }
249
250     /**
251      * Helper method for outputting a start element event
252      * for an element in no namespace.
253      */

254     public void startElement(String JavaDoc localName) throws SAXException JavaDoc {
255         startElement("", localName, localName, EMPTY_ATTRIBUTES);
256     }
257
258     /**
259      * Helper method for outputting a start element event
260      * for an element in no namespace.
261      */

262     public void startElement(String JavaDoc localName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
263         startElement("", localName, localName, attributes);
264     }
265
266     /**
267      * Helper method for outputting an end element event
268      * for an element in no namespace.
269      */

270     public void endElement(String JavaDoc localName) throws SAXException JavaDoc {
271         endElement("", localName, localName);
272     }
273
274
275     // ContentHandler interface
276
//-------------------------------------------------------------------------
277

278     /**
279      * Receive an object for locating the origin of SAX document events.
280      *
281      * <p>SAX parsers are strongly encouraged (though not absolutely
282      * required) to supply a locator: if it does so, it must supply
283      * the locator to the application by invoking this method before
284      * invoking any of the other methods in the ContentHandler
285      * interface.</p>
286      *
287      * <p>The locator allows the application to determine the end
288      * position of any document-related event, even if the parser is
289      * not reporting an error. Typically, the application will
290      * use this information for reporting its own errors (such as
291      * character content that does not match an application's
292      * business rules). The information returned by the locator
293      * is probably not sufficient for use with a search engine.</p>
294      *
295      * <p>Note that the locator will return correct information only
296      * during the invocation of the events in this interface. The
297      * application should not attempt to use it at any other time.</p>
298      *
299      * @param locator An object that can return the location of
300      * any SAX document event.
301      * @see org.xml.sax.Locator
302      */

303     public void setDocumentLocator(Locator JavaDoc locator) {
304         contentHandler.setDocumentLocator(locator);
305     }
306
307     /**
308      * Receive notification of the beginning of a document.
309      *
310      * <p>The SAX parser will invoke this method only once, before any
311      * other event callbacks (except for {@link #setDocumentLocator
312      * setDocumentLocator}).</p>
313      *
314      * @exception org.xml.sax.SAXException Any SAX exception, possibly
315      * wrapping another exception.
316      * @see #endDocument
317      */

318     public void startDocument() throws SAXException JavaDoc {
319         contentHandler.startDocument();
320     }
321
322     /**
323      * Receive notification of the end of a document.
324      *
325      * <p>The SAX parser will invoke this method only once, and it will
326      * be the last method invoked during the parse. The parser shall
327      * not invoke this method until it has either abandoned parsing
328      * (because of an unrecoverable error) or reached the end of
329      * input.</p>
330      *
331      * @exception org.xml.sax.SAXException Any SAX exception, possibly
332      * wrapping another exception.
333      * @see #startDocument
334      */

335     public void endDocument() throws SAXException JavaDoc {
336         contentHandler.endDocument();
337     }
338
339     /**
340      * Begin the scope of a prefix-URI Namespace mapping.
341      *
342      * <p>The information from this event is not necessary for
343      * normal Namespace processing: the SAX XML reader will
344      * automatically replace prefixes for element and attribute
345      * names when the <code>http://xml.org/sax/features/namespaces</code>
346      * feature is <var>true</var> (the default).</p>
347      *
348      * <p>There are cases, however, when applications need to
349      * use prefixes in character data or in attribute values,
350      * where they cannot safely be expanded automatically; the
351      * start/endPrefixMapping event supplies the information
352      * to the application to expand prefixes in those contexts
353      * itself, if necessary.</p>
354      *
355      * <p>Note that start/endPrefixMapping events are not
356      * guaranteed to be properly nested relative to each other:
357      * all startPrefixMapping events will occur immediately before the
358      * corresponding {@link #startElement startElement} event,
359      * and all {@link #endPrefixMapping endPrefixMapping}
360      * events will occur immediately after the corresponding
361      * {@link #endElement endElement} event,
362      * but their order is not otherwise
363      * guaranteed.</p>
364      *
365      * <p>There should never be start/endPrefixMapping events for the
366      * "xml" prefix, since it is predeclared and immutable.</p>
367      *
368      * @param prefix The Namespace prefix being declared.
369      * An empty string is used for the default element namespace,
370      * which has no prefix.
371      * @param uri The Namespace URI the prefix is mapped to.
372      * @exception org.xml.sax.SAXException The client may throw
373      * an exception during processing.
374      * @see #endPrefixMapping
375      * @see #startElement
376      */

377     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
378         namespaceStack.pushNamespace(prefix, uri);
379         // contentHandler.startPrefixMapping(prefix, uri) will be called if needed
380
// in pushNamespace
381
}
382
383     /**
384      * End the scope of a prefix-URI mapping.
385      *
386      * <p>See {@link #startPrefixMapping startPrefixMapping} for
387      * details. These events will always occur immediately after the
388      * corresponding {@link #endElement endElement} event, but the order of
389      * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
390      * guaranteed.</p>
391      *
392      * @param prefix The prefix that was being mapped.
393      * This is the empty string when a default mapping scope ends.
394      * @exception org.xml.sax.SAXException The client may throw
395      * an exception during processing.
396      * @see #startPrefixMapping
397      * @see #endElement
398      */

399     public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
400         // End prefix mapping was already called after endElement
401
// contentHandler.endPrefixMapping(prefix);
402
}
403
404     /**
405      * Receive notification of the beginning of an element.
406      *
407      * <p>The Parser will invoke this method at the beginning of every
408      * element in the XML document; there will be a corresponding
409      * {@link #endElement endElement} event for every startElement event
410      * (even when the element is empty). All of the element's content will be
411      * reported, in order, before the corresponding endElement
412      * event.</p>
413      *
414      * <p>This event allows up to three name components for each
415      * element:</p>
416      *
417      * <ol>
418      * <li>the Namespace URI;</li>
419      * <li>the local name; and</li>
420      * <li>the qualified (prefixed) name.</li>
421      * </ol>
422      *
423      * <p>Any or all of these may be provided, depending on the
424      * values of the <var>http://xml.org/sax/features/namespaces</var>
425      * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
426      * properties:</p>
427      *
428      * <ul>
429      * <li>the Namespace URI and local name are required when
430      * the namespaces property is <var>true</var> (the default), and are
431      * optional when the namespaces property is <var>false</var> (if one is
432      * specified, both must be);</li>
433      * <li>the qualified name is required when the namespace-prefixes property
434      * is <var>true</var>, and is optional when the namespace-prefixes property
435      * is <var>false</var> (the default).</li>
436      * </ul>
437      *
438      * <p>Note that the attribute list provided will contain only
439      * attributes with explicit values (specified or defaulted):
440      * #IMPLIED attributes will be omitted. The attribute list
441      * will contain attributes used for Namespace declarations
442      * (xmlns* attributes) only if the
443      * <code>http://xml.org/sax/features/namespace-prefixes</code>
444      * property is true (it is false by default, and support for a
445      * true value is optional).</p>
446      *
447      * <p>Like {@link #characters characters()}, attribute values may have
448      * characters that need more than one <code>char</code> value. </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 name (with prefix), or the
457      * empty string if qualified names are not available.
458      * @param atts The attributes attached to the element. If
459      * there are no attributes, it shall be an empty
460      * Attributes object.
461      * @exception org.xml.sax.SAXException Any SAX exception, possibly
462      * wrapping another exception.
463      * @see #endElement
464      * @see org.xml.sax.Attributes
465      */

466     public void startElement(
467         String JavaDoc uri,
468         String JavaDoc localName,
469         String JavaDoc qName,
470         Attributes JavaDoc atts)
471         throws SAXException JavaDoc {
472
473         int idx = qName.indexOf(':');
474         String JavaDoc attNsPrefix = "";
475         if (idx >= 0) {
476             attNsPrefix = qName.substring(0, idx);
477         }
478         namespaceStack.pushNamespace(attNsPrefix, uri);
479         for (int i = 0; i < atts.getLength(); i++) {
480             String JavaDoc attQName = atts.getQName(i);
481             // An attribute only has an namespace if has a prefix
482
// If not, stays in namespace of containing node
483
idx = attQName.indexOf(':');
484             if (idx >= 0) {
485                 attNsPrefix = attQName.substring(0, idx);
486                 String JavaDoc attUri = atts.getURI(i);
487                 namespaceStack.pushNamespace(attNsPrefix, attUri);
488             }
489         }
490
491         contentHandler.startElement(uri, localName, qName, atts);
492         // Inform namespaceStack of a new depth
493
namespaceStack.increaseLevel();
494     }
495
496     /**
497      * Receive notification of the end of an element.
498      *
499      * <p>The SAX parser will invoke this method at the end of every
500      * element in the XML document; there will be a corresponding
501      * {@link #startElement startElement} event for every endElement
502      * event (even when the element is empty).</p>
503      *
504      * <p>For information on the names, see startElement.</p>
505      *
506      * @param uri The Namespace URI, or the empty string if the
507      * element has no Namespace URI or if Namespace
508      * processing is not being performed.
509      * @param localName The local name (without prefix), or the
510      * empty string if Namespace processing is not being
511      * performed.
512      * @param qName The qualified XML 1.0 name (with prefix), or the
513      * empty string if qualified names are not available.
514      * @exception org.xml.sax.SAXException Any SAX exception, possibly
515      * wrapping another exception.
516      */

517     public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
518         throws SAXException JavaDoc {
519         contentHandler.endElement(uri, localName, qName);
520         // Inform namespaceStack to return to previous depth
521
namespaceStack.decreaseLevel();
522         namespaceStack.popNamespaces();
523     }
524
525     /**
526      * Receive notification of character data.
527      *
528      * <p>The Parser will call this method to report each chunk of
529      * character data. SAX parsers may return all contiguous character
530      * data in a single chunk, or they may split it into several
531      * chunks; however, all of the characters in any single event
532      * must come from the same external entity so that the Locator
533      * provides useful information.</p>
534      *
535      * <p>The application must not attempt to read from the array
536      * outside of the specified range.</p>
537      *
538      * <p>Individual characters may consist of more than one Java
539      * <code>char</code> value. There are two important cases where this
540      * happens, because characters can't be represented in just sixteen bits.
541      * In one case, characters are represented in a <em>Surrogate Pair</em>,
542      * using two special Unicode values. Such characters are in the so-called
543      * "Astral Planes", with a code point above U+FFFF. A second case involves
544      * composite characters, such as a base character combining with one or
545      * more accent characters. </p>
546      *
547      * <p> Your code should not assume that algorithms using
548      * <code>char</code>-at-a-time idioms will be working in character
549      * units; in some cases they will split characters. This is relevant
550      * wherever XML permits arbitrary characters, such as attribute values,
551      * processing instruction data, and comments as well as in data reported
552      * from this method. It's also generally relevant whenever Java code
553      * manipulates internationalized text; the issue isn't unique to XML.</p>
554      *
555      * <p>Note that some parsers will report whitespace in element
556      * content using the {@link #ignorableWhitespace ignorableWhitespace}
557      * method rather than this one (validating parsers <em>must</em>
558      * do so).</p>
559      *
560      * @param ch The characters from the XML document.
561      * @param start The start position in the array.
562      * @param length The number of characters to read from the array.
563      * @exception org.xml.sax.SAXException Any SAX exception, possibly
564      * wrapping another exception.
565      * @see #ignorableWhitespace
566      * @see org.xml.sax.Locator
567      */

568     public void characters(char[] ch, int start, int length) throws SAXException JavaDoc {
569         contentHandler.characters(ch, start, length);
570     }
571
572     /**
573      * Receive notification of ignorable whitespace in element content.
574      *
575      * <p>Validating Parsers must use this method to report each chunk
576      * of whitespace in element content (see the W3C XML 1.0 recommendation,
577      * section 2.10): non-validating parsers may also use this method
578      * if they are capable of parsing and using content models.</p>
579      *
580      * <p>SAX parsers may return all contiguous whitespace in a single
581      * chunk, or they may split it into several chunks; however, all of
582      * the characters in any single event must come from the same
583      * external entity, so that the Locator provides useful
584      * information.</p>
585      *
586      * <p>The application must not attempt to read from the array
587      * outside of the specified range.</p>
588      *
589      * @param ch The characters from the XML document.
590      * @param start The start position in the array.
591      * @param length The number of characters to read from the array.
592      * @exception org.xml.sax.SAXException Any SAX exception, possibly
593      * wrapping another exception.
594      * @see #characters
595      */

596     public void ignorableWhitespace(char[] ch, int start, int length)
597         throws SAXException JavaDoc {
598         contentHandler.ignorableWhitespace(ch, start, length);
599     }
600
601     /**
602      * Receive notification of a processing instruction.
603      *
604      * <p>The Parser will invoke this method once for each processing
605      * instruction found: note that processing instructions may occur
606      * before or after the main document element.</p>
607      *
608      * <p>A SAX parser must never report an XML declaration (XML 1.0,
609      * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
610      * using this method.</p>
611      *
612      * <p>Like {@link #characters characters()}, processing instruction
613      * data may have characters that need more than one <code>char</code>
614      * value. </p>
615      *
616      * @param target The processing instruction target.
617      * @param data The processing instruction data, or null if
618      * none was supplied. The data does not include any
619      * whitespace separating it from the target.
620      * @exception org.xml.sax.SAXException Any SAX exception, possibly
621      * wrapping another exception.
622      */

623     public void processingInstruction(String JavaDoc target, String JavaDoc data)
624         throws SAXException JavaDoc {
625         contentHandler.processingInstruction(target, data);
626     }
627
628     /**
629      * Receive notification of a skipped entity.
630      * This is not called for entity references within markup constructs
631      * such as element start tags or markup declarations. (The XML
632      * recommendation requires reporting skipped external entities.
633      * SAX also reports internal entity expansion/non-expansion, except
634      * within markup constructs.)
635      *
636      * <p>The Parser will invoke this method each time the entity is
637      * skipped. Non-validating processors may skip entities if they
638      * have not seen the declarations (because, for example, the
639      * entity was declared in an external DTD subset). All processors
640      * may skip external entities, depending on the values of the
641      * <code>http://xml.org/sax/features/external-general-entities</code>
642      * and the
643      * <code>http://xml.org/sax/features/external-parameter-entities</code>
644      * properties.</p>
645      *
646      * @param name The name of the skipped entity. If it is a
647      * parameter entity, the name will begin with '%', and if
648      * it is the external DTD subset, it will be the string
649      * "[dtd]".
650      * @exception org.xml.sax.SAXException Any SAX exception, possibly
651      * wrapping another exception.
652      */

653     public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
654         contentHandler.skippedEntity(name);
655     }
656
657
658     // Lexical Handler interface
659
//-------------------------------------------------------------------------
660

661     /**
662      * Report the start of DTD declarations, if any.
663      *
664      * <p>This method is intended to report the beginning of the
665      * DOCTYPE declaration; if the document has no DOCTYPE declaration,
666      * this method will not be invoked.</p>
667      *
668      * <p>All declarations reported through
669      * {@link org.xml.sax.DTDHandler DTDHandler} or
670      * {@link org.xml.sax.ext.DeclHandler DeclHandler} events must appear
671      * between the startDTD and {@link #endDTD endDTD} events.
672      * Declarations are assumed to belong to the internal DTD subset
673      * unless they appear between {@link #startEntity startEntity}
674      * and {@link #endEntity endEntity} events. Comments and
675      * processing instructions from the DTD should also be reported
676      * between the startDTD and endDTD events, in their original
677      * order of (logical) occurrence; they are not required to
678      * appear in their correct locations relative to DTDHandler
679      * or DeclHandler events, however.</p>
680      *
681      * <p>Note that the start/endDTD events will appear within
682      * the start/endDocument events from ContentHandler and
683      * before the first
684      * {@link org.xml.sax.ContentHandler#startElement startElement}
685      * event.</p>
686      *
687      * @param name The document type name.
688      * @param publicId The declared public identifier for the
689      * external DTD subset, or null if none was declared.
690      * @param systemId The declared system identifier for the
691      * external DTD subset, or null if none was declared.
692      * (Note that this is not resolved against the document
693      * base URI.)
694      * @exception SAXException The application may raise an
695      * exception.
696      * @see #endDTD
697      * @see #startEntity
698      */

699     public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
700         throws SAXException JavaDoc {
701         if (lexicalHandler != null) {
702             lexicalHandler.startDTD(name, publicId, systemId);
703         }
704     }
705
706     /**
707      * Report the end of DTD declarations.
708      *
709      * <p>This method is intended to report the end of the
710      * DOCTYPE declaration; if the document has no DOCTYPE declaration,
711      * this method will not be invoked.</p>
712      *
713      * @exception SAXException The application may raise an exception.
714      * @see #startDTD
715      */

716     public void endDTD() throws SAXException JavaDoc {
717         if (lexicalHandler != null) {
718             lexicalHandler.endDTD();
719         }
720     }
721
722     /**
723      * Report the beginning of some internal and external XML entities.
724      *
725      * <p>The reporting of parameter entities (including
726      * the external DTD subset) is optional, and SAX2 drivers that
727      * report LexicalHandler events may not implement it; you can use the
728      * <code
729      * >http://xml.org/sax/features/lexical-handler/parameter-entities</code>
730      * feature to query or control the reporting of parameter entities.</p>
731      *
732      * <p>General entities are reported with their regular names,
733      * parameter entities have '%' prepended to their names, and
734      * the external DTD subset has the pseudo-entity name "[dtd]".</p>
735      *
736      * <p>When a SAX2 driver is providing these events, all other
737      * events must be properly nested within start/end entity
738      * events. There is no additional requirement that events from
739      * {@link org.xml.sax.ext.DeclHandler DeclHandler} or
740      * {@link org.xml.sax.DTDHandler DTDHandler} be properly ordered.</p>
741      *
742      * <p>Note that skipped entities will be reported through the
743      * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
744      * event, which is part of the ContentHandler interface.</p>
745      *
746      * <p>Because of the streaming event model that SAX uses, some
747      * entity boundaries cannot be reported under any
748      * circumstances:</p>
749      *
750      * <ul>
751      * <li>general entities within attribute values</li>
752      * <li>parameter entities within declarations</li>
753      * </ul>
754      *
755      * <p>These will be silently expanded, with no indication of where
756      * the original entity boundaries were.</p>
757      *
758      * <p>Note also that the boundaries of character references (which
759      * are not really entities anyway) are not reported.</p>
760      *
761      * <p>All start/endEntity events must be properly nested.
762      *
763      * @param name The name of the entity. If it is a parameter
764      * entity, the name will begin with '%', and if it is the
765      * external DTD subset, it will be "[dtd]".
766      * @exception SAXException The application may raise an exception.
767      * @see #endEntity
768      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
769      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
770      */

771     public void startEntity(String JavaDoc name) throws SAXException JavaDoc {
772         if (lexicalHandler != null) {
773             lexicalHandler.startEntity(name);
774         }
775     }
776
777     /**
778      * Report the end of an entity.
779      *
780      * @param name The name of the entity that is ending.
781      * @exception SAXException The application may raise an exception.
782      * @see #startEntity
783      */

784     public void endEntity(String JavaDoc name) throws SAXException JavaDoc {
785         if (lexicalHandler != null) {
786             lexicalHandler.endEntity(name);
787         }
788     }
789
790     /**
791      * Report the start of a CDATA section.
792      *
793      * <p>The contents of the CDATA section will be reported through
794      * the regular {@link org.xml.sax.ContentHandler#characters
795      * characters} event; this event is intended only to report
796      * the boundary.</p>
797      *
798      * @exception SAXException The application may raise an exception.
799      * @see #endCDATA
800      */

801     public void startCDATA() throws SAXException JavaDoc {
802         if (lexicalHandler != null) {
803             lexicalHandler.startCDATA();
804         }
805     }
806
807     /**
808      * Report the end of a CDATA section.
809      *
810      * @exception SAXException The application may raise an exception.
811      * @see #startCDATA
812      */

813     public void endCDATA() throws SAXException JavaDoc {
814         if (lexicalHandler != null) {
815             lexicalHandler.endCDATA();
816         }
817     }
818
819     /**
820      * Report an XML comment anywhere in the document.
821      *
822      * <p>This callback will be used for comments inside or outside the
823      * document element, including comments in the external DTD
824      * subset (if read). Comments in the DTD must be properly
825      * nested inside start/endDTD and start/endEntity events (if
826      * used).</p>
827      *
828      * @param ch An array holding the characters in the comment.
829      * @param start The starting position in the array.
830      * @param length The number of characters to use from the array.
831      * @exception SAXException The application may raise an exception.
832      */

833     public void comment(char ch[], int start, int length) throws SAXException JavaDoc {
834         if (lexicalHandler != null) {
835             lexicalHandler.comment(ch, start, length);
836         }
837     }
838     
839     /** Pass data through the pipline.
840       * By default, this call is ignored.
841       * Subclasses are invited to use this as a way for children tags to
842       * pass data to their parent.
843       *
844       * @param object the data to pass
845       * @exception SAXException The application may raise an exception.
846       */

847     public void objectData(Object JavaDoc object) throws SAXException JavaDoc {
848         if(contentHandler instanceof XMLOutput)
849             ((XMLOutput) contentHandler).objectData(object);
850         else {
851             if(object!=null) {
852                 String JavaDoc output=object.toString();
853                 write(output);
854             } else {
855                 // we could have a "configurable null-toString"...
856
write("null");
857             }
858         }
859     }
860
861     // Properties
862
//-------------------------------------------------------------------------
863
/**
864      * @return the SAX ContentHandler to use to pipe SAX events into
865      */

866     public ContentHandler JavaDoc getContentHandler() {
867         return contentHandler;
868     }
869
870     /**
871      * Sets the SAX ContentHandler to pipe SAX events into
872      *
873      * @param contentHandler is the new ContentHandler to use.
874      * This value cannot be null.
875      */

876     public void setContentHandler(ContentHandler JavaDoc contentHandler) {
877         if (contentHandler == null) {
878             throw new NullPointerException JavaDoc("ContentHandler cannot be null!");
879         }
880         this.contentHandler = contentHandler;
881     }
882
883     /**
884      * @return the SAX LexicalHandler to use to pipe SAX events into
885      */

886     public LexicalHandler JavaDoc getLexicalHandler() {
887         return lexicalHandler;
888     }
889
890     /**
891      * Sets the SAX LexicalHandler to pipe SAX events into
892      *
893      * @param lexicalHandler is the new LexicalHandler to use.
894      * This value can be null.
895      */

896     public void setLexicalHandler(LexicalHandler JavaDoc lexicalHandler) {
897         this.lexicalHandler = lexicalHandler;
898     }
899
900     // Implementation methods
901
//-------------------------------------------------------------------------
902
/**
903      * Factory method to create a new XMLOutput from an XMLWriter
904      */

905     protected static XMLOutput createXMLOutput(final XMLWriter xmlWriter) {
906         XMLOutput answer = new XMLOutput() {
907             public void close() throws IOException JavaDoc {
908                 xmlWriter.close();
909             }
910         };
911         answer.setContentHandler(xmlWriter);
912         answer.setLexicalHandler(xmlWriter);
913         return answer;
914     }
915
916     private final class NamespaceStack {
917         /** A list of maps: Each map contains prefix->uri mapping */
918         private List JavaDoc nsStack;
919
920         private NamespaceStack() {
921             this.nsStack = new ArrayList JavaDoc();
922             this.nsStack.add(new HashMap JavaDoc());
923         }
924
925         private boolean isRootNodeDefaultNs(String JavaDoc prefix, String JavaDoc uri) {
926             return ("".equals(prefix) && "".equals(uri) && nsStack.size() == 1);
927         }
928
929         public void pushNamespace(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
930             Map JavaDoc prefixUriMap;
931
932             if (prefix == null) {
933                 prefix = "";
934             }
935             if (uri == null) {
936                 uri = "";
937             }
938
939             if ("xml".equals(prefix)) {
940                 // We should ignore setting 'xml' prefix
941
// As declared in java of ContentHandler#startPrefixMapping
942
return;
943             }
944
945
946             // Lets find out if we already declared this same prefix,
947
// if not declare in current depth map (the first of list)
948
// and call contentHandler.startPrefixMapping(prefix, uri);
949
boolean isNew = true;
950             for (Iterator JavaDoc iter = nsStack.iterator(); iter.hasNext();) {
951                 prefixUriMap = (Map JavaDoc) iter.next();
952                 if (prefixUriMap.containsKey(prefix)) {
953                     if (uri.equals(prefixUriMap.get(prefix))) {
954                         // Its an active namespace already
955
// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() IS NOT NEW prefix="+prefix+",uri="+uri);
956
isNew = false;
957                     }
958                     // We found it in stack
959
// If it was exacly the same, we won't bother
960
break;
961                 }
962             }
963
964             if (isNew) {
965                 // not declared sometime before
966
prefixUriMap = (Map JavaDoc) nsStack.get(0); // Current depth map
967
// Sanity check: Don't let two prefixes for diferent uris in
968
// same depth
969
if (prefixUriMap.containsKey(prefix)) {
970                     if (!uri.equals(prefixUriMap.get(prefix))) {
971                         throw new SAXException JavaDoc("Cannot set same prefix to diferent URI in same node: trying to add prefix \""
972                                 + prefix + "\" for uri \""+uri+"\" whereas the declared ones are " + prefixUriMap);
973                     }
974                 } else {
975                     prefixUriMap.put(prefix, uri);
976
977                     // To avoid setting xmlns="" for top node (not very nice :D)
978
// We need to especificaly check this condition
979
if (!isRootNodeDefaultNs(prefix, uri)) {
980 // System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() prefix="+prefix+",uri="+uri);
981
contentHandler.startPrefixMapping(prefix, uri);
982                     }
983                 }
984             }
985         }
986
987         public void popNamespaces() throws SAXException JavaDoc {
988             Map JavaDoc prefixUriMap = (Map JavaDoc)nsStack.get(0);
989             for (Iterator JavaDoc iter = prefixUriMap.keySet().iterator();iter.hasNext();) {
990                 String JavaDoc prefix = (String JavaDoc)iter.next();
991                 String JavaDoc uri = (String JavaDoc) prefixUriMap.get(prefix);
992                 iter.remove();
993
994                 // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
995
// We aren't going to call endPrefixMapping neither
996
if (!isRootNodeDefaultNs(prefix, uri)) {
997 // System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespaces() prefix="+prefix);
998
contentHandler.endPrefixMapping(prefix);
999                 }
1000            }
1001        }
1002
1003        public void popNamespace(String JavaDoc prefix) throws SAXException JavaDoc {
1004            Map JavaDoc prefixUriMap = (Map JavaDoc)nsStack.get(0);
1005
1006            if (prefix == null) {
1007                prefix = "";
1008            }
1009
1010            if ("xml".equals(prefix)) {
1011                // We should ignore setting 'xml' prefix
1012
// As declared in java of ContentHandler#startPrefixMapping
1013
return;
1014            }
1015
1016            if (prefixUriMap.containsKey(prefix)) {
1017                String JavaDoc uri = (String JavaDoc) prefixUriMap.get(prefix);
1018                prefixUriMap.remove(prefix);
1019                // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
1020
// We aren't going to call endPrefixMapping neither
1021
if (!isRootNodeDefaultNs(prefix, uri)) {
1022// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespace() prefix="+prefix);
1023
contentHandler.endPrefixMapping(prefix);
1024                }
1025            }/* else {
1026                improper nesting ? or already removed in popNamespaces
1027            }
1028            */

1029        }
1030
1031        public void decreaseLevel() {
1032            nsStack.remove(0);
1033        }
1034
1035        public void increaseLevel() {
1036            nsStack.add(0, new HashMap JavaDoc());
1037        }
1038    }
1039}
1040
Popular Tags