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.ArrayList;
  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 ArrayList<String> 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 ArrayList<String> 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 ArrayList<>();
 425                     m_namespaceURI = new ArrayList<>();
 426                 }
 427                 m_namespacePrefix.add(prefix);
 428                 m_namespaceURI.add(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.get(i);
1096                     final String uri = (String) m_namespaceURI.get(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 = m_namespacePrefix.get(i);
1169                 final String uri = m_namespaceURI.get(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(ArrayList<String> 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 }