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