content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * Copyright 2001-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id: ToHTMLStream.java,v 1.2.4.1 2005/09/15 08:15:26 suresh_emailid Exp $
   */
  package jaxp.sun.org.apache.xml.internal.serializer;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.UnsupportedEncodingException;
  import java.util.Properties;
  
  import jaxp.xml.transform.Result;
  
  import jaxp.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  import jaxp.sun.org.apache.xml.internal.serializer.utils.Utils;
  import org.xml.sax.Attributes;
  import org.xml.sax.SAXException;
  
  /**
   * This serializer takes a series of SAX or
   * SAX-like events and writes its output
   * to the given stream.
   *
   * This class is not a public API, it is public
   * because it is used from another package.
   *
   * @xsl.usage internal
   */
  public final class ToHTMLStream extends ToStream
  {
  
      /** This flag is set while receiving events from the DTD */
      protected boolean m_inDTD = false;
  
      /** True if the current element is a block element.  (seems like
       *  this needs to be a stack. -sb). */
      private boolean m_inBlockElem = false;
  
      /**
       * Map that tells which XML characters should have special treatment, and it
       *  provides character to entity name lookup.
       */
      private static final CharInfo m_htmlcharInfo =
  //        new CharInfo(CharInfo.HTML_ENTITIES_RESOURCE);
          CharInfo.getCharInfo(CharInfo.HTML_ENTITIES_RESOURCE, Method.HTML);
  
      /** A digital search trie for fast, case insensitive lookup of ElemDesc objects. */
      static final Trie m_elementFlags = new Trie();
  
      static {
          initTagReference(m_elementFlags);
      }
      static void initTagReference(Trie m_elementFlags) {
  
          // HTML 4.0 loose DTD
          m_elementFlags.put("BASEFONT", new ElemDesc(0 | ElemDesc.EMPTY));
          m_elementFlags.put(
              "FRAME",
              new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put("FRAMESET", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("NOFRAMES", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put(
              "ISINDEX",
              new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put(
              "APPLET",
              new ElemDesc(0 | ElemDesc.WHITESPACESENSITIVE));
          m_elementFlags.put("CENTER", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("DIR", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("MENU", new ElemDesc(0 | ElemDesc.BLOCK));
  
          // HTML 4.0 strict DTD
          m_elementFlags.put("TT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("I", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("B", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("BIG", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("SMALL", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("EM", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("STRONG", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("DFN", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("CODE", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("SAMP", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("KBD", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("VAR", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("CITE", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("ABBR", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put("ACRONYM", new ElemDesc(0 | ElemDesc.PHRASE));
          m_elementFlags.put(
              "SUP",
              new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
          m_elementFlags.put(
              "SUB",
              new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
          m_elementFlags.put(
              "SPAN",
              new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
          m_elementFlags.put(
              "BDO",
              new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
          m_elementFlags.put(
              "BR",
              new ElemDesc(
                  0
                      | ElemDesc.SPECIAL
                      | ElemDesc.ASPECIAL
                      | ElemDesc.EMPTY
                      | ElemDesc.BLOCK));
          m_elementFlags.put("BODY", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put(
              "ADDRESS",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put(
              "DIV",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put("A", new ElemDesc(0 | ElemDesc.SPECIAL));
          m_elementFlags.put(
              "MAP",
              new ElemDesc(
                  0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL | ElemDesc.BLOCK));
          m_elementFlags.put(
              "AREA",
              new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put(
              "LINK",
              new ElemDesc(
                  0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put(
              "IMG",
              new ElemDesc(
                  0
                      | ElemDesc.SPECIAL
                      | ElemDesc.ASPECIAL
                      | ElemDesc.EMPTY
                      | ElemDesc.WHITESPACESENSITIVE));
          m_elementFlags.put(
              "OBJECT",
              new ElemDesc(
                  0
                      | ElemDesc.SPECIAL
                      | ElemDesc.ASPECIAL
                      | ElemDesc.HEADMISC
                      | ElemDesc.WHITESPACESENSITIVE));
          m_elementFlags.put("PARAM", new ElemDesc(0 | ElemDesc.EMPTY));
          m_elementFlags.put(
              "HR",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET
                      | ElemDesc.EMPTY));
          m_elementFlags.put(
              "P",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put(
              "H1",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "H2",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "H3",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "H4",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "H5",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "H6",
              new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
          m_elementFlags.put(
              "PRE",
              new ElemDesc(0 | ElemDesc.PREFORMATTED | ElemDesc.BLOCK));
          m_elementFlags.put(
              "Q",
              new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
          m_elementFlags.put(
              "BLOCKQUOTE",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put("INS", new ElemDesc(0));
          m_elementFlags.put("DEL", new ElemDesc(0));
          m_elementFlags.put(
              "DL",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put("DT", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("DD", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put(
              "OL",
              new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
          m_elementFlags.put(
              "UL",
              new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
          m_elementFlags.put("LI", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("FORM", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("LABEL", new ElemDesc(0 | ElemDesc.FORMCTRL));
          m_elementFlags.put(
              "INPUT",
              new ElemDesc(
                  0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL | ElemDesc.EMPTY));
          m_elementFlags.put(
              "SELECT",
              new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
          m_elementFlags.put("OPTGROUP", new ElemDesc(0));
          m_elementFlags.put("OPTION", new ElemDesc(0));
          m_elementFlags.put(
              "TEXTAREA",
              new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
          m_elementFlags.put(
              "FIELDSET",
              new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.BLOCKFORM));
          m_elementFlags.put("LEGEND", new ElemDesc(0));
          m_elementFlags.put(
              "BUTTON",
              new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
          m_elementFlags.put(
              "TABLE",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put("CAPTION", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("THEAD", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("TFOOT", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("TBODY", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("COLGROUP", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put(
              "COL",
              new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put("TR", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put("TH", new ElemDesc(0));
          m_elementFlags.put("TD", new ElemDesc(0));
          m_elementFlags.put(
              "HEAD",
              new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.HEADELEM));
          m_elementFlags.put("TITLE", new ElemDesc(0 | ElemDesc.BLOCK));
          m_elementFlags.put(
              "BASE",
              new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put(
              "META",
              new ElemDesc(
                  0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
          m_elementFlags.put(
              "STYLE",
              new ElemDesc(
                  0 | ElemDesc.HEADMISC | ElemDesc.RAW | ElemDesc.BLOCK));
          m_elementFlags.put(
              "SCRIPT",
              new ElemDesc(
                  0
                      | ElemDesc.SPECIAL
                      | ElemDesc.ASPECIAL
                      | ElemDesc.HEADMISC
                      | ElemDesc.RAW));
          m_elementFlags.put(
              "NOSCRIPT",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          m_elementFlags.put("HTML", new ElemDesc(0 | ElemDesc.BLOCK));
  
          // From "John Ky" <hand@syd.speednet.com.au
          // Transitional Document Type Definition ()
          // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/sgml/loosedtd.html#basefont
          m_elementFlags.put("FONT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  
          // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-STRIKE
          m_elementFlags.put("S", new ElemDesc(0 | ElemDesc.FONTSTYLE));
          m_elementFlags.put("STRIKE", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  
          // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-U
          m_elementFlags.put("U", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  
          // From "John Ky" <hand@syd.speednet.com.au
          m_elementFlags.put("NOBR", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  
          // HTML 4.0, section 16.5
          m_elementFlags.put(
              "IFRAME",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
  
          // Netscape 4 extension
          m_elementFlags.put(
              "LAYER",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
          // Netscape 4 extension
          m_elementFlags.put(
              "ILAYER",
              new ElemDesc(
                  0
                      | ElemDesc.BLOCK
                      | ElemDesc.BLOCKFORM
                      | ElemDesc.BLOCKFORMFIELDSET));
  
  
          // NOW FOR ATTRIBUTE INFORMATION . . .
          ElemDesc elemDesc;
  
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("A");
          elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
          elemDesc.setAttr("NAME", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("AREA");
          elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
          elemDesc.setAttr("NOHREF", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("BASE");
          elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("BUTTON");
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("BLOCKQUOTE");
          elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("DEL");
          elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("DIR");
          elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
  
          elemDesc = (ElemDesc) m_elementFlags.get("DIV");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL); // Netscape 4 extension
          elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("DL");
          elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("FORM");
          elemDesc.setAttr("ACTION", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          // Attribution to: "Voytenko, Dimitry" <DVoytenko@SECTORBASE.COM>
          elemDesc = (ElemDesc) m_elementFlags.get("FRAME");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
          elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
          elemDesc.setAttr("NORESIZE",ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("HEAD");
          elemDesc.setAttr("PROFILE", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("HR");
          elemDesc.setAttr("NOSHADE", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          // HTML 4.0, section 16.5
          elemDesc = (ElemDesc) m_elementFlags.get("IFRAME");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
          elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          // Netscape 4 extension
          elemDesc = (ElemDesc) m_elementFlags.get("ILAYER");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("IMG");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
          elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
          elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
          elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("INPUT");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
          elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
          elemDesc.setAttr("CHECKED", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("INS");
          elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          // Netscape 4 extension
          elemDesc = (ElemDesc) m_elementFlags.get("LAYER");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("LINK");
          elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("MENU");
          elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("OBJECT");
          elemDesc.setAttr("CLASSID", ElemDesc.ATTRURL);
          elemDesc.setAttr("CODEBASE", ElemDesc.ATTRURL);
          elemDesc.setAttr("DATA", ElemDesc.ATTRURL);
          elemDesc.setAttr("ARCHIVE", ElemDesc.ATTRURL);
          elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
          elemDesc.setAttr("DECLARE", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("OL");
          elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("OPTGROUP");
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("OPTION");
          elemDesc.setAttr("SELECTED", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("Q");
          elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("SCRIPT");
          elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
          elemDesc.setAttr("FOR", ElemDesc.ATTRURL);
          elemDesc.setAttr("DEFER", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("SELECT");
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("MULTIPLE", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("TABLE");
          elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("TD");
          elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("TEXTAREA");
          elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
          elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("TH");
          elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          // The nowrap attribute of a tr element is both
          // a Netscape and Internet-Explorer extension
          elemDesc = (ElemDesc) m_elementFlags.get("TR");
          elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
  
          // ----------------------------------------------
          elemDesc = (ElemDesc) m_elementFlags.get("UL");
          elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
      }
  
      /**
       * Dummy element for elements not found.
       */
      static private final ElemDesc m_dummy = new ElemDesc(0 | ElemDesc.BLOCK);
  
      /** True if URLs should be specially escaped with the %xx form. */
      private boolean m_specialEscapeURLs = true;
  
      /** True if the META tag should be omitted. */
      private boolean m_omitMetaTag = false;
  
      /**
       * Tells if the formatter should use special URL escaping.
       *
       * @param bool True if URLs should be specially escaped with the %xx form.
       */
      public void setSpecialEscapeURLs(boolean bool)
      {
          m_specialEscapeURLs = bool;
      }
  
      /**
       * Tells if the formatter should omit the META tag.
       *
       * @param bool True if the META tag should be omitted.
       */
      public void setOmitMetaTag(boolean bool)
      {
          m_omitMetaTag = bool;
      }
  
      /**
       * Specifies an output format for this serializer. It the
       * serializer has already been associated with an output format,
       * it will switch to the new format. This method should not be
       * called while the serializer is in the process of serializing
       * a document.
       *
       * This method can be called multiple times before starting
       * the serialization of a particular result-tree. In principle
       * all serialization parameters can be changed, with the exception
       * of method="html" (it must be method="html" otherwise we
       * shouldn't even have a ToHTMLStream object here!)
       *
       * @param format The output format or serialzation parameters
       * to use.
       */
      public void setOutputFormat(Properties format)
      {
  
          m_specialEscapeURLs =
              OutputPropertyUtils.getBooleanProperty(
                  OutputPropertiesFactory.S_USE_URL_ESCAPING,
                  format);
  
          m_omitMetaTag =
              OutputPropertyUtils.getBooleanProperty(
                  OutputPropertiesFactory.S_OMIT_META_TAG,
                  format);
  
          super.setOutputFormat(format);
      }
  
      /**
       * Tells if the formatter should use special URL escaping.
       *
       * @return True if URLs should be specially escaped with the %xx form.
       */
      private final boolean getSpecialEscapeURLs()
      {
          return m_specialEscapeURLs;
      }
  
      /**
       * Tells if the formatter should omit the META tag.
       *
       * @return True if the META tag should be omitted.
       */
      private final boolean getOmitMetaTag()
      {
          return m_omitMetaTag;
      }
  
      /**
       * Get a description of the given element.
       *
       * @param name non-null name of element, case insensitive.
       *
       * @return non-null reference to ElemDesc, which may be m_dummy if no
       *         element description matches the given name.
       */
      public static final ElemDesc getElemDesc(String name)
      {
          /* this method used to return m_dummy  when name was null
           * but now it doesn't check and and requires non-null name.
           */
          Object obj = m_elementFlags.get(name);
          if (null != obj)
              return (ElemDesc)obj;
          return m_dummy;
      }
  
      /**
       * A Trie that is just a copy of the "static" one.
       * We need this one to be able to use the faster, but not thread-safe
       * method Trie.get2(name)
       */
      private Trie m_htmlInfo = new Trie(m_elementFlags);
      /**
       * Calls to this method could be replaced with calls to
       * getElemDesc(name), but this one should be faster.
       */
      private ElemDesc getElemDesc2(String name)
      {
          Object obj = m_htmlInfo.get2(name);
          if (null != obj)
              return (ElemDesc)obj;
          return m_dummy;
      }
  
      /**
       * Default constructor.
       */
      public ToHTMLStream()
      {
  
          super();
          m_charInfo = m_htmlcharInfo;
          // initialize namespaces
          m_prefixMap = new NamespaceMappings();
  
      }
  
      /** The name of the current element. */
  //    private String m_currentElementName = null;
  
      /**
       * Receive notification of the beginning of a document.
       *
       * @throws org.xml.sax.SAXException Any SAX exception, possibly
       *            wrapping another exception.
       *
       * @throws org.xml.sax.SAXException
       */
      protected void startDocumentInternal() throws org.xml.sax.SAXException
      {
          super.startDocumentInternal();
  
          m_needToCallStartDocument = false;
          m_needToOutputDocTypeDecl = true;
          m_startNewLine = false;
          setOmitXMLDeclaration(true);
  
          if (true == m_needToOutputDocTypeDecl)
          {
              String doctypeSystem = getDoctypeSystem();
              String doctypePublic = getDoctypePublic();
              if ((null != doctypeSystem) || (null != doctypePublic))
              {
                  final java.io.Writer writer = m_writer;
                  try
                  {
                  writer.write("<!DOCTYPE html");
  
                  if (null != doctypePublic)
                  {
                      writer.write(" PUBLIC \"");
                      writer.write(doctypePublic);
                      writer.write('"');
                  }
  
                  if (null != doctypeSystem)
                  {
                      if (null == doctypePublic)
                          writer.write(" SYSTEM \"");
                      else
                          writer.write(" \"");
  
                      writer.write(doctypeSystem);
                      writer.write('"');
                  }
  
                  writer.write('>');
                  outputLineSep();
                  }
                  catch(IOException e)
                  {
                      throw new SAXException(e);
                  }
              }
          }
  
          m_needToOutputDocTypeDecl = false;
      }
  
      /**
       * Receive notification of the end of a document.
       *
       * @throws org.xml.sax.SAXException Any SAX exception, possibly
       *            wrapping another exception.
       *
       * @throws org.xml.sax.SAXException
       */
      public final void endDocument() throws org.xml.sax.SAXException
      {
  
          flushPending();
          if (m_doIndent && !m_isprevtext)
          {
              try
              {
              outputLineSep();
              }
              catch(IOException e)
              {
                  throw new SAXException(e);
              }
          }
  
          flushWriter();
          if (m_tracer != null)
              super.fireEndDoc();
      }
  
      /**
       *  Receive notification of the beginning of an element.
       *
       *
       *  @param namespaceURI
       *  @param localName
       *  @param name The element type name.
       *  @param atts The attributes attached to the element, if any.
       *  @throws org.xml.sax.SAXException Any SAX exception, possibly
       *             wrapping another exception.
       *  @see #endElement
       *  @see org.xml.sax.AttributeList
       */
      public void startElement(
          String namespaceURI,
          String localName,
          String name,
          Attributes atts)
          throws org.xml.sax.SAXException
      {
  
          ElemContext elemContext = m_elemContext;
  
          // clean up any pending things first
          if (elemContext.m_startTagOpen)
          {
              closeStartTag();
              elemContext.m_startTagOpen = false;
          }
          else if (m_cdataTagOpen)
          {
              closeCDATA();
              m_cdataTagOpen = false;
          }
          else if (m_needToCallStartDocument)
          {
              startDocumentInternal();
              m_needToCallStartDocument = false;
          }
  
  
          // if this element has a namespace then treat it like XML
          if (null != namespaceURI && namespaceURI.length() > 0)
          {
              super.startElement(namespaceURI, localName, name, atts);
  
              return;
          }
  
          try
          {
              // getElemDesc2(name) is faster than getElemDesc(name)
              ElemDesc elemDesc = getElemDesc2(name);
              int elemFlags = elemDesc.getFlags();
  
              // deal with indentation issues first
              if (m_doIndent)
              {
  
                  boolean isBlockElement = (elemFlags & ElemDesc.BLOCK) != 0;
                  if (m_ispreserve)
                      m_ispreserve = false;
                  else if (
                      (null != elemContext.m_elementName)
                      && (!m_inBlockElem
                          || isBlockElement) /* && !isWhiteSpaceSensitive */
                      )
                  {
                      m_startNewLine = true;
  
                      indent();
  
                  }
                  m_inBlockElem = !isBlockElement;
              }
  
              // save any attributes for later processing
              if (atts != null)
                  addAttributes(atts);
  
              m_isprevtext = false;
              final java.io.Writer writer = m_writer;
              writer.write('<');
              writer.write(name);
  
  
  
              if (m_tracer != null)
                  firePseudoAttributes();
  
              if ((elemFlags & ElemDesc.EMPTY) != 0)
              {
                  // an optimization for elements which are expected
                  // to be empty.
                  m_elemContext = elemContext.push();
                  /* XSLTC sometimes calls namespaceAfterStartElement()
                   * so we need to remember the name
                   */
                  m_elemContext.m_elementName = name;
                  m_elemContext.m_elementDesc = elemDesc;
                  return;
              }
              else
              {
                  elemContext = elemContext.push(namespaceURI,localName,name);
                  m_elemContext = elemContext;
                  elemContext.m_elementDesc = elemDesc;
                  elemContext.m_isRaw = (elemFlags & ElemDesc.RAW) != 0;
              }
  
  
              if ((elemFlags & ElemDesc.HEADELEM) != 0)
              {
                  // This is the <HEAD> element, do some special processing
                  closeStartTag();
                  elemContext.m_startTagOpen = false;
                  if (!m_omitMetaTag)
                  {
                      if (m_doIndent)
                          indent();
                      writer.write(
                          "<META http-equiv=\"Content-Type\" content=\"text/html; charset=");
                      String encoding = getEncoding();
                      String encode = Encodings.getMimeEncoding(encoding);
                      writer.write(encode);
                      writer.write("\">");
                  }
              }
          }
          catch (IOException e)
          {
              throw new SAXException(e);
          }
      }
  
      /**
       *  Receive notification of the end of an element.
       *
       *
       *  @param namespaceURI
       *  @param localName
       *  @param name The element type name
       *  @throws org.xml.sax.SAXException Any SAX exception, possibly
       *             wrapping another exception.
       */
      public final void endElement(
          final String namespaceURI,
          final String localName,
          final String name)
          throws org.xml.sax.SAXException
      {
          // deal with any pending issues
          if (m_cdataTagOpen)
              closeCDATA();
  
          // if the element has a namespace, treat it like XML, not HTML
          if (null != namespaceURI && namespaceURI.length() > 0)
          {
              super.endElement(namespaceURI, localName, name);
  
              return;
          }
  
          try
          {
  
              ElemContext elemContext = m_elemContext;
              final ElemDesc elemDesc = elemContext.m_elementDesc;
              final int elemFlags = elemDesc.getFlags();
              final boolean elemEmpty = (elemFlags & ElemDesc.EMPTY) != 0;
  
              // deal with any indentation issues
              if (m_doIndent)
              {
                  final boolean isBlockElement = (elemFlags&ElemDesc.BLOCK) != 0;
                  boolean shouldIndent = false;
  
                  if (m_ispreserve)
                  {
                      m_ispreserve = false;
                  }
                  else if (m_doIndent && (!m_inBlockElem || isBlockElement))
                  {
                      m_startNewLine = true;
                      shouldIndent = true;
                  }
                  if (!elemContext.m_startTagOpen && shouldIndent)
                      indent(elemContext.m_currentElemDepth - 1);
                  m_inBlockElem = !isBlockElement;
              }
  
              final java.io.Writer writer = m_writer;
              if (!elemContext.m_startTagOpen)
              {
                  writer.write("</");
                  writer.write(name);
                  writer.write('>');
              }
              else
              {
                  // the start-tag open when this method was called,
                  // so we need to process it now.
  
                  if (m_tracer != null)
                      super.fireStartElem(name);
  
                  // the starting tag was still open when we received this endElement() call
                  // so we need to process any gathered attributes NOW, before they go away.
                  int nAttrs = m_attributes.getLength();
                  if (nAttrs > 0)
                  {
                      processAttributes(m_writer, nAttrs);
                      // clear attributes object for re-use with next element
                      m_attributes.clear();
                  }
                  if (!elemEmpty)
                  {
                      // As per Dave/Paul recommendation 12/06/2000
                      // if (shouldIndent)
                      // writer.write('>');
                      //  indent(m_currentIndent);
  
                      writer.write("></");
                      writer.write(name);
                      writer.write('>');
                  }
                  else
                  {
                      writer.write('>');
                  }
              }
  
              // clean up because the element has ended
              if ((elemFlags & ElemDesc.WHITESPACESENSITIVE) != 0)
                  m_ispreserve = true;
              m_isprevtext = false;
  
              // fire off the end element event
              if (m_tracer != null)
                  super.fireEndElem(name);
  
              // OPTIMIZE-EMPTY
              if (elemEmpty)
              {
                  // a quick exit if the HTML element had no children.
                  // This block of code can be removed if the corresponding block of code
                  // in startElement() also labeled with "OPTIMIZE-EMPTY" is also removed
                  m_elemContext = elemContext.m_prev;
                  return;
              }
  
              // some more clean because the element has ended.
              if (!elemContext.m_startTagOpen)
              {
                  if (m_doIndent && !m_preserves.isEmpty())
                      m_preserves.pop();
              }
              m_elemContext = elemContext.m_prev;
  //            m_isRawStack.pop();
          }
          catch (IOException e)
          {
              throw new SAXException(e);
          }
      }
  
      /**
       * Process an attribute.
       * @param   writer The writer to write the processed output to.
       * @param   name   The name of the attribute.
       * @param   value   The value of the attribute.
       * @param   elemDesc The description of the HTML element
       *           that has this attribute.
       *
       * @throws org.xml.sax.SAXException
       */
      protected void processAttribute(
          java.io.Writer writer,
          String name,
          String value,
          ElemDesc elemDesc)
          throws IOException
      {
          writer.write(' ');
  
          if (   ((value.length() == 0) || value.equalsIgnoreCase(name))
              && elemDesc != null
              && elemDesc.isAttrFlagSet(name, ElemDesc.ATTREMPTY))
          {
              writer.write(name);
          }
          else
          {
              // %REVIEW% %OPT%
              // Two calls to single-char write may NOT
              // be more efficient than one to string-write...
              writer.write(name);
              writer.write("=\"");
              if (   elemDesc != null
                  && elemDesc.isAttrFlagSet(name, ElemDesc.ATTRURL))
                  writeAttrURI(writer, value, m_specialEscapeURLs);
              else
                  writeAttrString(writer, value, this.getEncoding());
              writer.write('"');
  
          }
      }
  
      /**
       * Tell if a character is an ASCII digit.
       */
      private boolean isASCIIDigit(char c)
      {
          return (c >= '0' && c <= '9');
      }
  
      /**
       * Make an integer into an HH hex value.
       * Does no checking on the size of the input, since this
       * is only meant to be used locally by writeAttrURI.
       *
       * @param i must be a value less than 255.
       *
       * @return should be a two character string.
       */
      private static String makeHHString(int i)
      {
          String s = Integer.toHexString(i).toUpperCase();
          if (s.length() == 1)
          {
              s = "0" + s;
          }
          return s;
      }
  
      /**
      * Dmitri Ilyin: Makes sure if the String is HH encoded sign.
      * @param str must be 2 characters long
      *
      * @return true or false
      */
      private boolean isHHSign(String str)
      {
          boolean sign = true;
          try
          {
              char r = (char) Integer.parseInt(str, 16);
          }
          catch (NumberFormatException e)
          {
              sign = false;
          }
          return sign;
      }
  
      /**
       * Write the specified <var>string</var> after substituting non ASCII characters,
       * with <CODE>%HH</CODE>, where HH is the hex of the byte value.
       *
       * @param   string      String to convert to XML format.
       * @param doURLEscaping True if we should try to encode as
       *                      per http://www.ietf.org/rfc/rfc2396.txt.
       *
       * @throws org.xml.sax.SAXException if a bad surrogate pair is detected.
       */
      public void writeAttrURI(
          final java.io.Writer writer, String string, boolean doURLEscaping)
          throws IOException
      {
          // http://www.ietf.org/rfc/rfc2396.txt says:
          // A URI is always in an "escaped" form, since escaping or unescaping a
          // completed URI might change its semantics.  Normally, the only time
          // escape encodings can safely be made is when the URI is being created
          // from its component parts; each component may have its own set of
          // characters that are reserved, so only the mechanism responsible for
          // generating or interpreting that component can determine whether or
          // not escaping a character will change its semantics. Likewise, a URI
          // must be separated into its components before the escaped characters
          // within those components can be safely decoded.
          //
          // ...So we do our best to do limited escaping of the URL, without
          // causing damage.  If the URL is already properly escaped, in theory, this
          // function should not change the string value.
  
          final int end = string.length();
          if (end > m_attrBuff.length)
          {
             m_attrBuff = new char[end*2 + 1];
          }
          string.getChars(0,end, m_attrBuff, 0);
          final char[] chars = m_attrBuff;
  
          int cleanStart = 0;
          int cleanLength = 0;
  
  
          char ch = 0;
          for (int i = 0; i < end; i++)
          {
              ch = chars[i];
  
              if ((ch < 32) || (ch > 126))
              {
                  if (cleanLength > 0)
                  {
                      writer.write(chars, cleanStart, cleanLength);
                      cleanLength = 0;
                  }
                  if (doURLEscaping)
                  {
                      // Encode UTF16 to UTF8.
                      // Reference is Unicode, A Primer, by Tony Graham.
                      // Page 92.
  
                      // Note that Kay doesn't escape 0x20...
                      //  if(ch == 0x20) // Not sure about this... -sb
                      //  {
                      //    writer.write(ch);
                      //  }
                      //  else
                      if (ch <= 0x7F)
                      {
                          writer.write('%');
                          writer.write(makeHHString(ch));
                      }
                      else if (ch <= 0x7FF)
                      {
                          // Clear low 6 bits before rotate, put high 4 bits in low byte,
                          // and set two high bits.
                          int high = (ch >> 6) | 0xC0;
                          int low = (ch & 0x3F) | 0x80;
                          // First 6 bits, + high bit
                          writer.write('%');
                          writer.write(makeHHString(high));
                          writer.write('%');
                          writer.write(makeHHString(low));
                      }
                      else if (Encodings.isHighUTF16Surrogate(ch)) // high surrogate
                      {
                          // I'm sure this can be done in 3 instructions, but I choose
                          // to try and do it exactly like it is done in the book, at least
                          // until we are sure this is totally clean.  I don't think performance
                          // is a big issue with this particular function, though I could be
                          // wrong.  Also, the stuff below clearly does more masking than
                          // it needs to do.
  
                          // Clear high 6 bits.
                          int highSurrogate = ((int) ch) & 0x03FF;
  
                          // Middle 4 bits (wwww) + 1
                          // "Note that the value of wwww from the high surrogate bit pattern
                          // is incremented to make the uuuuu bit pattern in the scalar value
                          // so the surrogate pair don't address the BMP."
                          int wwww = ((highSurrogate & 0x03C0) >> 6);
                          int uuuuu = wwww + 1;
  
                          // next 4 bits
                          int zzzz = (highSurrogate & 0x003C) >> 2;
  
                          // low 2 bits
                          int yyyyyy = ((highSurrogate & 0x0003) << 4) & 0x30;
  
                          // Get low surrogate character.
                          ch = chars[++i];
  
                          // Clear high 6 bits.
                          int lowSurrogate = ((int) ch) & 0x03FF;
  
                          // put the middle 4 bits into the bottom of yyyyyy (byte 3)
                          yyyyyy = yyyyyy | ((lowSurrogate & 0x03C0) >> 6);
  
                          // bottom 6 bits.
                          int xxxxxx = (lowSurrogate & 0x003F);
  
                          int byte1 = 0xF0 | (uuuuu >> 2); // top 3 bits of uuuuu
                          int byte2 =
                              0x80 | (((uuuuu & 0x03) << 4) & 0x30) | zzzz;
                          int byte3 = 0x80 | yyyyyy;
                          int byte4 = 0x80 | xxxxxx;
  
                          writer.write('%');
                          writer.write(makeHHString(byte1));
                          writer.write('%');
                          writer.write(makeHHString(byte2));
                          writer.write('%');
                          writer.write(makeHHString(byte3));
                          writer.write('%');
                          writer.write(makeHHString(byte4));
                      }
                      else
                      {
                          int high = (ch >> 12) | 0xE0; // top 4 bits
                          int middle = ((ch & 0x0FC0) >> 6) | 0x80;
                          // middle 6 bits
                          int low = (ch & 0x3F) | 0x80;
                          // First 6 bits, + high bit
                          writer.write('%');
                          writer.write(makeHHString(high));
                          writer.write('%');
                          writer.write(makeHHString(middle));
                          writer.write('%');
                          writer.write(makeHHString(low));
                      }
  
                  }
                  else if (escapingNotNeeded(ch))
                  {
                      writer.write(ch);
                  }
                  else
                  {
                      writer.write("&#");
                      writer.write(Integer.toString(ch));
                      writer.write(';');
                  }
                  // In this character range we have first written out any previously accumulated
                  // "clean" characters, then processed the current more complicated character,
                  // which may have incremented "i".
                  // We now we reset the next possible clean character.
                  cleanStart = i + 1;
              }
              // Since http://www.ietf.org/rfc/rfc2396.txt refers to the URI grammar as
              // not allowing quotes in the URI proper syntax, nor in the fragment
              // identifier, we believe that it's OK to double escape quotes.
              else if (ch == '"')
              {
                  // If the character is a '%' number number, try to avoid double-escaping.
                  // There is a question if this is legal behavior.
  
                  // Dmitri Ilyin: to check if '%' number number is invalid. It must be checked if %xx is a sign, that would be encoded
                  // The encoded signes are in Hex form. So %xx my be in form %3C that is "<" sign. I will try to change here a little.
  
                  //        if( ((i+2) < len) && isASCIIDigit(stringArray[i+1]) && isASCIIDigit(stringArray[i+2]) )
  
                  // We are no longer escaping '%'
  
                  if (cleanLength > 0)
                  {
                      writer.write(chars, cleanStart, cleanLength);
                      cleanLength = 0;
                  }
  
  
                  // Mike Kay encodes this as &#34;, so he may know something I don't?
                  if (doURLEscaping)
                      writer.write("%22");
                  else
                      writer.write("&quot;"); // we have to escape this, I guess.
  
                  // We have written out any clean characters, then the escaped '%' and now we
                  // We now we reset the next possible clean character.
                  cleanStart = i + 1;
              }
              else if (ch == '&')
              {
                  // HTML 4.01 reads, "Authors should use "&amp;" (ASCII decimal 38)
                  // instead of "&" to avoid confusion with the beginning of a character
                  // reference (entity reference open delimiter).
                  if (cleanLength > 0)
                  {
                      writer.write(chars, cleanStart, cleanLength);
                      cleanLength = 0;
                  }
                  writer.write("&amp;");
                  cleanStart = i + 1;
              }
              else
              {
                  // no processing for this character, just count how
                  // many characters in a row that we have that need no processing
                  cleanLength++;
              }
          }
  
          // are there any clean characters at the end of the array
          // that we haven't processed yet?
          if (cleanLength > 1)
          {
              // if the whole string can be written out as-is do so
              // otherwise write out the clean chars at the end of the
              // array
              if (cleanStart == 0)
                  writer.write(string);
              else
                  writer.write(chars, cleanStart, cleanLength);
          }
          else if (cleanLength == 1)
          {
              // a little optimization for 1 clean character
              // (we could have let the previous if(...) handle them all)
              writer.write(ch);
          }
      }
  
      /**
       * Writes the specified <var>string</var> after substituting <VAR>specials</VAR>,
       * and UTF-16 surrogates for character references <CODE>&amp;#xnn</CODE>.
       *
       * @param   string      String to convert to XML format.
       * @param   encoding    CURRENTLY NOT IMPLEMENTED.
       *
       * @throws org.xml.sax.SAXException
       */
      public void writeAttrString(
          final java.io.Writer writer, String string, String encoding)
          throws IOException
      {
          final int end = string.length();
          if (end > m_attrBuff.length)
          {
              m_attrBuff = new char[end * 2 + 1];
          }
          string.getChars(0, end, m_attrBuff, 0);
          final char[] chars = m_attrBuff;
  
  
  
          int cleanStart = 0;
          int cleanLength = 0;
  
          char ch = 0;
          for (int i = 0; i < end; i++)
          {
              ch = chars[i];
  
              // System.out.println("SPECIALSSIZE: "+SPECIALSSIZE);
              // System.out.println("ch: "+(int)ch);
              // System.out.println("m_maxCharacter: "+(int)m_maxCharacter);
              // System.out.println("m_attrCharsMap[ch]: "+(int)m_attrCharsMap[ch]);
              if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch)))
              {
                  cleanLength++;
              }
              else if ('<' == ch || '>' == ch)
              {
                  cleanLength++; // no escaping in this case, as specified in 15.2
              }
              else if (
                  ('&' == ch) && ((i + 1) < end) && ('{' == chars[i + 1]))
              {
                  cleanLength++; // no escaping in this case, as specified in 15.2
              }
              else
              {
                  if (cleanLength > 0)
                  {
                      writer.write(chars,cleanStart,cleanLength);
                      cleanLength = 0;
                  }
                  int pos = accumDefaultEntity(writer, ch, i, chars, end, false, true);
  
                  if (i != pos)
                  {
                      i = pos - 1;
                  }
                  else
                  {
                      if (Encodings.isHighUTF16Surrogate(ch))
                      {
  
                              writeUTF16Surrogate(ch, chars, i, end);
                              i++; // two input characters processed
                                   // this increments by one and the for()
                                   // loop itself increments by another one.
                      }
  
                      // The next is kind of a hack to keep from escaping in the case
                      // of Shift_JIS and the like.
  
                      /*
                      else if ((ch < m_maxCharacter) && (m_maxCharacter == 0xFFFF)
                      && (ch != 160))
                      {
                      writer.write(ch);  // no escaping in this case
                      }
                      else
                      */
                      String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
                      if (null != outputStringForChar)
                      {
                          writer.write(outputStringForChar);
                      }
                      else if (escapingNotNeeded(ch))
                      {
                          writer.write(ch); // no escaping in this case
                      }
                      else
                      {
                          writer.write("&#");
                          writer.write(Integer.toString(ch));
                          writer.write(';');
                      }
                  }
                  cleanStart = i + 1;
              }
          } // end of for()
  
          // are there any clean characters at the end of the array
          // that we haven't processed yet?
          if (cleanLength > 1)
          {
              // if the whole string can be written out as-is do so
              // otherwise write out the clean chars at the end of the
              // array
              if (cleanStart == 0)
                  writer.write(string);
              else
                  writer.write(chars, cleanStart, cleanLength);
          }
          else if (cleanLength == 1)
          {
              // a little optimization for 1 clean character
              // (we could have let the previous if(...) handle them all)
              writer.write(ch);
          }
      }
  
  
  
      /**
       * Receive notification of character data.
       *
       * <p>The Parser will call this method to report each chunk of
       * character data.  SAX parsers may return all contiguous character
       * data in a single chunk, or they may split it into several
       * chunks; however, all of the characters in any single event
       * must come from the same external entity, so that the Locator
       * provides useful information.</p>
       *
       * <p>The application must not attempt to read from the array
       * outside of the specified range.</p>
       *
       * <p>Note that some parsers will report whitespace using the
       * ignorableWhitespace() method rather than this one (validating
       * parsers must do so).</p>
       *
       * @param chars The characters from the XML document.
       * @param start The start position in the array.
       * @param length The number of characters to read from the array.
       * @throws org.xml.sax.SAXException Any SAX exception, possibly
       *            wrapping another exception.
       * @see #ignorableWhitespace
       * @see org.xml.sax.Locator
       *
       * @throws org.xml.sax.SAXException
       */
      public final void characters(char chars[], int start, int length)
          throws org.xml.sax.SAXException
      {
  
          if (m_elemContext.m_isRaw)
          {
              try
              {
                  if (m_elemContext.m_startTagOpen)
                  {
                      closeStartTag();
                      m_elemContext.m_startTagOpen = false;
                  }
                  m_ispreserve = true;
  
  //              With m_ispreserve just set true it looks like shouldIndent()
  //              will always return false, so drop any possible indentation.
  //              if (shouldIndent())
  //                  indent();
  
                  // writer.write("<![CDATA[");
                  // writer.write(chars, start, length);
                  writeNormalizedChars(chars, start, length, false, m_lineSepUse);
  
                  // writer.write("]]>");
  
                  // time to generate characters event
                  if (m_tracer != null)
                      super.fireCharEvent(chars, start, length);
  
                  return;
              }
              catch (IOException ioe)
              {
                  throw new org.xml.sax.SAXException(
                      Utils.messages.createMessage(
                          MsgKey.ER_OIERROR,
                          null),
                      ioe);
                  //"IO error", ioe);
              }
          }
          else
          {
              super.characters(chars, start, length);
          }
      }
  
      /**
       *  Receive notification of cdata.
       *
       *  <p>The Parser will call this method to report each chunk of
       *  character data.  SAX parsers may return all contiguous character
       *  data in a single chunk, or they may split it into several
       *  chunks; however, all of the characters in any single event
       *  must come from the same external entity, so that the Locator
       *  provides useful information.</p>
       *
       *  <p>The application must not attempt to read from the array
       *  outside of the specified range.</p>
       *
       *  <p>Note that some parsers will report whitespace using the
       *  ignorableWhitespace() method rather than this one (validating
       *  parsers must do so).</p>
       *
       *  @param ch The characters from the XML document.
       *  @param start The start position in the array.
       *  @param length The number of characters to read from the array.
       *  @throws org.xml.sax.SAXException Any SAX exception, possibly
       *             wrapping another exception.
       *  @see #ignorableWhitespace
       *  @see org.xml.sax.Locator
       *
       * @throws org.xml.sax.SAXException
       */
      public final void cdata(char ch[], int start, int length)
          throws org.xml.sax.SAXException
      {
  
          if ((null != m_elemContext.m_elementName)
              && (m_elemContext.m_elementName.equalsIgnoreCase("SCRIPT")
                  || m_elemContext.m_elementName.equalsIgnoreCase("STYLE")))
          {
              try
              {
                  if (m_elemContext.m_startTagOpen)
                  {
                      closeStartTag();
                      m_elemContext.m_startTagOpen = false;
                  }
  
                  m_ispreserve = true;
  
                  if (shouldIndent())
                      indent();
  
                  // writer.write(ch, start, length);
                  writeNormalizedChars(ch, start, length, true, m_lineSepUse);
              }
              catch (IOException ioe)
              {
                  throw new org.xml.sax.SAXException(
                      Utils.messages.createMessage(
                          MsgKey.ER_OIERROR,
                          null),
                      ioe);
                  //"IO error", ioe);
              }
          }
          else
          {
              super.cdata(ch, start, length);
          }
      }
  
      /**
       *  Receive notification of a processing instruction.
       *
       *  @param target The processing instruction target.
       *  @param data The processing instruction data, or null if
       *         none was supplied.
       *  @throws org.xml.sax.SAXException Any SAX exception, possibly
       *             wrapping another exception.
       *
       * @throws org.xml.sax.SAXException
       */
      public void processingInstruction(String target, String data)
          throws org.xml.sax.SAXException
      {
  
          // Process any pending starDocument and startElement first.
          flushPending();
  
          // Use a fairly nasty hack to tell if the next node is supposed to be
          // unescaped text.
          if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
          {
              startNonEscaping();
          }
          else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
          {
              endNonEscaping();
          }
          else
          {
              try
              {
              if (m_elemContext.m_startTagOpen)
              {
                  closeStartTag();
                  m_elemContext.m_startTagOpen = false;
              }
              else if (m_needToCallStartDocument)
                  startDocumentInternal();
  
              if (shouldIndent())
                  indent();
  
              final java.io.Writer writer = m_writer;
              //writer.write("<?" + target);
              writer.write("<?");
              writer.write(target);
  
              if (data.length() > 0 && !Character.isSpaceChar(data.charAt(0)))
                  writer.write(' ');
  
              //writer.write(data + ">"); // different from XML
              writer.write(data); // different from XML
              writer.write('>'); // different from XML
  
              // Always output a newline char if not inside of an
              // element. The whitespace is not significant in that
              // case.
              if (m_elemContext.m_currentElemDepth <= 0)
                  outputLineSep();
  
              m_startNewLine = true;
              }
              catch(IOException e)
              {
                  throw new SAXException(e);
              }
          }
  
          // now generate the PI event
          if (m_tracer != null)
              super.fireEscapingEvent(target, data);
       }
  
      /**
       * Receive notivication of a entityReference.
       *
       * @param name non-null reference to entity name string.
       *
       * @throws org.xml.sax.SAXException
       */
      public final void entityReference(String name)
          throws org.xml.sax.SAXException
      {
          try
          {
  
          final java.io.Writer writer = m_writer;
          writer.write('&');
          writer.write(name);
          writer.write(';');
  
          } catch(IOException e)
          {
              throw new SAXException(e);
          }
      }
      /**
       * @see ExtendedContentHandler#endElement(String)
       */
      public final void endElement(String elemName) throws SAXException
      {
          endElement(null, null, elemName);
      }
  
      /**
       * Process the attributes, which means to write out the currently
       * collected attributes to the writer. The attributes are not
       * cleared by this method
       *
       * @param writer the writer to write processed attributes to.
       * @param nAttrs the number of attributes in m_attributes
       * to be processed
       *
       * @throws org.xml.sax.SAXException
       */
      public void processAttributes(java.io.Writer writer, int nAttrs)
          throws IOException,SAXException
      {
              /*
               * process the collected attributes
               */
              for (int i = 0; i < nAttrs; i++)
              {
                  processAttribute(
                      writer,
                      m_attributes.getQName(i),
                      m_attributes.getValue(i),
                      m_elemContext.m_elementDesc);
              }
      }
  
      /**
       * For the enclosing elements starting tag write out out any attributes
       * followed by ">"
       *
       *@throws org.xml.sax.SAXException
       */
      protected void closeStartTag() throws SAXException
      {
              try
              {
  
              // finish processing attributes, time to fire off the start element event
              if (m_tracer != null)
                  super.fireStartElem(m_elemContext.m_elementName);
  
              int nAttrs = m_attributes.getLength();
              if (nAttrs>0)
              {
                  processAttributes(m_writer, nAttrs);
                  // clear attributes object for re-use with next element
                  m_attributes.clear();
              }
  
              m_writer.write('>');
  
              /* whether Xalan or XSLTC, we have the prefix mappings now, so
               * lets determine if the current element is specified in the cdata-
               * section-elements list.
               */
              if (m_cdataSectionElements != null)
                  m_elemContext.m_isCdataSection = isCdataSection();
              if (m_doIndent)
              {
                  m_isprevtext = false;
                  m_preserves.push(m_ispreserve);
              }
  
              }
              catch(IOException e)
              {
                  throw new SAXException(e);
              }
      }
      /**
       * Initialize the serializer with the specified output stream and output
       * format. Must be called before calling any of the serialize methods.
       *
       * @param output The output stream to use
       * @param format The output format
       * @throws UnsupportedEncodingException The encoding specified   in the
       * output format is not supported
       */
      protected synchronized void init(OutputStream output, Properties format)
          throws UnsupportedEncodingException
      {
          if (null == format)
          {
              format = OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
           }
          super.init(output,format, false);
      }
  
          /**
           * Specifies an output stream to which the document should be
           * serialized. This method should not be called while the
           * serializer is in the process of serializing a document.
           * <p>
           * The encoding specified in the output properties is used, or
           * if no encoding was specified, the default for the selected
           * output method.
           *
           * @param output The output stream
           */
          public void setOutputStream(OutputStream output)
          {
  
              try
              {
                  Properties format;
                  if (null == m_format)
                      format = OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
                  else
                      format = m_format;
                  init(output, format, true);
              }
              catch (UnsupportedEncodingException uee)
              {
  
                  // Should have been warned in init, I guess...
              }
          }
          /**
           * This method is used when a prefix/uri namespace mapping
           * is indicated after the element was started with a
           * startElement() and before and endElement().
           * startPrefixMapping(prefix,uri) would be used before the
           * startElement() call.
           * @param uri the URI of the namespace
           * @param prefix the prefix associated with the given URI.
           *
           * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
           */
          public void namespaceAfterStartElement(String prefix, String uri)
              throws SAXException
          {
              // hack for XSLTC with finding URI for default namespace
              if (m_elemContext.m_elementURI == null)
              {
                  String prefix1 = getPrefixPart(m_elemContext.m_elementName);
                  if (prefix1 == null && EMPTYSTRING.equals(prefix))
                  {
                      // the elements URI is not known yet, and it
                      // doesn't have a prefix, and we are currently
                      // setting the uri for prefix "", so we have
                      // the uri for the element... lets remember it
                      m_elemContext.m_elementURI = uri;
                  }
              }
              startPrefixMapping(prefix,uri,false);
          }
  
      public void startDTD(String name, String publicId, String systemId)
          throws SAXException
      {
          m_inDTD = true;
          super.startDTD(name, publicId, systemId);
      }
  
      /**
       * Report the end of DTD declarations.
       * @throws org.xml.sax.SAXException The application may raise an exception.
       * @see #startDTD
       */
      public void endDTD() throws org.xml.sax.SAXException
      {
          m_inDTD = false;
          /* for ToHTMLStream the DOCTYPE is entirely output in the
           * startDocumentInternal() method, so don't do anything here
           */
      }
      /**
       * This method does nothing.
       */
      public void attributeDecl(
          String eName,
          String aName,
          String type,
          String valueDefault,
          String value)
          throws SAXException
      {
          // The internal DTD subset is not serialized by the ToHTMLStream serializer
      }
  
      /**
       * This method does nothing.
       */
      public void elementDecl(String name, String model) throws SAXException
      {
          // The internal DTD subset is not serialized by the ToHTMLStream serializer
      }
      /**
       * This method does nothing.
       */
      public void internalEntityDecl(String name, String value)
          throws SAXException
      {
          // The internal DTD subset is not serialized by the ToHTMLStream serializer
      }
      /**
       * This method does nothing.
       */
      public void externalEntityDecl(
          String name,
          String publicId,
          String systemId)
          throws SAXException
      {
          // The internal DTD subset is not serialized by the ToHTMLStream serializer
      }
  
      /**
       * This method is used to add an attribute to the currently open element.
       * The caller has guaranted that this attribute is unique, which means that it
       * not been seen before and will not be seen again.
       *
       * @param name the qualified name of the attribute
       * @param value the value of the attribute which can contain only
       * ASCII printable characters characters in the range 32 to 127 inclusive.
       * @param flags the bit values of this integer give optimization information.
       */
      public void addUniqueAttribute(String name, String value, int flags)
          throws SAXException
      {
          try
          {
              final java.io.Writer writer = m_writer;
              if ((flags & NO_BAD_CHARS) > 0 && m_htmlcharInfo.onlyQuotAmpLtGt)
              {
                  // "flags" has indicated that the characters
                  // '>'  '<'   '&'  and '"' are not in the value and
                  // m_htmlcharInfo has recorded that there are no other
                  // entities in the range 0 to 127 so we write out the
                  // value directly
                  writer.write(' ');
                  writer.write(name);
                  writer.write("=\"");
                  writer.write(value);
                  writer.write('"');
              }
              else if (
                  (flags & HTML_ATTREMPTY) > 0
                      && (value.length() == 0 || value.equalsIgnoreCase(name)))
              {
                  writer.write(' ');
                  writer.write(name);
              }
              else
              {
                  writer.write(' ');
                  writer.write(name);
                  writer.write("=\"");
                  if ((flags & HTML_ATTRURL) > 0)
                  {
                      writeAttrURI(writer, value, m_specialEscapeURLs);
                  }
                  else
                  {
                      writeAttrString(writer, value, this.getEncoding());
                  }
                  writer.write('"');
              }
          } catch (IOException e) {
              throw new SAXException(e);
          }
      }
  
      public void comment(char ch[], int start, int length)
              throws SAXException
      {
          // The internal DTD subset is not serialized by the ToHTMLStream serializer
          if (m_inDTD)
              return;
          super.comment(ch, start, length);
      }
  
      public boolean reset()
      {
          boolean ret = super.reset();
          if (!ret)
              return false;
          initToHTMLStream();
          return true;
      }
  
      private void initToHTMLStream()
      {
  //        m_elementDesc = null;
          m_inBlockElem = false;
          m_inDTD = false;
  //        m_isRawStack.clear();
          m_omitMetaTag = false;
          m_specialEscapeURLs = true;
      }
  
      static class Trie
      {
          /**
           * A digital search trie for 7-bit ASCII text
           * The API is a subset of java.util.Hashtable
           * The key must be a 7-bit ASCII string
           * The value may be any Java Object
           * One can get an object stored in a trie from its key,
           * but the search is either case sensitive or case
           * insensitive to the characters in the key, and this
           * choice of sensitivity or insensitivity is made when
           * the Trie is created, before any objects are put in it.
           *
           * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
           * It exists to cut the serializers dependancy on that package.
           *
           * @xsl.usage internal
           */
  
          /** Size of the m_nextChar array.  */
          public static final int ALPHA_SIZE = 128;
  
          /** The root node of the tree.    */
          final Node m_Root;
  
          /** helper buffer to convert Strings to char arrays */
          private char[] m_charBuffer = new char[0];
  
          /** true if the search for an object is lower case only with the key */
          private final boolean m_lowerCaseOnly;
  
          /**
           * Construct the trie that has a case insensitive search.
           */
          public Trie()
          {
              m_Root = new Node();
              m_lowerCaseOnly = false;
          }
  
          /**
           * Construct the trie given the desired case sensitivity with the key.
           * @param lowerCaseOnly true if the search keys are to be loser case only,
           * not case insensitive.
           */
          public Trie(boolean lowerCaseOnly)
          {
              m_Root = new Node();
              m_lowerCaseOnly = lowerCaseOnly;
          }
  
          /**
           * Put an object into the trie for lookup.
           *
           * @param key must be a 7-bit ASCII string
           * @param value any java object.
           *
           * @return The old object that matched key, or null.
           */
          public Object put(String key, Object value)
          {
  
              final int len = key.length();
              if (len > m_charBuffer.length)
              {
                  // make the biggest buffer ever needed in get(String)
                  m_charBuffer = new char[len];
              }
  
              Node node = m_Root;
  
              for (int i = 0; i < len; i++)
              {
                  Node nextNode =
                      node.m_nextChar[Character.toLowerCase(key.charAt(i))];
  
                  if (nextNode != null)
                  {
                      node = nextNode;
                  }
                  else
                  {
                      for (; i < len; i++)
                      {
                          Node newNode = new Node();
                          if (m_lowerCaseOnly)
                          {
                              // put this value into the tree only with a lower case key
                              node.m_nextChar[Character.toLowerCase(
                                  key.charAt(i))] =
                                  newNode;
                          }
                          else
                          {
                              // put this value into the tree with a case insensitive key
                              node.m_nextChar[Character.toUpperCase(
                                  key.charAt(i))] =
                                  newNode;
                              node.m_nextChar[Character.toLowerCase(
                                  key.charAt(i))] =
                                  newNode;
                          }
                          node = newNode;
                      }
                      break;
                  }
              }
  
              Object ret = node.m_Value;
  
              node.m_Value = value;
  
              return ret;
          }
  
          /**
           * Get an object that matches the key.
           *
           * @param key must be a 7-bit ASCII string
           *
           * @return The object that matches the key, or null.
           */
          public Object get(final String key)
          {
  
              final int len = key.length();
  
              /* If the name is too long, we won't find it, this also keeps us
               * from overflowing m_charBuffer
               */
              if (m_charBuffer.length < len)
                  return null;
  
              Node node = m_Root;
              switch (len) // optimize the look up based on the number of chars
              {
                  // case 0 looks silly, but the generated bytecode runs
                  // faster for lookup of elements of length 2 with this in
                  // and a fair bit faster.  Don't know why.
                  case 0 :
                      {
                          return null;
                      }
  
                  case 1 :
                      {
                          final char ch = key.charAt(0);
                          if (ch < ALPHA_SIZE)
                          {
                              node = node.m_nextChar[ch];
                              if (node != null)
                                  return node.m_Value;
                          }
                          return null;
                      }
                      //                comment out case 2 because the default is faster
                      //                case 2 :
                      //                    {
                      //                        final char ch0 = key.charAt(0);
                      //                        final char ch1 = key.charAt(1);
                      //                        if (ch0 < ALPHA_SIZE && ch1 < ALPHA_SIZE)
                      //                        {
                      //                            node = node.m_nextChar[ch0];
                      //                            if (node != null)
                      //                            {
                      //
                      //                                if (ch1 < ALPHA_SIZE)
                      //                                {
                      //                                    node = node.m_nextChar[ch1];
                      //                                    if (node != null)
                      //                                        return node.m_Value;
                      //                                }
                      //                            }
                      //                        }
                      //                        return null;
                      //                   }
                  default :
                      {
                          for (int i = 0; i < len; i++)
                          {
                              // A thread-safe way to loop over the characters
                              final char ch = key.charAt(i);
                              if (ALPHA_SIZE <= ch)
                              {
                                  // the key is not 7-bit ASCII so we won't find it here
                                  return null;
                              }
  
                              node = node.m_nextChar[ch];
                              if (node == null)
                                  return null;
                          }
  
                          return node.m_Value;
                      }
              }
          }
  
          /**
           * The node representation for the trie.
           * @xsl.usage internal
           */
          private class Node
          {
  
              /**
               * Constructor, creates a Node[ALPHA_SIZE].
               */
              Node()
              {
                  m_nextChar = new Node[ALPHA_SIZE];
                  m_Value = null;
              }
  
              /** The next nodes.   */
              final Node m_nextChar[];
  
              /** The value.   */
              Object m_Value;
          }
          /**
           * Construct the trie from another Trie.
           * Both the existing Trie and this new one share the same table for
           * lookup, and it is assumed that the table is fully populated and
           * not changing anymore.
           *
           * @param existingTrie the Trie that this one is a copy of.
           */
          public Trie(Trie existingTrie)
          {
              // copy some fields from the existing Trie into this one.
              m_Root = existingTrie.m_Root;
              m_lowerCaseOnly = existingTrie.m_lowerCaseOnly;
  
              // get a buffer just big enough to hold the longest key in the table.
              int max = existingTrie.getLongestKeyLength();
              m_charBuffer = new char[max];
          }
  
          /**
           * Get an object that matches the key.
           * This method is faster than get(), but is not thread-safe.
           *
           * @param key must be a 7-bit ASCII string
           *
           * @return The object that matches the key, or null.
           */
          public Object get2(final String key)
          {
  
              final int len = key.length();
  
              /* If the name is too long, we won't find it, this also keeps us
               * from overflowing m_charBuffer
               */
              if (m_charBuffer.length < len)
                  return null;
  
              Node node = m_Root;
              switch (len) // optimize the look up based on the number of chars
              {
                  // case 0 looks silly, but the generated bytecode runs
                  // faster for lookup of elements of length 2 with this in
                  // and a fair bit faster.  Don't know why.
                  case 0 :
                      {
                          return null;
                      }
  
                  case 1 :
                      {
                          final char ch = key.charAt(0);
                          if (ch < ALPHA_SIZE)
                          {
                              node = node.m_nextChar[ch];
                              if (node != null)
                                  return node.m_Value;
                          }
                          return null;
                      }
                  default :
                      {
                          /* Copy string into array. This is not thread-safe because
                           * it modifies the contents of m_charBuffer. If multiple
                           * threads were to use this Trie they all would be
                           * using this same array (not good). So this
                           * method is not thread-safe, but it is faster because
                           * converting to a char[] and looping over elements of
                           * the array is faster than a String's charAt(i).
                           */
                          key.getChars(0, len, m_charBuffer, 0);
  
                          for (int i = 0; i < len; i++)
                          {
                              final char ch = m_charBuffer[i];
                              if (ALPHA_SIZE <= ch)
                              {
                                  // the key is not 7-bit ASCII so we won't find it here
                                  return null;
                              }
  
                              node = node.m_nextChar[ch];
                              if (node == null)
                                  return null;
                          }
  
                          return node.m_Value;
                      }
              }
          }
  
          /**
           * Get the length of the longest key used in the table.
           */
          public int getLongestKeyLength()
          {
              return m_charBuffer.length;
          }
      }
  }
  