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