View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: ToHTMLSAXHandler.java,v 1.3 2005/09/28 13:49:07 pvedula Exp $
22   */
23  
24  package com.sun.org.apache.xml.internal.serializer;
25  
26  import java.io.IOException;
27  import java.io.OutputStream;
28  import java.io.Writer;
29  import java.util.Properties;
30  
31  import javax.xml.transform.Result;
32  
33  import org.w3c.dom.Node;
34  import org.xml.sax.Attributes;
35  import org.xml.sax.ContentHandler;
36  import org.xml.sax.Locator;
37  import org.xml.sax.SAXException;
38  import org.xml.sax.ext.LexicalHandler;
39  
40  /**
41   * This class accepts SAX-like calls, then sends true SAX calls to a
42   * wrapped SAX handler.  There is optimization done knowing that the ultimate
43   * output is HTML.
44   *
45   * This class is not a public API.
46   *
47   * @xsl.usage internal
48   */
49  public final class ToHTMLSAXHandler extends ToSAXHandler
50  {
51          /**
52           *  Handle document type declaration (for first element only)
53           */
54          private boolean m_dtdHandled = false;
55  
56      /**
57       * Keeps track of whether output escaping is currently enabled
58       */
59      protected boolean m_escapeSetting = true;
60  
61      /**
62       * Returns null.
63       * @return null
64       * @see Serializer#getOutputFormat()
65       */
66      public Properties getOutputFormat()
67      {
68          return null;
69      }
70  
71      /**
72       * Reurns null
73       * @return null
74       * @see Serializer#getOutputStream()
75       */
76      public OutputStream getOutputStream()
77      {
78          return null;
79      }
80  
81      /**
82       * Returns null
83       * @return null
84       * @see Serializer#getWriter()
85       */
86      public Writer getWriter()
87      {
88          return null;
89      }
90  
91      /**
92       * Does nothing.
93       *
94       */
95      public void indent(int n) throws SAXException
96      {
97      }
98  
99      /**
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         super.setDocumentLocator(arg0);
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_escapeSetting = true;
746     }
747 }