View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 1999-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: ToUnknownStream.java,v 1.3 2005/09/28 13:49:08 pvedula Exp $
22   */
23  package com.sun.org.apache.xml.internal.serializer;
24  
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.io.Writer;
28  import java.util.Properties;
29  import java.util.Vector;
30  
31  import javax.xml.transform.SourceLocator;
32  import javax.xml.transform.Transformer;
33  
34  import org.w3c.dom.Node;
35  import org.xml.sax.Attributes;
36  import org.xml.sax.ContentHandler;
37  import org.xml.sax.Locator;
38  import org.xml.sax.SAXException;
39  
40  
41  /**
42   *This class wraps another SerializationHandler. The wrapped object will either
43   * handler XML or HTML, which is not known until a little later when the first XML
44   * tag is seen.  If the first tag is <html> then the wrapped object is an HTML
45   * handler, otherwise it is an XML handler.
46   *
47   * This class effectively caches the first few calls to it then passes them
48   * on to the wrapped handler (once it exists).  After that subsequent calls a
49   * simply passed directly to the wrapped handler.
50   *
51   * The user of this class doesn't know if the output is ultimatley XML or HTML.
52   *
53   * This class is not a public API, it is public because it is used within Xalan.
54   * @xsl.usage internal
55   */
56  public final class ToUnknownStream extends SerializerBase
57  {
58  
59      /**
60       * The wrapped handler, initially XML but possibly switched to HTML
61       */
62      private SerializationHandler m_handler;
63  
64      /**
65       * A String with no characters
66       */
67      private static final String EMPTYSTRING = "";
68  
69      /**
70       * true if the underlying handler (XML or HTML) is fully initialized
71       */
72      private boolean m_wrapped_handler_not_initialized = false;
73  
74  
75      /**
76       * the prefix of the very first tag in the document
77       */
78      private String m_firstElementPrefix;
79      /**
80       * the element name (including any prefix) of the very first tag in the document
81       */
82      private String m_firstElementName;
83  
84      /**
85       * the namespace URI associated with the first element
86       */
87      private String m_firstElementURI;
88  
89      /**
90       * the local name (no prefix) associated with the first element
91       */
92      private String m_firstElementLocalName = null;
93  
94      /**
95       * true if the first tag has been emitted to the wrapped handler
96       */
97      private boolean m_firstTagNotEmitted = true;
98  
99      /**
100      * A collection of namespace URI's (only for first element).
101      * _namespacePrefix has the matching prefix for these URI's
102      */
103     private Vector m_namespaceURI = null;
104     /**
105      * A collection of namespace Prefix (only for first element)
106      * _namespaceURI has the matching URIs for these prefix'
107      */
108     private Vector m_namespacePrefix = null;
109 
110     /**
111      * true if startDocument() was called before the underlying handler
112      * was initialized
113      */
114     private boolean m_needToCallStartDocument = false;
115     /**
116      * true if setVersion() was called before the underlying handler
117      * was initialized
118      */
119     private boolean m_setVersion_called = false;
120     /**
121      * true if setDoctypeSystem() was called before the underlying handler
122      * was initialized
123      */
124     private boolean m_setDoctypeSystem_called = false;
125     /**
126      * true if setDoctypePublic() was called before the underlying handler
127      * was initialized
128      */
129     private boolean m_setDoctypePublic_called = false;
130     /**
131      * true if setMediaType() was called before the underlying handler
132      * was initialized
133      */
134     private boolean m_setMediaType_called = false;
135 
136     /**
137      * Default constructor.
138      * Initially this object wraps an XML Stream object, so _handler is never null.
139      * That may change later to an HTML Stream object.
140      */
141     public ToUnknownStream()
142     {
143         m_handler = new ToXMLStream();
144     }
145 
146     /**
147      * @see Serializer#asContentHandler()
148      * @return the wrapped XML or HTML handler
149      */
150     public ContentHandler asContentHandler() throws IOException
151     {
152         /* don't return the real handler ( m_handler ) because
153          * that would expose the real handler to the outside.
154          * Keep m_handler private so it can be internally swapped
155          * to an HTML handler.
156          */
157         return this;
158     }
159 
160     /**
161      * @see SerializationHandler#close()
162      */
163     public void close()
164     {
165         m_handler.close();
166     }
167 
168     /**
169      * @see Serializer#getOutputFormat()
170      * @return the properties of the underlying handler
171      */
172     public Properties getOutputFormat()
173     {
174         return m_handler.getOutputFormat();
175     }
176 
177     /**
178      * @see Serializer#getOutputStream()
179      * @return the OutputStream of the underlying XML or HTML handler
180      */
181     public OutputStream getOutputStream()
182     {
183         return m_handler.getOutputStream();
184     }
185 
186     /**
187      * @see Serializer#getWriter()
188      * @return the Writer of the underlying XML or HTML handler
189      */
190     public Writer getWriter()
191     {
192         return m_handler.getWriter();
193     }
194 
195     /**
196      * passes the call on to the underlying HTML or XML handler
197      * @see Serializer#reset()
198      * @return ???
199      */
200     public boolean reset()
201     {
202         return m_handler.reset();
203     }
204 
205     /**
206      * Converts the DOM node to output
207      * @param node the DOM node to transform to output
208      * @see DOMSerializer#serialize(Node)
209      *
210      */
211     public void serialize(Node node) throws IOException
212     {
213         if (m_firstTagNotEmitted)
214         {
215             flush();
216         }
217         m_handler.serialize(node);
218     }
219 
220     /**
221      * @see SerializationHandler#setEscaping(boolean)
222      */
223     public boolean setEscaping(boolean escape) throws SAXException
224     {
225         return m_handler.setEscaping(escape);
226     }
227 
228     /**
229      * Set the properties of the handler
230      * @param format the output properties to set
231      * @see Serializer#setOutputFormat(Properties)
232      */
233     public void setOutputFormat(Properties format)
234     {
235         m_handler.setOutputFormat(format);
236     }
237 
238     /**
239      * Sets the output stream to write to
240      * @param output the OutputStream to write to
241      * @see Serializer#setOutputStream(OutputStream)
242      */
243     public void setOutputStream(OutputStream output)
244     {
245         m_handler.setOutputStream(output);
246     }
247 
248     /**
249      * Sets the writer to write to
250      * @param writer the writer to write to
251      * @see Serializer#setWriter(Writer)
252      */
253     public void setWriter(Writer writer)
254     {
255         m_handler.setWriter(writer);
256     }
257 
258     /**
259      * Adds an attribute to the currenly open tag
260      * @param uri the URI of a namespace
261      * @param localName the attribute name, without prefix
262      * @param rawName the attribute name, with prefix (if any)
263      * @param type the type of the attribute, typically "CDATA"
264      * @param value the value of the parameter
265      * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
266      * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
267      */
268     public void addAttribute(
269         String uri,
270         String localName,
271         String rawName,
272         String type,
273         String value)
274         throws SAXException
275     {
276         addAttribute(uri, localName, rawName, type, value, false);
277     }
278 
279     /**
280      * Adds an attribute to the currenly open tag
281      * @param uri the URI of a namespace
282      * @param localName the attribute name, without prefix
283      * @param rawName the attribute name, with prefix (if any)
284      * @param type the type of the attribute, typically "CDATA"
285      * @param value the value of the parameter
286      * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
287      * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
288      */
289     public void addAttribute(
290         String uri,
291         String localName,
292         String rawName,
293         String type,
294         String value,
295         boolean XSLAttribute)
296         throws SAXException
297     {
298         if (m_firstTagNotEmitted)
299         {
300             flush();
301         }
302         m_handler.addAttribute(uri, localName, rawName, type, value, XSLAttribute);
303     }
304     /**
305      * Adds an attribute to the currenly open tag
306      * @param rawName the attribute name, with prefix (if any)
307      * @param value the value of the parameter
308      * @see ExtendedContentHandler#addAttribute(String, String)
309      */
310     public void addAttribute(String rawName, String value)
311     {
312         if (m_firstTagNotEmitted)
313         {
314             flush();
315         }
316         m_handler.addAttribute(rawName, value);
317 
318     }
319 
320     /**
321      * Adds a unique attribute to the currenly open tag
322      */
323     public void addUniqueAttribute(String rawName, String value, int flags)
324         throws SAXException
325     {
326         if (m_firstTagNotEmitted)
327         {
328             flush();
329         }
330         m_handler.addUniqueAttribute(rawName, value, flags);
331 
332     }
333 
334     /**
335      * Converts the String to a character array and calls the SAX method
336      * characters(char[],int,int);
337      *
338      * @see ExtendedContentHandler#characters(String)
339      */
340     public void characters(String chars) throws SAXException
341     {
342         final int length = chars.length();
343         if (length > m_charsBuff.length)
344         {
345             m_charsBuff = new char[length*2 + 1];
346         }
347         chars.getChars(0, length, m_charsBuff, 0);
348         this.characters(m_charsBuff, 0, length);
349     }
350 
351     /**
352      * Pass the call on to the underlying handler
353      * @see ExtendedContentHandler#endElement(String)
354      */
355     public void endElement(String elementName) throws SAXException
356     {
357         if (m_firstTagNotEmitted)
358         {
359             flush();
360         }
361         m_handler.endElement(elementName);
362     }
363 
364 
365     /**
366      * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
367      * @param prefix The prefix that maps to the URI
368      * @param uri The URI for the namespace
369      */
370     public void startPrefixMapping(String prefix, String uri) throws SAXException
371     {
372         this.startPrefixMapping(prefix,uri, true);
373     }
374 
375     /**
376      * This method is used when a prefix/uri namespace mapping
377      * is indicated after the element was started with a
378      * startElement() and before and endElement().
379      * startPrefixMapping(prefix,uri) would be used before the
380      * startElement() call.
381      * @param uri the URI of the namespace
382      * @param prefix the prefix associated with the given URI.
383      *
384      * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
385      */
386     public void namespaceAfterStartElement(String prefix, String uri)
387         throws SAXException
388     {
389         // hack for XSLTC with finding URI for default namespace
390         if (m_firstTagNotEmitted && m_firstElementURI == null && m_firstElementName != null)
391         {
392             String prefix1 = getPrefixPart(m_firstElementName);
393             if (prefix1 == null && EMPTYSTRING.equals(prefix))
394             {
395                 // the elements URI is not known yet, and it
396                 // doesn't have a prefix, and we are currently
397                 // setting the uri for prefix "", so we have
398                 // the uri for the element... lets remember it
399                 m_firstElementURI = uri;
400             }
401         }
402         startPrefixMapping(prefix,uri, false);
403     }
404 
405     public boolean startPrefixMapping(String prefix, String uri, boolean shouldFlush)
406         throws SAXException
407     {
408         boolean pushed = false;
409         if (m_firstTagNotEmitted)
410         {
411             if (m_firstElementName != null && shouldFlush)
412             {
413                 /* we've already seen a startElement, and this is a prefix mapping
414                  * for the up coming element, so flush the old element
415                  * then send this event on its way.
416                  */
417                 flush();
418                 pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
419             }
420             else
421             {
422                 if (m_namespacePrefix == null)
423                 {
424                     m_namespacePrefix = new Vector();
425                     m_namespaceURI = new Vector();
426                 }
427                 m_namespacePrefix.addElement(prefix);
428                 m_namespaceURI.addElement(uri);
429 
430                 if (m_firstElementURI == null)
431                 {
432                     if (prefix.equals(m_firstElementPrefix))
433                         m_firstElementURI = uri;
434                 }
435             }
436 
437         }
438         else
439         {
440            pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
441         }
442         return pushed;
443     }
444 
445     /**
446       * This method cannot be cached because default is different in
447       * HTML and XML (we need more than a boolean).
448       */
449 
450     public void setVersion(String version)
451     {
452         m_handler.setVersion(version);
453 
454         // Cache call to setVersion()
455         //       super.setVersion(version);
456         m_setVersion_called = true;
457     }
458 
459     /**
460      * @see org.xml.sax.ContentHandler#startDocument()
461      */
462     public void startDocument() throws SAXException
463     {
464         m_needToCallStartDocument = true;
465     }
466 
467 
468 
469     public void startElement(String qName) throws SAXException
470     {
471         this.startElement(null, null, qName, null);
472     }
473 
474     public void startElement(String namespaceURI, String localName, String qName) throws SAXException
475     {
476         this.startElement(namespaceURI, localName, qName, null);
477     }
478 
479     public void startElement(
480         String namespaceURI,
481         String localName,
482         String elementName,
483         Attributes atts) throws SAXException
484     {
485 
486         if (m_needToCallSetDocumentInfo){
487             super.setDocumentInfo();
488             m_needToCallSetDocumentInfo = false;
489         }
490 
491         /* we are notified of the start of an element */
492         if (m_firstTagNotEmitted)
493         {
494             /* we have not yet sent the first element on its way */
495             if (m_firstElementName != null)
496             {
497                 /* this is not the first element, but a later one.
498                  * But we have the old element pending, so flush it out,
499                  * then send this one on its way.
500                  */
501                 flush();
502                 m_handler.startElement(namespaceURI, localName, elementName,  atts);
503             }
504             else
505             {
506                 /* this is the very first element that we have seen,
507                  * so save it for flushing later.  We may yet get to know its
508                  * URI due to added attributes.
509                  */
510 
511                 m_wrapped_handler_not_initialized = true;
512                 m_firstElementName = elementName;
513 
514                 // null if not known
515                 m_firstElementPrefix = getPrefixPartUnknown(elementName);
516 
517                 // null if not known
518                 m_firstElementURI = namespaceURI;
519 
520                 // null if not known
521                 m_firstElementLocalName = localName;
522 
523                 if (m_tracer != null)
524                     firePseudoElement(elementName);
525 
526                 /* we don't want to call our own addAttributes, which
527                  * merely delegates to the wrapped handler, but we want to
528                  * add these attributes to m_attributes. So me must call super.
529                  * addAttributes() In this case m_attributes is only used for the
530                  * first element, after that this class totally delegates to the
531                  * wrapped handler which is either XML or HTML.
532                  */
533                 if (atts != null)
534                     super.addAttributes(atts);
535 
536                 // if there are attributes, then lets make the flush()
537                 // call the startElement on the handler and send the
538                 // attributes on their way.
539                 if (atts != null)
540                     flush();
541 
542             }
543         }
544         else
545         {
546             // this is not the first element, but a later one, so just
547             // send it on its way.
548             m_handler.startElement(namespaceURI, localName, elementName,  atts);
549         }
550     }
551 
552     /**
553      * Pass the call on to the underlying handler
554      * @see ExtendedLexicalHandler#comment(String)
555      */
556     public void comment(String comment) throws SAXException
557     {
558         if (m_firstTagNotEmitted && m_firstElementName != null)
559         {
560             emitFirstTag();
561         }
562         else if (m_needToCallStartDocument)
563         {
564             m_handler.startDocument();
565             m_needToCallStartDocument = false;
566         }
567 
568         m_handler.comment(comment);
569     }
570 
571     /**
572      * Pass the call on to the underlying handler
573      * @see XSLOutputAttributes#getDoctypePublic()
574      */
575     public String getDoctypePublic()
576     {
577 
578         return m_handler.getDoctypePublic();
579     }
580 
581     /**
582      * Pass the call on to the underlying handler
583      * @see XSLOutputAttributes#getDoctypeSystem()
584      */
585     public String getDoctypeSystem()
586     {
587         return m_handler.getDoctypeSystem();
588     }
589 
590     /**
591      * Pass the call on to the underlying handler
592      * @see XSLOutputAttributes#getEncoding()
593      */
594     public String getEncoding()
595     {
596         return m_handler.getEncoding();
597     }
598 
599     /**
600      * Pass the call on to the underlying handler
601      * @see XSLOutputAttributes#getIndent()
602      */
603     public boolean getIndent()
604     {
605         return m_handler.getIndent();
606     }
607 
608     /**
609      * Pass the call on to the underlying handler
610      * @see XSLOutputAttributes#getIndentAmount()
611      */
612     public int getIndentAmount()
613     {
614         return m_handler.getIndentAmount();
615     }
616 
617     /**
618      * Pass the call on to the underlying handler
619      * @see XSLOutputAttributes#getMediaType()
620      */
621     public String getMediaType()
622     {
623         return m_handler.getMediaType();
624     }
625 
626     /**
627      * Pass the call on to the underlying handler
628      * @see XSLOutputAttributes#getOmitXMLDeclaration()
629      */
630     public boolean getOmitXMLDeclaration()
631     {
632         return m_handler.getOmitXMLDeclaration();
633     }
634 
635     /**
636      * Pass the call on to the underlying handler
637      * @see XSLOutputAttributes#getStandalone()
638      */
639     public String getStandalone()
640     {
641         return m_handler.getStandalone();
642     }
643 
644     /**
645      * Pass the call on to the underlying handler
646      * @see XSLOutputAttributes#getVersion()
647      */
648     public String getVersion()
649     {
650         return m_handler.getVersion();
651     }
652 
653     /**
654      * @see XSLOutputAttributes#setDoctype(String, String)
655      */
656     public void setDoctype(String system, String pub)
657     {
658         m_handler.setDoctypePublic(pub);
659         m_handler.setDoctypeSystem(system);
660     }
661 
662     /**
663      * Set the doctype in the underlying XML handler. Remember that this method
664      * was called, just in case we need to transfer this doctype to an HTML handler
665      * @param doctype the public doctype to set
666      * @see XSLOutputAttributes#setDoctypePublic(String)
667      */
668     public void setDoctypePublic(String doctype)
669     {
670         m_handler.setDoctypePublic(doctype);
671         m_setDoctypePublic_called = true;
672     }
673 
674     /**
675      * Set the doctype in the underlying XML handler. Remember that this method
676      * was called, just in case we need to transfer this doctype to an HTML handler
677      * @param doctype the system doctype to set
678      * @see XSLOutputAttributes#setDoctypeSystem(String)
679      */
680     public void setDoctypeSystem(String doctype)
681     {
682         m_handler.setDoctypeSystem(doctype);
683         m_setDoctypeSystem_called = true;
684     }
685 
686     /**
687      * Pass the call on to the underlying handler
688      * @see XSLOutputAttributes#setEncoding(String)
689      */
690     public void setEncoding(String encoding)
691     {
692         m_handler.setEncoding(encoding);
693     }
694 
695     /**
696      * Pass the call on to the underlying handler
697      * @see XSLOutputAttributes#setIndent(boolean)
698      */
699     public void setIndent(boolean indent)
700     {
701         m_handler.setIndent(indent);
702     }
703 
704     /**
705      * Pass the call on to the underlying handler
706      */
707     public void setIndentAmount(int value)
708     {
709         m_handler.setIndentAmount(value);
710     }
711 
712     /**
713      * @see XSLOutputAttributes#setMediaType(String)
714      */
715     public void setMediaType(String mediaType)
716     {
717         m_handler.setMediaType(mediaType);
718         m_setMediaType_called = true;
719     }
720 
721     /**
722      * Pass the call on to the underlying handler
723      * @see XSLOutputAttributes#setOmitXMLDeclaration(boolean)
724      */
725     public void setOmitXMLDeclaration(boolean b)
726     {
727         m_handler.setOmitXMLDeclaration(b);
728     }
729 
730     /**
731      * Pass the call on to the underlying handler
732      * @see XSLOutputAttributes#setStandalone(String)
733      */
734     public void setStandalone(String standalone)
735     {
736         m_handler.setStandalone(standalone);
737     }
738 
739     /**
740      * @see XSLOutputAttributes#setVersion(String)
741      */
742 
743     /**
744      * Pass the call on to the underlying handler
745      * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
746      */
747     public void attributeDecl(
748         String arg0,
749         String arg1,
750         String arg2,
751         String arg3,
752         String arg4)
753         throws SAXException
754     {
755         m_handler.attributeDecl(arg0, arg1, arg2, arg3, arg4);
756     }
757 
758     /**
759      * Pass the call on to the underlying handler
760      * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
761      */
762     public void elementDecl(String arg0, String arg1) throws SAXException
763     {
764         if (m_firstTagNotEmitted)
765         {
766             emitFirstTag();
767         }
768         m_handler.elementDecl(arg0, arg1);
769     }
770 
771     /**
772      * Pass the call on to the underlying handler
773      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
774      */
775     public void externalEntityDecl(
776         String name,
777         String publicId,
778         String systemId)
779         throws SAXException
780     {
781         if (m_firstTagNotEmitted)
782         {
783             flush();
784         }
785         m_handler.externalEntityDecl(name, publicId, systemId);
786     }
787 
788     /**
789      * Pass the call on to the underlying handler
790      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
791      */
792     public void internalEntityDecl(String arg0, String arg1)
793         throws SAXException
794     {
795         if (m_firstTagNotEmitted)
796         {
797             flush();
798         }
799         m_handler.internalEntityDecl(arg0, arg1);
800     }
801 
802     /**
803      * Pass the call on to the underlying handler
804      * @see org.xml.sax.ContentHandler#characters(char[], int, int)
805      */
806     public void characters(char[] characters, int offset, int length)
807         throws SAXException
808     {
809         if (m_firstTagNotEmitted)
810         {
811             flush();
812         }
813 
814         m_handler.characters(characters, offset, length);
815 
816     }
817 
818     /**
819      * Pass the call on to the underlying handler
820      * @see org.xml.sax.ContentHandler#endDocument()
821      */
822     public void endDocument() throws SAXException
823     {
824         if (m_firstTagNotEmitted)
825         {
826             flush();
827         }
828 
829         m_handler.endDocument();
830 
831 
832     }
833 
834     /**
835      * Pass the call on to the underlying handler
836      * @see org.xml.sax.ContentHandler#endElement(String, String, String)
837      */
838     public void endElement(String namespaceURI, String localName, String qName)
839         throws SAXException
840     {
841         if (m_firstTagNotEmitted)
842         {
843             flush();
844             if (namespaceURI == null && m_firstElementURI != null)
845                 namespaceURI = m_firstElementURI;
846 
847 
848             if (localName == null && m_firstElementLocalName != null)
849                 localName = m_firstElementLocalName;
850         }
851 
852         m_handler.endElement(namespaceURI, localName, qName);
853     }
854 
855     /**
856      * Pass the call on to the underlying handler
857      * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
858      */
859     public void endPrefixMapping(String prefix) throws SAXException
860     {
861         m_handler.endPrefixMapping(prefix);
862     }
863 
864     /**
865      * Pass the call on to the underlying handler
866      * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
867      */
868     public void ignorableWhitespace(char[] ch, int start, int length)
869         throws SAXException
870     {
871         if (m_firstTagNotEmitted)
872         {
873             flush();
874         }
875         m_handler.ignorableWhitespace(ch, start, length);
876     }
877 
878     /**
879      * Pass the call on to the underlying handler
880      * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
881      */
882     public void processingInstruction(String target, String data)
883         throws SAXException
884     {
885           if (m_firstTagNotEmitted)
886         {
887             flush();
888         }
889 
890         m_handler.processingInstruction(target, data);
891     }
892 
893     /**
894      * Pass the call on to the underlying handler
895      * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
896      */
897     public void setDocumentLocator(Locator locator)
898     {
899         super.setDocumentLocator(locator);
900         m_handler.setDocumentLocator(locator);
901     }
902 
903     /**
904      * Pass the call on to the underlying handler
905      * @see org.xml.sax.ContentHandler#skippedEntity(String)
906      */
907     public void skippedEntity(String name) throws SAXException
908     {
909         m_handler.skippedEntity(name);
910     }
911 
912 
913 
914     /**
915      * Pass the call on to the underlying handler
916      * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
917      */
918     public void comment(char[] ch, int start, int length) throws SAXException
919     {
920         if (m_firstTagNotEmitted)
921         {
922             flush();
923         }
924 
925         m_handler.comment(ch, start, length);
926     }
927 
928     /**
929      * Pass the call on to the underlying handler
930      * @see org.xml.sax.ext.LexicalHandler#endCDATA()
931      */
932     public void endCDATA() throws SAXException
933     {
934 
935         m_handler.endCDATA();
936     }
937 
938     /**
939      * Pass the call on to the underlying handler
940      * @see org.xml.sax.ext.LexicalHandler#endDTD()
941      */
942     public void endDTD() throws SAXException
943     {
944 
945         m_handler.endDTD();
946     }
947 
948     /**
949      * Pass the call on to the underlying handler
950      * @see org.xml.sax.ext.LexicalHandler#endEntity(String)
951      */
952     public void endEntity(String name) throws SAXException
953     {
954         if (m_firstTagNotEmitted)
955         {
956             emitFirstTag();
957         }
958         m_handler.endEntity(name);
959     }
960 
961     /**
962      * Pass the call on to the underlying handler
963      * @see org.xml.sax.ext.LexicalHandler#startCDATA()
964      */
965     public void startCDATA() throws SAXException
966     {
967         m_handler.startCDATA();
968     }
969 
970     /**
971      * Pass the call on to the underlying handler
972      * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
973      */
974     public void startDTD(String name, String publicId, String systemId)
975         throws SAXException
976     {
977         m_handler.startDTD(name, publicId, systemId);
978     }
979 
980     /**
981      * Pass the call on to the underlying handler
982      * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
983      */
984     public void startEntity(String name) throws SAXException
985     {
986         m_handler.startEntity(name);
987     }
988 
989     /**
990      * Initialize the wrapped output stream (XML or HTML).
991      * If the stream handler should be HTML, then replace the XML handler with
992      * an HTML handler. After than send the starting method calls that were cached
993      * to the wrapped handler.
994      *
995      */
996     private void initStreamOutput() throws SAXException
997     {
998 
999         // Try to rule out if this is an not to be an HTML document based on prefix
1000         boolean firstElementIsHTML = isFirstElemHTML();
1001 
1002         if (firstElementIsHTML)
1003         {
1004             // create an HTML output handler, and initialize it
1005 
1006             // keep a reference to the old handler, ... it will soon be gone
1007             SerializationHandler oldHandler = m_handler;
1008 
1009             /* We have to make sure we get an output properties with the proper
1010              * defaults for the HTML method.  The easiest way to do this is to
1011              * have the OutputProperties class do it.
1012              */
1013 
1014             Properties htmlProperties =
1015                 OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
1016             Serializer serializer =
1017                 SerializerFactory.getSerializer(htmlProperties);
1018 
1019             // The factory should be returning a ToStream
1020             // Don't know what to do if it doesn't
1021             // i.e. the user has over-ridden the content-handler property
1022             // for html
1023             m_handler = (SerializationHandler) serializer;
1024             //m_handler = new ToHTMLStream();
1025 
1026             Writer writer = oldHandler.getWriter();
1027 
1028             if (null != writer)
1029                 m_handler.setWriter(writer);
1030             else
1031             {
1032                 OutputStream os = oldHandler.getOutputStream();
1033 
1034                 if (null != os)
1035                     m_handler.setOutputStream(os);
1036             }
1037 
1038             // need to copy things from the old handler to the new one here
1039 
1040             //            if (_setVersion_called)
1041             //            {
1042             m_handler.setVersion(oldHandler.getVersion());
1043             //            }
1044             //            if (_setDoctypeSystem_called)
1045             //            {
1046             m_handler.setDoctypeSystem(oldHandler.getDoctypeSystem());
1047             //            }
1048             //            if (_setDoctypePublic_called)
1049             //            {
1050             m_handler.setDoctypePublic(oldHandler.getDoctypePublic());
1051             //            }
1052             //            if (_setMediaType_called)
1053             //            {
1054             m_handler.setMediaType(oldHandler.getMediaType());
1055             //            }
1056 
1057             m_handler.setTransformer(oldHandler.getTransformer());
1058         }
1059 
1060         /* Now that we have a real wrapped handler (XML or HTML) lets
1061          * pass any cached calls to it
1062          */
1063         // Call startDocument() if necessary
1064         if (m_needToCallStartDocument)
1065         {
1066             m_handler.startDocument();
1067             m_needToCallStartDocument = false;
1068         }
1069 
1070         // the wrapped handler is now fully initialized
1071         m_wrapped_handler_not_initialized = false;
1072     }
1073 
1074     private void emitFirstTag() throws SAXException
1075     {
1076         if (m_firstElementName != null)
1077         {
1078             if (m_wrapped_handler_not_initialized)
1079             {
1080                 initStreamOutput();
1081                 m_wrapped_handler_not_initialized = false;
1082             }
1083             // Output first tag
1084             m_handler.startElement(m_firstElementURI, null, m_firstElementName, m_attributes);
1085             // don't need the collected attributes of the first element anymore.
1086             m_attributes = null;
1087 
1088             // Output namespaces of first tag
1089             if (m_namespacePrefix != null)
1090             {
1091                 final int n = m_namespacePrefix.size();
1092                 for (int i = 0; i < n; i++)
1093                 {
1094                     final String prefix =
1095                         (String) m_namespacePrefix.elementAt(i);
1096                     final String uri = (String) m_namespaceURI.elementAt(i);
1097                     m_handler.startPrefixMapping(prefix, uri, false);
1098                 }
1099                 m_namespacePrefix = null;
1100                 m_namespaceURI = null;
1101             }
1102             m_firstTagNotEmitted = false;
1103         }
1104     }
1105 
1106     /**
1107      * Utility function for calls to local-name().
1108      *
1109      * Don't want to override static function on SerializerBase
1110      * So added Unknown suffix to method name.
1111      */
1112     private String getLocalNameUnknown(String value)
1113     {
1114         int idx = value.lastIndexOf(':');
1115         if (idx >= 0)
1116             value = value.substring(idx + 1);
1117         idx = value.lastIndexOf('@');
1118         if (idx >= 0)
1119             value = value.substring(idx + 1);
1120         return (value);
1121     }
1122 
1123     /**
1124          * Utility function to return prefix
1125          *
1126          * Don't want to override static function on SerializerBase
1127          * So added Unknown suffix to method name.
1128          */
1129     private String getPrefixPartUnknown(String qname)
1130     {
1131         final int index = qname.indexOf(':');
1132         return (index > 0) ? qname.substring(0, index) : EMPTYSTRING;
1133     }
1134 
1135     /**
1136      * Determine if the firts element in the document is <html> or <HTML>
1137      * This uses the cached first element name, first element prefix and the
1138      * cached namespaces from previous method calls
1139      *
1140      * @return true if the first element is an opening <html> tag
1141      */
1142     private boolean isFirstElemHTML()
1143     {
1144         boolean isHTML;
1145 
1146         // is the first tag html, not considering the prefix ?
1147         isHTML =
1148             getLocalNameUnknown(m_firstElementName).equalsIgnoreCase("html");
1149 
1150         // Try to rule out if this is not to be an HTML document based on URI
1151         if (isHTML
1152             && m_firstElementURI != null
1153             && !EMPTYSTRING.equals(m_firstElementURI))
1154         {
1155             // the <html> element has a non-trivial namespace
1156             isHTML = false;
1157         }
1158         // Try to rule out if this is an not to be an HTML document based on prefix
1159         if (isHTML && m_namespacePrefix != null)
1160         {
1161             /* the first element has a name of "html", but lets check the prefix.
1162              * If the prefix points to a namespace with a URL that is not ""
1163              * then the doecument doesn't start with an <html> tag, and isn't html
1164              */
1165             final int max = m_namespacePrefix.size();
1166             for (int i = 0; i < max; i++)
1167             {
1168                 final String prefix = (String) m_namespacePrefix.elementAt(i);
1169                 final String uri = (String) m_namespaceURI.elementAt(i);
1170 
1171                 if (m_firstElementPrefix != null
1172                     && m_firstElementPrefix.equals(prefix)
1173                     && !EMPTYSTRING.equals(uri))
1174                 {
1175                     // The first element has a prefix, so it can't be <html>
1176                     isHTML = false;
1177                     break;
1178                 }
1179             }
1180 
1181         }
1182         return isHTML;
1183     }
1184     /**
1185      * @see Serializer#asDOMSerializer()
1186      */
1187     public DOMSerializer asDOMSerializer() throws IOException
1188     {
1189         return m_handler.asDOMSerializer();
1190     }
1191 
1192     /**
1193      * @param URI_and_localNames Vector a list of pairs of URI/localName
1194      * specified in the cdata-section-elements attribute.
1195      * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
1196      */
1197     public void setCdataSectionElements(Vector URI_and_localNames)
1198     {
1199         m_handler.setCdataSectionElements(URI_and_localNames);
1200     }
1201     /**
1202      * @see ExtendedContentHandler#addAttributes(org.xml.sax.Attributes)
1203      */
1204     public void addAttributes(Attributes atts) throws SAXException
1205     {
1206         m_handler.addAttributes(atts);
1207     }
1208 
1209     /**
1210      * Get the current namespace mappings.
1211      * Simply returns the mappings of the wrapped handler.
1212      * @see ExtendedContentHandler#getNamespaceMappings()
1213      */
1214     public NamespaceMappings getNamespaceMappings()
1215     {
1216         NamespaceMappings mappings = null;
1217         if (m_handler != null)
1218         {
1219             mappings = m_handler.getNamespaceMappings();
1220         }
1221         return mappings;
1222     }
1223     /**
1224      * @see SerializationHandler#flushPending()
1225      */
1226     public void flushPending() throws SAXException
1227     {
1228 
1229         flush();
1230 
1231         m_handler.flushPending();
1232     }
1233 
1234     private void flush()
1235     {
1236         try
1237         {
1238         if (m_firstTagNotEmitted)
1239         {
1240             emitFirstTag();
1241         }
1242         if (m_needToCallStartDocument)
1243         {
1244             m_handler.startDocument();
1245             m_needToCallStartDocument = false;
1246         }
1247         }
1248         catch(SAXException e)
1249         {
1250             throw new RuntimeException(e.toString());
1251         }
1252 
1253 
1254     }
1255 
1256     /**
1257      * @see ExtendedContentHandler#getPrefix
1258      */
1259     public String getPrefix(String namespaceURI)
1260     {
1261         return m_handler.getPrefix(namespaceURI);
1262     }
1263     /**
1264      * @see ExtendedContentHandler#entityReference(java.lang.String)
1265      */
1266     public void entityReference(String entityName) throws SAXException
1267     {
1268         m_handler.entityReference(entityName);
1269     }
1270 
1271     /**
1272      * @see ExtendedContentHandler#getNamespaceURI(java.lang.String, boolean)
1273      */
1274     public String getNamespaceURI(String qname, boolean isElement)
1275     {
1276         return m_handler.getNamespaceURI(qname, isElement);
1277     }
1278 
1279     public String getNamespaceURIFromPrefix(String prefix)
1280     {
1281         return m_handler.getNamespaceURIFromPrefix(prefix);
1282     }
1283 
1284     public void setTransformer(Transformer t)
1285     {
1286         m_handler.setTransformer(t);
1287         if ((t instanceof SerializerTrace) &&
1288             (((SerializerTrace) t).hasTraceListeners())) {
1289            m_tracer = (SerializerTrace) t;
1290         } else {
1291            m_tracer = null;
1292         }
1293     }
1294     public Transformer getTransformer()
1295     {
1296         return m_handler.getTransformer();
1297     }
1298 
1299     /**
1300      * @see SerializationHandler#setContentHandler(org.xml.sax.ContentHandler)
1301      */
1302     public void setContentHandler(ContentHandler ch)
1303     {
1304         m_handler.setContentHandler(ch);
1305     }
1306     /**
1307      * This method is used to set the source locator, which might be used to
1308      * generated an error message.
1309      * @param locator the source locator
1310      *
1311      * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
1312      */
1313     public void setSourceLocator(SourceLocator locator)
1314     {
1315         m_handler.setSourceLocator(locator);
1316     }
1317 
1318     protected void firePseudoElement(String elementName)
1319     {
1320 
1321         if (m_tracer != null) {
1322             StringBuffer sb = new StringBuffer();
1323 
1324             sb.append('<');
1325             sb.append(elementName);
1326 
1327             // convert the StringBuffer to a char array and
1328             // emit the trace event that these characters "might"
1329             // be written
1330             char ch[] = sb.toString().toCharArray();
1331             m_tracer.fireGenerateEvent(
1332                 SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
1333                 ch,
1334                 0,
1335                 ch.length);
1336         }
1337     }
1338 }