001: /*
002: * Copyright 2001-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: ToHTMLSAXHandler.java,v 1.14 2005/07/15 16:01:12 minchau Exp $
018: */
019:
020: package org.apache.xml.serializer;
021:
022: import java.io.IOException;
023: import java.io.OutputStream;
024: import java.io.Writer;
025: import java.util.Properties;
026:
027: import javax.xml.transform.Result;
028:
029: import org.w3c.dom.Node;
030: import org.xml.sax.Attributes;
031: import org.xml.sax.ContentHandler;
032: import org.xml.sax.Locator;
033: import org.xml.sax.SAXException;
034: import org.xml.sax.ext.LexicalHandler;
035:
036: /**
037: * This class accepts SAX-like calls, then sends true SAX calls to a
038: * wrapped SAX handler. There is optimization done knowing that the ultimate
039: * output is HTML.
040: *
041: * This class is not a public API.
042: *
043: * @xsl.usage internal
044: */
045: public final class ToHTMLSAXHandler extends ToSAXHandler {
046: /**
047: * Handle document type declaration (for first element only)
048: */
049: private boolean m_dtdHandled = false;
050:
051: /**
052: * Keeps track of whether output escaping is currently enabled
053: */
054: protected boolean m_escapeSetting = false;
055:
056: /**
057: * Returns null.
058: * @return null
059: * @see Serializer#getOutputFormat()
060: */
061: public Properties getOutputFormat() {
062: return null;
063: }
064:
065: /**
066: * Reurns null
067: * @return null
068: * @see Serializer#getOutputStream()
069: */
070: public OutputStream getOutputStream() {
071: return null;
072: }
073:
074: /**
075: * Returns null
076: * @return null
077: * @see Serializer#getWriter()
078: */
079: public Writer getWriter() {
080: return null;
081: }
082:
083: /**
084: * Does nothing.
085: *
086: */
087: public void indent(int n) throws SAXException {
088: }
089:
090: /**
091: * Does nothing.
092: * @see DOMSerializer#serialize(Node)
093: */
094: public void serialize(Node node) throws IOException {
095: return;
096: }
097:
098: /**
099: * Turns special character escaping on/off.
100: *
101: *
102: * @param escape true if escaping is to be set on.
103: *
104: * @see SerializationHandler#setEscaping(boolean)
105: */
106: public boolean setEscaping(boolean escape) throws SAXException {
107: boolean oldEscapeSetting = m_escapeSetting;
108: m_escapeSetting = escape;
109:
110: if (escape) {
111: processingInstruction(Result.PI_ENABLE_OUTPUT_ESCAPING, "");
112: } else {
113: processingInstruction(Result.PI_DISABLE_OUTPUT_ESCAPING, "");
114: }
115:
116: return oldEscapeSetting;
117: }
118:
119: /**
120: * Does nothing
121: * @param indent the number of spaces to indent per indentation level
122: * (ignored)
123: * @see SerializationHandler#setIndent(boolean)
124: */
125: public void setIndent(boolean indent) {
126: }
127:
128: /**
129: * Does nothing.
130: * @param format this parameter is not used
131: * @see Serializer#setOutputFormat(Properties)
132: */
133: public void setOutputFormat(Properties format) {
134: }
135:
136: /**
137: * Does nothing.
138: * @param output this parameter is ignored
139: * @see Serializer#setOutputStream(OutputStream)
140: */
141: public void setOutputStream(OutputStream output) {
142: }
143:
144: /**
145: * Does nothing.
146: * @param writer this parameter is ignored.
147: * @see Serializer#setWriter(Writer)
148: */
149: public void setWriter(Writer writer) {
150: }
151:
152: /**
153: * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
154: */
155: /**
156: * Does nothing.
157: *
158: * @param eName this parameter is ignored
159: * @param aName this parameter is ignored
160: * @param type this parameter is ignored
161: * @param valueDefault this parameter is ignored
162: * @param value this parameter is ignored
163: * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String,String,String)
164: */
165: public void attributeDecl(String eName, String aName, String type,
166: String valueDefault, String value) throws SAXException {
167: }
168:
169: /**
170: * Does nothing.
171: * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
172: */
173: public void elementDecl(String name, String model)
174: throws SAXException {
175: return;
176: }
177:
178: /**
179: * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
180: */
181: public void externalEntityDecl(String arg0, String arg1, String arg2)
182: throws SAXException {
183: }
184:
185: /**
186: * Does nothing.
187: *
188: * @see org.xml.sax.DTDHandler#unparsedEntityDecl
189: */
190: public void internalEntityDecl(String name, String value)
191: throws SAXException {
192: }
193:
194: /**
195: * Receive notification of the end of an element.
196: *
197: * <p>The SAX parser will invoke this method at the end of every
198: * element in the XML document; there will be a corresponding
199: * startElement() event for every endElement() event (even when the
200: * element is empty).</p>
201: *
202: * <p>If the element name has a namespace prefix, the prefix will
203: * still be attached to the name.</p>
204: *
205: *
206: * @param uri The Namespace URI, or the empty string if the
207: * element has no Namespace URI or if Namespace
208: * processing is not being performed.
209: * @param localName The local name (without prefix), or the
210: * empty string if Namespace processing is not being
211: * performed.
212: * @param qName The qualified name (with prefix), or the
213: * empty string if qualified names are not available.
214: * @throws org.xml.sax.SAXException Any SAX exception, possibly
215: * wrapping another exception.
216: * @see org.xml.sax.ContentHandler#endElement(String, String, String)
217: */
218: public void endElement(String uri, String localName, String qName)
219: throws SAXException {
220: flushPending();
221: m_saxHandler.endElement(uri, localName, qName);
222:
223: // time to fire off endElement event
224: if (m_tracer != null)
225: super .fireEndElem(qName);
226: }
227:
228: /**
229: * Does nothing.
230: */
231: public void endPrefixMapping(String prefix) throws SAXException {
232: }
233:
234: /**
235: * Does nothing.
236: * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
237: */
238: public void ignorableWhitespace(char[] ch, int start, int length)
239: throws SAXException {
240: }
241:
242: /**
243: * Receive notification of a processing instruction.
244: *
245: * <p>The Parser will invoke this method once for each processing
246: * instruction found: note that processing instructions may occur
247: * before or after the main document element.</p>
248: *
249: * <p>A SAX parser should never report an XML declaration (XML 1.0,
250: * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
251: * using this method.</p>
252: *
253: * @param target The processing instruction target.
254: * @param data The processing instruction data, or null if
255: * none was supplied.
256: * @throws org.xml.sax.SAXException Any SAX exception, possibly
257: * wrapping another exception.
258: *
259: * @throws org.xml.sax.SAXException
260: * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
261: */
262: public void processingInstruction(String target, String data)
263: throws SAXException {
264: flushPending();
265: m_saxHandler.processingInstruction(target, data);
266:
267: // time to fire off processing instruction event
268:
269: if (m_tracer != null)
270: super .fireEscapingEvent(target, data);
271: }
272:
273: /**
274: * Does nothing.
275: * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
276: */
277: public void setDocumentLocator(Locator arg0) {
278: // do nothing
279: }
280:
281: /**
282: * Does nothing.
283: * @see org.xml.sax.ContentHandler#skippedEntity(String)
284: */
285: public void skippedEntity(String arg0) throws SAXException {
286: }
287:
288: /**
289: * Receive notification of the beginning of an element, although this is a
290: * SAX method additional namespace or attribute information can occur before
291: * or after this call, that is associated with this element.
292: *
293: *
294: * @param namespaceURI The Namespace URI, or the empty string if the
295: * element has no Namespace URI or if Namespace
296: * processing is not being performed.
297: * @param localName The local name (without prefix), or the
298: * empty string if Namespace processing is not being
299: * performed.
300: * @param qName The elements name.
301: * @param atts The attributes attached to the element, if any.
302: * @throws org.xml.sax.SAXException Any SAX exception, possibly
303: * wrapping another exception.
304: * @see org.xml.sax.ContentHandler#startElement
305: * @see org.xml.sax.ContentHandler#endElement
306: * @see org.xml.sax.AttributeList
307: *
308: * @throws org.xml.sax.SAXException
309: *
310: * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
311: */
312: public void startElement(String namespaceURI, String localName,
313: String qName, Attributes atts) throws SAXException {
314: flushPending();
315: super .startElement(namespaceURI, localName, qName, atts);
316: m_saxHandler.startElement(namespaceURI, localName, qName, atts);
317: m_elemContext.m_startTagOpen = false;
318: }
319:
320: /**
321: * Receive notification of a comment anywhere in the document. This callback
322: * will be used for comments inside or outside the document element.
323: * @param ch An array holding the characters in the comment.
324: * @param start The starting position in the array.
325: * @param length The number of characters to use from the array.
326: * @throws org.xml.sax.SAXException The application may raise an exception.
327: *
328: * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
329: */
330: public void comment(char[] ch, int start, int length)
331: throws SAXException {
332: flushPending();
333: if (m_lexHandler != null)
334: m_lexHandler.comment(ch, start, length);
335:
336: // time to fire off comment event
337: if (m_tracer != null)
338: super .fireCommentEvent(ch, start, length);
339: return;
340: }
341:
342: /**
343: * Does nothing.
344: * @see org.xml.sax.ext.LexicalHandler#endCDATA()
345: */
346: public void endCDATA() throws SAXException {
347: return;
348: }
349:
350: /**
351: * Does nothing.
352: * @see org.xml.sax.ext.LexicalHandler#endDTD()
353: */
354: public void endDTD() throws SAXException {
355: }
356:
357: /**
358: * Does nothing.
359: * @see org.xml.sax.ext.LexicalHandler#startCDATA()
360: */
361: public void startCDATA() throws SAXException {
362: }
363:
364: /**
365: * Does nothing.
366: * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
367: */
368: public void startEntity(String arg0) throws SAXException {
369: }
370:
371: /**
372: * Receive notification of the end of a document.
373: *
374: * <p>The SAX parser will invoke this method only once, and it will
375: * be the last method invoked during the parse. The parser shall
376: * not invoke this method until it has either abandoned parsing
377: * (because of an unrecoverable error) or reached the end of
378: * input.</p>
379: *
380: * @throws org.xml.sax.SAXException Any SAX exception, possibly
381: * wrapping another exception.
382: *
383: * @throws org.xml.sax.SAXException
384: *
385: *
386: */
387: public void endDocument() throws SAXException {
388: flushPending();
389:
390: // Close output document
391: m_saxHandler.endDocument();
392:
393: if (m_tracer != null)
394: super .fireEndDoc();
395: }
396:
397: /**
398: * This method is called when all the data needed for a call to the
399: * SAX handler's startElement() method has been gathered.
400: */
401: protected void closeStartTag() throws SAXException {
402:
403: m_elemContext.m_startTagOpen = false;
404:
405: // Now is time to send the startElement event
406: m_saxHandler.startElement(EMPTYSTRING,
407: m_elemContext.m_elementName,
408: m_elemContext.m_elementName, m_attributes);
409: m_attributes.clear();
410:
411: }
412:
413: /**
414: * Do nothing.
415: * @see SerializationHandler#close()
416: */
417: public void close() {
418: return;
419: }
420:
421: /**
422: * Receive notification of character data.
423: *
424: * @param chars The string of characters to process.
425: *
426: * @throws org.xml.sax.SAXException
427: *
428: * @see ExtendedContentHandler#characters(String)
429: */
430: public void characters(final String chars) throws SAXException {
431: final int length = chars.length();
432: if (length > m_charsBuff.length) {
433: m_charsBuff = new char[length * 2 + 1];
434: }
435: chars.getChars(0, length, m_charsBuff, 0);
436: this .characters(m_charsBuff, 0, length);
437: }
438:
439: /**
440: * A constructor
441: * @param handler the wrapped SAX content handler
442: * @param encoding the encoding of the output HTML document
443: */
444: public ToHTMLSAXHandler(ContentHandler handler, String encoding) {
445: super (handler, encoding);
446: }
447:
448: /**
449: * A constructor.
450: * @param handler the wrapped SAX content handler
451: * @param lex the wrapped lexical handler
452: * @param encoding the encoding of the output HTML document
453: */
454: public ToHTMLSAXHandler(ContentHandler handler, LexicalHandler lex,
455: String encoding) {
456: super (handler, lex, encoding);
457: }
458:
459: /**
460: * An element starts, but attributes are not fully known yet.
461: *
462: * @param elementNamespaceURI the URI of the namespace of the element
463: * (optional)
464: * @param elementLocalName the element name, but without prefix
465: * (optional)
466: * @param elementName the element name, with prefix, if any (required)
467: *
468: * @see ExtendedContentHandler#startElement(String)
469: */
470: public void startElement(String elementNamespaceURI,
471: String elementLocalName, String elementName)
472: throws SAXException {
473:
474: super .startElement(elementNamespaceURI, elementLocalName,
475: elementName);
476:
477: flushPending();
478:
479: // Handle document type declaration (for first element only)
480: if (!m_dtdHandled) {
481: String doctypeSystem = getDoctypeSystem();
482: String doctypePublic = getDoctypePublic();
483: if ((doctypeSystem != null) || (doctypePublic != null)) {
484: if (m_lexHandler != null)
485: m_lexHandler.startDTD(elementName, doctypePublic,
486: doctypeSystem);
487: }
488: m_dtdHandled = true;
489: }
490: m_elemContext = m_elemContext.push(elementNamespaceURI,
491: elementLocalName, elementName);
492: }
493:
494: /**
495: * An element starts, but attributes are not fully known yet.
496: *
497: * @param elementName the element name, with prefix, if any
498: *
499: * @see ExtendedContentHandler#startElement(String)
500: */
501: public void startElement(String elementName) throws SAXException {
502: this .startElement(null, null, elementName);
503: }
504:
505: /**
506: * Receive notification of the end of an element.
507: * @param elementName The element type name
508: * @throws org.xml.sax.SAXException Any SAX exception, possibly
509: * wrapping another exception.
510: *
511: * @see ExtendedContentHandler#endElement(String)
512: */
513: public void endElement(String elementName) throws SAXException {
514: flushPending();
515: m_saxHandler.endElement(EMPTYSTRING, elementName, elementName);
516:
517: // time to fire off endElement event
518: if (m_tracer != null)
519: super .fireEndElem(elementName);
520: }
521:
522: /**
523: * Receive notification of character data.
524: *
525: * <p>The Parser will call this method to report each chunk of
526: * character data. SAX parsers may return all contiguous character
527: * data in a single chunk, or they may split it into several
528: * chunks; however, all of the characters in any single event
529: * must come from the same external entity, so that the Locator
530: * provides useful information.</p>
531: *
532: * <p>The application must not attempt to read from the array
533: * outside of the specified range.</p>
534: *
535: * <p>Note that some parsers will report whitespace using the
536: * ignorableWhitespace() method rather than this one (validating
537: * parsers must do so).</p>
538: *
539: * @param ch The characters from the XML document.
540: * @param off The start position in the array.
541: * @param len The number of characters to read from the array.
542: * @throws org.xml.sax.SAXException Any SAX exception, possibly
543: * wrapping another exception.
544: * @see #ignorableWhitespace
545: * @see org.xml.sax.Locator
546: *
547: * @throws org.xml.sax.SAXException
548: *
549: * @see org.xml.sax.ContentHandler#characters(char[], int, int)
550: */
551: public void characters(char[] ch, int off, int len)
552: throws SAXException {
553:
554: flushPending();
555: m_saxHandler.characters(ch, off, len);
556:
557: // time to fire off characters event
558: if (m_tracer != null)
559: super .fireCharEvent(ch, off, len);
560: }
561:
562: /**
563: * This method flushes any pending events, which can be startDocument()
564: * closing the opening tag of an element, or closing an open CDATA section.
565: */
566: public void flushPending() throws SAXException {
567: if (m_needToCallStartDocument) {
568: startDocumentInternal();
569: m_needToCallStartDocument = false;
570: }
571: // Close any open element
572: if (m_elemContext.m_startTagOpen) {
573: closeStartTag();
574: m_elemContext.m_startTagOpen = false;
575: }
576: }
577:
578: /**
579: * Handle a prefix/uri mapping, which is associated with a startElement()
580: * that is soon to follow. Need to close any open start tag to make
581: * sure than any name space attributes due to this event are associated wih
582: * the up comming element, not the current one.
583: * @see ExtendedContentHandler#startPrefixMapping
584: *
585: * @param prefix The Namespace prefix being declared.
586: * @param uri The Namespace URI the prefix is mapped to.
587: * @param shouldFlush true if any open tags need to be closed first, this
588: * will impact which element the mapping applies to (open parent, or its up
589: * comming child)
590: * @return returns true if the call made a change to the current
591: * namespace information, false if it did not change anything, e.g. if the
592: * prefix/namespace mapping was already in scope from before.
593: *
594: * @throws org.xml.sax.SAXException The client may throw
595: * an exception during processing.
596: */
597: public boolean startPrefixMapping(String prefix, String uri,
598: boolean shouldFlush) throws SAXException {
599: // no namespace support for HTML
600: if (shouldFlush)
601: flushPending();
602: m_saxHandler.startPrefixMapping(prefix, uri);
603: return false;
604: }
605:
606: /**
607: * Begin the scope of a prefix-URI Namespace mapping
608: * just before another element is about to start.
609: * This call will close any open tags so that the prefix mapping
610: * will not apply to the current element, but the up comming child.
611: *
612: * @see org.xml.sax.ContentHandler#startPrefixMapping
613: *
614: * @param prefix The Namespace prefix being declared.
615: * @param uri The Namespace URI the prefix is mapped to.
616: *
617: * @throws org.xml.sax.SAXException The client may throw
618: * an exception during processing.
619: *
620: */
621: public void startPrefixMapping(String prefix, String uri)
622: throws org.xml.sax.SAXException {
623: startPrefixMapping(prefix, uri, true);
624: }
625:
626: /**
627: * This method is used when a prefix/uri namespace mapping
628: * is indicated after the element was started with a
629: * startElement() and before and endElement().
630: * startPrefixMapping(prefix,uri) would be used before the
631: * startElement() call.
632: * @param prefix the prefix associated with the given URI.
633: * @param uri the URI of the namespace
634: *
635: * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
636: */
637: public void namespaceAfterStartElement(final String prefix,
638: final String uri) throws SAXException {
639: // hack for XSLTC with finding URI for default namespace
640: if (m_elemContext.m_elementURI == null) {
641: String prefix1 = getPrefixPart(m_elemContext.m_elementName);
642: if (prefix1 == null && EMPTYSTRING.equals(prefix)) {
643: // the elements URI is not known yet, and it
644: // doesn't have a prefix, and we are currently
645: // setting the uri for prefix "", so we have
646: // the uri for the element... lets remember it
647: m_elemContext.m_elementURI = uri;
648: }
649: }
650: startPrefixMapping(prefix, uri, false);
651: }
652:
653: /**
654: * Try's to reset the super class and reset this class for
655: * re-use, so that you don't need to create a new serializer
656: * (mostly for performance reasons).
657: *
658: * @return true if the class was successfuly reset.
659: * @see Serializer#reset()
660: */
661: public boolean reset() {
662: boolean wasReset = false;
663: if (super .reset()) {
664: resetToHTMLSAXHandler();
665: wasReset = true;
666: }
667: return wasReset;
668: }
669:
670: /**
671: * Reset all of the fields owned by ToHTMLSAXHandler class
672: *
673: */
674: private void resetToHTMLSAXHandler() {
675: this .m_escapeSetting = false;
676: }
677: }
|