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