1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-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: ToHTMLStream.java,v 1.2.4.1 2005/09/15 08:15:26 suresh_emailid 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.UnsupportedEncodingException;
  28 import java.util.Properties;
  29 
  30 import javax.xml.transform.Result;
  31 
  32 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  33 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  34 import org.xml.sax.Attributes;
  35 import org.xml.sax.SAXException;
  36 
  37 /**
  38  * This serializer takes a series of SAX or
  39  * SAX-like events and writes its output
  40  * to the given stream.
  41  *
  42  * This class is not a public API, it is public
  43  * because it is used from another package.
  44  *
  45  * @xsl.usage internal
  46  */
  47 public final class ToHTMLStream extends ToStream
  48 {
  49 
  50     /** This flag is set while receiving events from the DTD */
  51     protected boolean m_inDTD = false;
  52 
  53     /** True if the current element is a block element.  (seems like
  54      *  this needs to be a stack. -sb). */
  55     private boolean m_inBlockElem = false;
  56 
  57     /**
  58      * Map that tells which XML characters should have special treatment, and it
  59      *  provides character to entity name lookup.
  60      */
  61     private static final CharInfo m_htmlcharInfo =
  62 //        new CharInfo(CharInfo.HTML_ENTITIES_RESOURCE);
  63         CharInfo.getCharInfoInternal(CharInfo.HTML_ENTITIES_RESOURCE, Method.HTML);
  64 
  65     /** A digital search trie for fast, case insensitive lookup of ElemDesc objects. */
  66     static final Trie m_elementFlags = new Trie();
  67 
  68     static {
  69         initTagReference(m_elementFlags);
  70     }
  71     static void initTagReference(Trie m_elementFlags) {
  72 
  73         // HTML 4.0 loose DTD
  74         m_elementFlags.put("BASEFONT", new ElemDesc(0 | ElemDesc.EMPTY));
  75         m_elementFlags.put(
  76             "FRAME",
  77             new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
  78         m_elementFlags.put("FRAMESET", new ElemDesc(0 | ElemDesc.BLOCK));
  79         m_elementFlags.put("NOFRAMES", new ElemDesc(0 | ElemDesc.BLOCK));
  80         m_elementFlags.put(
  81             "ISINDEX",
  82             new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
  83         m_elementFlags.put(
  84             "APPLET",
  85             new ElemDesc(0 | ElemDesc.WHITESPACESENSITIVE));
  86         m_elementFlags.put("CENTER", new ElemDesc(0 | ElemDesc.BLOCK));
  87         m_elementFlags.put("DIR", new ElemDesc(0 | ElemDesc.BLOCK));
  88         m_elementFlags.put("MENU", new ElemDesc(0 | ElemDesc.BLOCK));
  89 
  90         // HTML 4.0 strict DTD
  91         m_elementFlags.put("TT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  92         m_elementFlags.put("I", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  93         m_elementFlags.put("B", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  94         m_elementFlags.put("BIG", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  95         m_elementFlags.put("SMALL", new ElemDesc(0 | ElemDesc.FONTSTYLE));
  96         m_elementFlags.put("EM", new ElemDesc(0 | ElemDesc.PHRASE));
  97         m_elementFlags.put("STRONG", new ElemDesc(0 | ElemDesc.PHRASE));
  98         m_elementFlags.put("DFN", new ElemDesc(0 | ElemDesc.PHRASE));
  99         m_elementFlags.put("CODE", new ElemDesc(0 | ElemDesc.PHRASE));
 100         m_elementFlags.put("SAMP", new ElemDesc(0 | ElemDesc.PHRASE));
 101         m_elementFlags.put("KBD", new ElemDesc(0 | ElemDesc.PHRASE));
 102         m_elementFlags.put("VAR", new ElemDesc(0 | ElemDesc.PHRASE));
 103         m_elementFlags.put("CITE", new ElemDesc(0 | ElemDesc.PHRASE));
 104         m_elementFlags.put("ABBR", new ElemDesc(0 | ElemDesc.PHRASE));
 105         m_elementFlags.put("ACRONYM", new ElemDesc(0 | ElemDesc.PHRASE));
 106         m_elementFlags.put(
 107             "SUP",
 108             new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
 109         m_elementFlags.put(
 110             "SUB",
 111             new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
 112         m_elementFlags.put(
 113             "SPAN",
 114             new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
 115         m_elementFlags.put(
 116             "BDO",
 117             new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
 118         m_elementFlags.put(
 119             "BR",
 120             new ElemDesc(
 121                 0
 122                     | ElemDesc.SPECIAL
 123                     | ElemDesc.ASPECIAL
 124                     | ElemDesc.EMPTY
 125                     | ElemDesc.BLOCK));
 126         m_elementFlags.put("BODY", new ElemDesc(0 | ElemDesc.BLOCK));
 127         m_elementFlags.put(
 128             "ADDRESS",
 129             new ElemDesc(
 130                 0
 131                     | ElemDesc.BLOCK
 132                     | ElemDesc.BLOCKFORM
 133                     | ElemDesc.BLOCKFORMFIELDSET));
 134         m_elementFlags.put(
 135             "DIV",
 136             new ElemDesc(
 137                 0
 138                     | ElemDesc.BLOCK
 139                     | ElemDesc.BLOCKFORM
 140                     | ElemDesc.BLOCKFORMFIELDSET));
 141         m_elementFlags.put("A", new ElemDesc(0 | ElemDesc.SPECIAL));
 142         m_elementFlags.put(
 143             "MAP",
 144             new ElemDesc(
 145                 0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL | ElemDesc.BLOCK));
 146         m_elementFlags.put(
 147             "AREA",
 148             new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
 149         m_elementFlags.put(
 150             "LINK",
 151             new ElemDesc(
 152                 0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
 153         m_elementFlags.put(
 154             "IMG",
 155             new ElemDesc(
 156                 0
 157                     | ElemDesc.SPECIAL
 158                     | ElemDesc.ASPECIAL
 159                     | ElemDesc.EMPTY
 160                     | ElemDesc.WHITESPACESENSITIVE));
 161         m_elementFlags.put(
 162             "OBJECT",
 163             new ElemDesc(
 164                 0
 165                     | ElemDesc.SPECIAL
 166                     | ElemDesc.ASPECIAL
 167                     | ElemDesc.HEADMISC
 168                     | ElemDesc.WHITESPACESENSITIVE));
 169         m_elementFlags.put("PARAM", new ElemDesc(0 | ElemDesc.EMPTY));
 170         m_elementFlags.put(
 171             "HR",
 172             new ElemDesc(
 173                 0
 174                     | ElemDesc.BLOCK
 175                     | ElemDesc.BLOCKFORM
 176                     | ElemDesc.BLOCKFORMFIELDSET
 177                     | ElemDesc.EMPTY));
 178         m_elementFlags.put(
 179             "P",
 180             new ElemDesc(
 181                 0
 182                     | ElemDesc.BLOCK
 183                     | ElemDesc.BLOCKFORM
 184                     | ElemDesc.BLOCKFORMFIELDSET));
 185         m_elementFlags.put(
 186             "H1",
 187             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 188         m_elementFlags.put(
 189             "H2",
 190             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 191         m_elementFlags.put(
 192             "H3",
 193             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 194         m_elementFlags.put(
 195             "H4",
 196             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 197         m_elementFlags.put(
 198             "H5",
 199             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 200         m_elementFlags.put(
 201             "H6",
 202             new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
 203         m_elementFlags.put(
 204             "PRE",
 205             new ElemDesc(0 | ElemDesc.PREFORMATTED | ElemDesc.BLOCK));
 206         m_elementFlags.put(
 207             "Q",
 208             new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
 209         m_elementFlags.put(
 210             "BLOCKQUOTE",
 211             new ElemDesc(
 212                 0
 213                     | ElemDesc.BLOCK
 214                     | ElemDesc.BLOCKFORM
 215                     | ElemDesc.BLOCKFORMFIELDSET));
 216         m_elementFlags.put("INS", new ElemDesc(0));
 217         m_elementFlags.put("DEL", new ElemDesc(0));
 218         m_elementFlags.put(
 219             "DL",
 220             new ElemDesc(
 221                 0
 222                     | ElemDesc.BLOCK
 223                     | ElemDesc.BLOCKFORM
 224                     | ElemDesc.BLOCKFORMFIELDSET));
 225         m_elementFlags.put("DT", new ElemDesc(0 | ElemDesc.BLOCK));
 226         m_elementFlags.put("DD", new ElemDesc(0 | ElemDesc.BLOCK));
 227         m_elementFlags.put(
 228             "OL",
 229             new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
 230         m_elementFlags.put(
 231             "UL",
 232             new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
 233         m_elementFlags.put("LI", new ElemDesc(0 | ElemDesc.BLOCK));
 234         m_elementFlags.put("FORM", new ElemDesc(0 | ElemDesc.BLOCK));
 235         m_elementFlags.put("LABEL", new ElemDesc(0 | ElemDesc.FORMCTRL));
 236         m_elementFlags.put(
 237             "INPUT",
 238             new ElemDesc(
 239                 0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL | ElemDesc.EMPTY));
 240         m_elementFlags.put(
 241             "SELECT",
 242             new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
 243         m_elementFlags.put("OPTGROUP", new ElemDesc(0));
 244         m_elementFlags.put("OPTION", new ElemDesc(0));
 245         m_elementFlags.put(
 246             "TEXTAREA",
 247             new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
 248         m_elementFlags.put(
 249             "FIELDSET",
 250             new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.BLOCKFORM));
 251         m_elementFlags.put("LEGEND", new ElemDesc(0));
 252         m_elementFlags.put(
 253             "BUTTON",
 254             new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
 255         m_elementFlags.put(
 256             "TABLE",
 257             new ElemDesc(
 258                 0
 259                     | ElemDesc.BLOCK
 260                     | ElemDesc.BLOCKFORM
 261                     | ElemDesc.BLOCKFORMFIELDSET));
 262         m_elementFlags.put("CAPTION", new ElemDesc(0 | ElemDesc.BLOCK));
 263         m_elementFlags.put("THEAD", new ElemDesc(0 | ElemDesc.BLOCK));
 264         m_elementFlags.put("TFOOT", new ElemDesc(0 | ElemDesc.BLOCK));
 265         m_elementFlags.put("TBODY", new ElemDesc(0 | ElemDesc.BLOCK));
 266         m_elementFlags.put("COLGROUP", new ElemDesc(0 | ElemDesc.BLOCK));
 267         m_elementFlags.put(
 268             "COL",
 269             new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
 270         m_elementFlags.put("TR", new ElemDesc(0 | ElemDesc.BLOCK));
 271         m_elementFlags.put("TH", new ElemDesc(0));
 272         m_elementFlags.put("TD", new ElemDesc(0));
 273         m_elementFlags.put(
 274             "HEAD",
 275             new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.HEADELEM));
 276         m_elementFlags.put("TITLE", new ElemDesc(0 | ElemDesc.BLOCK));
 277         m_elementFlags.put(
 278             "BASE",
 279             new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
 280         m_elementFlags.put(
 281             "META",
 282             new ElemDesc(
 283                 0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
 284         m_elementFlags.put(
 285             "STYLE",
 286             new ElemDesc(
 287                 0 | ElemDesc.HEADMISC | ElemDesc.RAW | ElemDesc.BLOCK));
 288         m_elementFlags.put(
 289             "SCRIPT",
 290             new ElemDesc(
 291                 0
 292                     | ElemDesc.SPECIAL
 293                     | ElemDesc.ASPECIAL
 294                     | ElemDesc.HEADMISC
 295                     | ElemDesc.RAW));
 296         m_elementFlags.put(
 297             "NOSCRIPT",
 298             new ElemDesc(
 299                 0
 300                     | ElemDesc.BLOCK
 301                     | ElemDesc.BLOCKFORM
 302                     | ElemDesc.BLOCKFORMFIELDSET));
 303         m_elementFlags.put("HTML", new ElemDesc(0 | ElemDesc.BLOCK));
 304 
 305         // From "John Ky" <hand@syd.speednet.com.au
 306         // Transitional Document Type Definition ()
 307         // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/sgml/loosedtd.html#basefont
 308         m_elementFlags.put("FONT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
 309 
 310         // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-STRIKE
 311         m_elementFlags.put("S", new ElemDesc(0 | ElemDesc.FONTSTYLE));
 312         m_elementFlags.put("STRIKE", new ElemDesc(0 | ElemDesc.FONTSTYLE));
 313 
 314         // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-U
 315         m_elementFlags.put("U", new ElemDesc(0 | ElemDesc.FONTSTYLE));
 316 
 317         // From "John Ky" <hand@syd.speednet.com.au
 318         m_elementFlags.put("NOBR", new ElemDesc(0 | ElemDesc.FONTSTYLE));
 319 
 320         // HTML 4.0, section 16.5
 321         m_elementFlags.put(
 322             "IFRAME",
 323             new ElemDesc(
 324                 0
 325                     | ElemDesc.BLOCK
 326                     | ElemDesc.BLOCKFORM
 327                     | ElemDesc.BLOCKFORMFIELDSET));
 328 
 329         // Netscape 4 extension
 330         m_elementFlags.put(
 331             "LAYER",
 332             new ElemDesc(
 333                 0
 334                     | ElemDesc.BLOCK
 335                     | ElemDesc.BLOCKFORM
 336                     | ElemDesc.BLOCKFORMFIELDSET));
 337         // Netscape 4 extension
 338         m_elementFlags.put(
 339             "ILAYER",
 340             new ElemDesc(
 341                 0
 342                     | ElemDesc.BLOCK
 343                     | ElemDesc.BLOCKFORM
 344                     | ElemDesc.BLOCKFORMFIELDSET));
 345 
 346 
 347         // NOW FOR ATTRIBUTE INFORMATION . . .
 348         ElemDesc elemDesc;
 349 
 350 
 351         // ----------------------------------------------
 352         elemDesc = (ElemDesc) m_elementFlags.get("A");
 353         elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
 354         elemDesc.setAttr("NAME", ElemDesc.ATTRURL);
 355 
 356         // ----------------------------------------------
 357         elemDesc = (ElemDesc) m_elementFlags.get("AREA");
 358         elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
 359         elemDesc.setAttr("NOHREF", ElemDesc.ATTREMPTY);
 360 
 361         // ----------------------------------------------
 362         elemDesc = (ElemDesc) m_elementFlags.get("BASE");
 363         elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
 364 
 365         // ----------------------------------------------
 366         elemDesc = (ElemDesc) m_elementFlags.get("BUTTON");
 367         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 368 
 369         // ----------------------------------------------
 370         elemDesc = (ElemDesc) m_elementFlags.get("BLOCKQUOTE");
 371         elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
 372 
 373         // ----------------------------------------------
 374         elemDesc = (ElemDesc) m_elementFlags.get("DEL");
 375         elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
 376 
 377         // ----------------------------------------------
 378         elemDesc = (ElemDesc) m_elementFlags.get("DIR");
 379         elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
 380 
 381         // ----------------------------------------------
 382 
 383         elemDesc = (ElemDesc) m_elementFlags.get("DIV");
 384         elemDesc.setAttr("SRC", ElemDesc.ATTRURL); // Netscape 4 extension
 385         elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
 386 
 387         // ----------------------------------------------
 388         elemDesc = (ElemDesc) m_elementFlags.get("DL");
 389         elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
 390 
 391         // ----------------------------------------------
 392         elemDesc = (ElemDesc) m_elementFlags.get("FORM");
 393         elemDesc.setAttr("ACTION", ElemDesc.ATTRURL);
 394 
 395         // ----------------------------------------------
 396         // Attribution to: "Voytenko, Dimitry" <DVoytenko@SECTORBASE.COM>
 397         elemDesc = (ElemDesc) m_elementFlags.get("FRAME");
 398         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 399         elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
 400         elemDesc.setAttr("NORESIZE",ElemDesc.ATTREMPTY);
 401 
 402         // ----------------------------------------------
 403         elemDesc = (ElemDesc) m_elementFlags.get("HEAD");
 404         elemDesc.setAttr("PROFILE", ElemDesc.ATTRURL);
 405 
 406         // ----------------------------------------------
 407         elemDesc = (ElemDesc) m_elementFlags.get("HR");
 408         elemDesc.setAttr("NOSHADE", ElemDesc.ATTREMPTY);
 409 
 410         // ----------------------------------------------
 411         // HTML 4.0, section 16.5
 412         elemDesc = (ElemDesc) m_elementFlags.get("IFRAME");
 413         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 414         elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
 415 
 416         // ----------------------------------------------
 417         // Netscape 4 extension
 418         elemDesc = (ElemDesc) m_elementFlags.get("ILAYER");
 419         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 420 
 421         // ----------------------------------------------
 422         elemDesc = (ElemDesc) m_elementFlags.get("IMG");
 423         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 424         elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
 425         elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
 426         elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
 427 
 428         // ----------------------------------------------
 429         elemDesc = (ElemDesc) m_elementFlags.get("INPUT");
 430         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 431         elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
 432         elemDesc.setAttr("CHECKED", ElemDesc.ATTREMPTY);
 433         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 434         elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
 435         elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
 436 
 437         // ----------------------------------------------
 438         elemDesc = (ElemDesc) m_elementFlags.get("INS");
 439         elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
 440 
 441         // ----------------------------------------------
 442         // Netscape 4 extension
 443         elemDesc = (ElemDesc) m_elementFlags.get("LAYER");
 444         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 445 
 446         // ----------------------------------------------
 447         elemDesc = (ElemDesc) m_elementFlags.get("LINK");
 448         elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
 449 
 450         // ----------------------------------------------
 451         elemDesc = (ElemDesc) m_elementFlags.get("MENU");
 452         elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
 453 
 454         // ----------------------------------------------
 455         elemDesc = (ElemDesc) m_elementFlags.get("OBJECT");
 456         elemDesc.setAttr("CLASSID", ElemDesc.ATTRURL);
 457         elemDesc.setAttr("CODEBASE", ElemDesc.ATTRURL);
 458         elemDesc.setAttr("DATA", ElemDesc.ATTRURL);
 459         elemDesc.setAttr("ARCHIVE", ElemDesc.ATTRURL);
 460         elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
 461         elemDesc.setAttr("DECLARE", ElemDesc.ATTREMPTY);
 462 
 463         // ----------------------------------------------
 464         elemDesc = (ElemDesc) m_elementFlags.get("OL");
 465         elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
 466 
 467         // ----------------------------------------------
 468         elemDesc = (ElemDesc) m_elementFlags.get("OPTGROUP");
 469         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 470 
 471         // ----------------------------------------------
 472         elemDesc = (ElemDesc) m_elementFlags.get("OPTION");
 473         elemDesc.setAttr("SELECTED", ElemDesc.ATTREMPTY);
 474         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 475 
 476         // ----------------------------------------------
 477         elemDesc = (ElemDesc) m_elementFlags.get("Q");
 478         elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
 479 
 480         // ----------------------------------------------
 481         elemDesc = (ElemDesc) m_elementFlags.get("SCRIPT");
 482         elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
 483         elemDesc.setAttr("FOR", ElemDesc.ATTRURL);
 484         elemDesc.setAttr("DEFER", ElemDesc.ATTREMPTY);
 485 
 486         // ----------------------------------------------
 487         elemDesc = (ElemDesc) m_elementFlags.get("SELECT");
 488         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 489         elemDesc.setAttr("MULTIPLE", ElemDesc.ATTREMPTY);
 490 
 491         // ----------------------------------------------
 492         elemDesc = (ElemDesc) m_elementFlags.get("TABLE");
 493         elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
 494 
 495         // ----------------------------------------------
 496         elemDesc = (ElemDesc) m_elementFlags.get("TD");
 497         elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
 498 
 499         // ----------------------------------------------
 500         elemDesc = (ElemDesc) m_elementFlags.get("TEXTAREA");
 501         elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
 502         elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
 503 
 504         // ----------------------------------------------
 505         elemDesc = (ElemDesc) m_elementFlags.get("TH");
 506         elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
 507 
 508         // ----------------------------------------------
 509         // The nowrap attribute of a tr element is both
 510         // a Netscape and Internet-Explorer extension
 511         elemDesc = (ElemDesc) m_elementFlags.get("TR");
 512         elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
 513 
 514         // ----------------------------------------------
 515         elemDesc = (ElemDesc) m_elementFlags.get("UL");
 516         elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
 517     }
 518 
 519     /**
 520      * Dummy element for elements not found.
 521      */
 522     static private final ElemDesc m_dummy = new ElemDesc(0 | ElemDesc.BLOCK);
 523 
 524     /** True if URLs should be specially escaped with the %xx form. */
 525     private boolean m_specialEscapeURLs = true;
 526 
 527     /** True if the META tag should be omitted. */
 528     private boolean m_omitMetaTag = false;
 529 
 530     /**
 531      * Tells if the formatter should use special URL escaping.
 532      *
 533      * @param bool True if URLs should be specially escaped with the %xx form.
 534      */
 535     public void setSpecialEscapeURLs(boolean bool)
 536     {
 537         m_specialEscapeURLs = bool;
 538     }
 539 
 540     /**
 541      * Tells if the formatter should omit the META tag.
 542      *
 543      * @param bool True if the META tag should be omitted.
 544      */
 545     public void setOmitMetaTag(boolean bool)
 546     {
 547         m_omitMetaTag = bool;
 548     }
 549 
 550     /**
 551      * Specifies an output format for this serializer. It the
 552      * serializer has already been associated with an output format,
 553      * it will switch to the new format. This method should not be
 554      * called while the serializer is in the process of serializing
 555      * a document.
 556      *
 557      * This method can be called multiple times before starting
 558      * the serialization of a particular result-tree. In principle
 559      * all serialization parameters can be changed, with the exception
 560      * of method="html" (it must be method="html" otherwise we
 561      * shouldn't even have a ToHTMLStream object here!)
 562      *
 563      * @param format The output format or serialzation parameters
 564      * to use.
 565      */
 566     public void setOutputFormat(Properties format)
 567     {
 568 
 569         m_specialEscapeURLs =
 570             OutputPropertyUtils.getBooleanProperty(
 571                 OutputPropertiesFactory.S_USE_URL_ESCAPING,
 572                 format);
 573 
 574         m_omitMetaTag =
 575             OutputPropertyUtils.getBooleanProperty(
 576                 OutputPropertiesFactory.S_OMIT_META_TAG,
 577                 format);
 578 
 579         super.setOutputFormat(format);
 580     }
 581 
 582     /**
 583      * Tells if the formatter should use special URL escaping.
 584      *
 585      * @return True if URLs should be specially escaped with the %xx form.
 586      */
 587     private final boolean getSpecialEscapeURLs()
 588     {
 589         return m_specialEscapeURLs;
 590     }
 591 
 592     /**
 593      * Tells if the formatter should omit the META tag.
 594      *
 595      * @return True if the META tag should be omitted.
 596      */
 597     private final boolean getOmitMetaTag()
 598     {
 599         return m_omitMetaTag;
 600     }
 601 
 602     /**
 603      * Get a description of the given element.
 604      *
 605      * @param name non-null name of element, case insensitive.
 606      *
 607      * @return non-null reference to ElemDesc, which may be m_dummy if no
 608      *         element description matches the given name.
 609      */
 610     public static final ElemDesc getElemDesc(String name)
 611     {
 612         /* this method used to return m_dummy  when name was null
 613          * but now it doesn't check and and requires non-null name.
 614          */
 615         Object obj = m_elementFlags.get(name);
 616         if (null != obj)
 617             return (ElemDesc)obj;
 618         return m_dummy;
 619     }
 620 
 621     /**
 622      * A Trie that is just a copy of the "static" one.
 623      * We need this one to be able to use the faster, but not thread-safe
 624      * method Trie.get2(name)
 625      */
 626     private Trie m_htmlInfo = new Trie(m_elementFlags);
 627     /**
 628      * Calls to this method could be replaced with calls to
 629      * getElemDesc(name), but this one should be faster.
 630      */
 631     private ElemDesc getElemDesc2(String name)
 632     {
 633         Object obj = m_htmlInfo.get2(name);
 634         if (null != obj)
 635             return (ElemDesc)obj;
 636         return m_dummy;
 637     }
 638 
 639     /**
 640      * Default constructor.
 641      */
 642     public ToHTMLStream()
 643     {
 644 
 645         super();
 646         m_charInfo = m_htmlcharInfo;
 647         // initialize namespaces
 648         m_prefixMap = new NamespaceMappings();
 649 
 650     }
 651 
 652     /** The name of the current element. */
 653 //    private String m_currentElementName = null;
 654 
 655     /**
 656      * Receive notification of the beginning of a document.
 657      *
 658      * @throws org.xml.sax.SAXException Any SAX exception, possibly
 659      *            wrapping another exception.
 660      *
 661      * @throws org.xml.sax.SAXException
 662      */
 663     protected void startDocumentInternal() throws org.xml.sax.SAXException
 664     {
 665         super.startDocumentInternal();
 666 
 667         m_needToCallStartDocument = false;
 668         m_needToOutputDocTypeDecl = true;
 669         m_startNewLine = false;
 670         setOmitXMLDeclaration(true);
 671 
 672         if (true == m_needToOutputDocTypeDecl)
 673         {
 674             String doctypeSystem = getDoctypeSystem();
 675             String doctypePublic = getDoctypePublic();
 676             if ((null != doctypeSystem) || (null != doctypePublic))
 677             {
 678                 final java.io.Writer writer = m_writer;
 679                 try
 680                 {
 681                 writer.write("<!DOCTYPE html");
 682 
 683                 if (null != doctypePublic)
 684                 {
 685                     writer.write(" PUBLIC \"");
 686                     writer.write(doctypePublic);
 687                     writer.write('"');
 688                 }
 689 
 690                 if (null != doctypeSystem)
 691                 {
 692                     if (null == doctypePublic)
 693                         writer.write(" SYSTEM \"");
 694                     else
 695                         writer.write(" \"");
 696 
 697                     writer.write(doctypeSystem);
 698                     writer.write('"');
 699                 }
 700 
 701                 writer.write('>');
 702                 outputLineSep();
 703                 }
 704                 catch(IOException e)
 705                 {
 706                     throw new SAXException(e);
 707                 }
 708             }
 709         }
 710 
 711         m_needToOutputDocTypeDecl = false;
 712     }
 713 
 714     /**
 715      * Receive notification of the end of a document.
 716      *
 717      * @throws org.xml.sax.SAXException Any SAX exception, possibly
 718      *            wrapping another exception.
 719      *
 720      * @throws org.xml.sax.SAXException
 721      */
 722     public final void endDocument() throws org.xml.sax.SAXException
 723     {
 724 
 725         flushPending();
 726         if (m_doIndent && !m_isprevtext)
 727         {
 728             try
 729             {
 730             outputLineSep();
 731             }
 732             catch(IOException e)
 733             {
 734                 throw new SAXException(e);
 735             }
 736         }
 737 
 738         flushWriter();
 739         if (m_tracer != null)
 740             super.fireEndDoc();
 741     }
 742 
 743     /**
 744      *  Receive notification of the beginning of an element.
 745      *
 746      *
 747      *  @param namespaceURI
 748      *  @param localName
 749      *  @param name The element type name.
 750      *  @param atts The attributes attached to the element, if any.
 751      *  @throws org.xml.sax.SAXException Any SAX exception, possibly
 752      *             wrapping another exception.
 753      *  @see #endElement
 754      *  @see org.xml.sax.AttributeList
 755      */
 756     public void startElement(
 757         String namespaceURI,
 758         String localName,
 759         String name,
 760         Attributes atts)
 761         throws org.xml.sax.SAXException
 762     {
 763 
 764         ElemContext elemContext = m_elemContext;
 765 
 766         // clean up any pending things first
 767         if (elemContext.m_startTagOpen)
 768         {
 769             closeStartTag();
 770             elemContext.m_startTagOpen = false;
 771         }
 772         else if (m_cdataTagOpen)
 773         {
 774             closeCDATA();
 775             m_cdataTagOpen = false;
 776         }
 777         else if (m_needToCallStartDocument)
 778         {
 779             startDocumentInternal();
 780             m_needToCallStartDocument = false;
 781         }
 782 
 783 
 784         // if this element has a namespace then treat it like XML
 785         if (null != namespaceURI && namespaceURI.length() > 0)
 786         {
 787             super.startElement(namespaceURI, localName, name, atts);
 788 
 789             return;
 790         }
 791 
 792         try
 793         {
 794             // getElemDesc2(name) is faster than getElemDesc(name)
 795             ElemDesc elemDesc = getElemDesc2(name);
 796             int elemFlags = elemDesc.getFlags();
 797 
 798             // deal with indentation issues first
 799             if (m_doIndent)
 800             {
 801 
 802                 boolean isBlockElement = (elemFlags & ElemDesc.BLOCK) != 0;
 803                 if (m_ispreserve)
 804                     m_ispreserve = false;
 805                 else if (
 806                     (null != elemContext.m_elementName)
 807                     && (!m_inBlockElem
 808                         || isBlockElement) /* && !isWhiteSpaceSensitive */
 809                     )
 810                 {
 811                     m_startNewLine = true;
 812 
 813                     indent();
 814 
 815                 }
 816                 m_inBlockElem = !isBlockElement;
 817             }
 818 
 819             // save any attributes for later processing
 820             if (atts != null)
 821                 addAttributes(atts);
 822 
 823             m_isprevtext = false;
 824             final java.io.Writer writer = m_writer;
 825             writer.write('<');
 826             writer.write(name);
 827 
 828 
 829 
 830             if (m_tracer != null)
 831                 firePseudoAttributes();
 832 
 833             if ((elemFlags & ElemDesc.EMPTY) != 0)
 834             {
 835                 // an optimization for elements which are expected
 836                 // to be empty.
 837                 m_elemContext = elemContext.push();
 838                 /* XSLTC sometimes calls namespaceAfterStartElement()
 839                  * so we need to remember the name
 840                  */
 841                 m_elemContext.m_elementName = name;
 842                 m_elemContext.m_elementDesc = elemDesc;
 843                 return;
 844             }
 845             else
 846             {
 847                 elemContext = elemContext.push(namespaceURI,localName,name);
 848                 m_elemContext = elemContext;
 849                 elemContext.m_elementDesc = elemDesc;
 850                 elemContext.m_isRaw = (elemFlags & ElemDesc.RAW) != 0;
 851             }
 852 
 853 
 854             if ((elemFlags & ElemDesc.HEADELEM) != 0)
 855             {
 856                 // This is the <HEAD> element, do some special processing
 857                 closeStartTag();
 858                 elemContext.m_startTagOpen = false;
 859                 if (!m_omitMetaTag)
 860                 {
 861                     if (m_doIndent)
 862                         indent();
 863                     writer.write(
 864                         "<META http-equiv=\"Content-Type\" content=\"text/html; charset=");
 865                     String encoding = getEncoding();
 866                     String encode = Encodings.getMimeEncoding(encoding);
 867                     writer.write(encode);
 868                     writer.write("\">");
 869                 }
 870             }
 871         }
 872         catch (IOException e)
 873         {
 874             throw new SAXException(e);
 875         }
 876     }
 877 
 878     /**
 879      *  Receive notification of the end of an element.
 880      *
 881      *
 882      *  @param namespaceURI
 883      *  @param localName
 884      *  @param name The element type name
 885      *  @throws org.xml.sax.SAXException Any SAX exception, possibly
 886      *             wrapping another exception.
 887      */
 888     public final void endElement(
 889         final String namespaceURI,
 890         final String localName,
 891         final String name)
 892         throws org.xml.sax.SAXException
 893     {
 894         // deal with any pending issues
 895         if (m_cdataTagOpen)
 896             closeCDATA();
 897 
 898         // if the element has a namespace, treat it like XML, not HTML
 899         if (null != namespaceURI && namespaceURI.length() > 0)
 900         {
 901             super.endElement(namespaceURI, localName, name);
 902 
 903             return;
 904         }
 905 
 906         try
 907         {
 908 
 909             ElemContext elemContext = m_elemContext;
 910             final ElemDesc elemDesc = elemContext.m_elementDesc;
 911             final int elemFlags = elemDesc.getFlags();
 912             final boolean elemEmpty = (elemFlags & ElemDesc.EMPTY) != 0;
 913 
 914             // deal with any indentation issues
 915             if (m_doIndent)
 916             {
 917                 final boolean isBlockElement = (elemFlags&ElemDesc.BLOCK) != 0;
 918                 boolean shouldIndent = false;
 919 
 920                 if (m_ispreserve)
 921                 {
 922                     m_ispreserve = false;
 923                 }
 924                 else if (m_doIndent && (!m_inBlockElem || isBlockElement))
 925                 {
 926                     m_startNewLine = true;
 927                     shouldIndent = true;
 928                 }
 929                 if (!elemContext.m_startTagOpen && shouldIndent)
 930                     indent(elemContext.m_currentElemDepth - 1);
 931                 m_inBlockElem = !isBlockElement;
 932             }
 933 
 934             final java.io.Writer writer = m_writer;
 935             if (!elemContext.m_startTagOpen)
 936             {
 937                 writer.write("</");
 938                 writer.write(name);
 939                 writer.write('>');
 940             }
 941             else
 942             {
 943                 // the start-tag open when this method was called,
 944                 // so we need to process it now.
 945 
 946                 if (m_tracer != null)
 947                     super.fireStartElem(name);
 948 
 949                 // the starting tag was still open when we received this endElement() call
 950                 // so we need to process any gathered attributes NOW, before they go away.
 951                 int nAttrs = m_attributes.getLength();
 952                 if (nAttrs > 0)
 953                 {
 954                     processAttributes(m_writer, nAttrs);
 955                     // clear attributes object for re-use with next element
 956                     m_attributes.clear();
 957                 }
 958                 if (!elemEmpty)
 959                 {
 960                     // As per Dave/Paul recommendation 12/06/2000
 961                     // if (shouldIndent)
 962                     // writer.write('>');
 963                     //  indent(m_currentIndent);
 964 
 965                     writer.write("></");
 966                     writer.write(name);
 967                     writer.write('>');
 968                 }
 969                 else
 970                 {
 971                     writer.write('>');
 972                 }
 973             }
 974 
 975             // clean up because the element has ended
 976             if ((elemFlags & ElemDesc.WHITESPACESENSITIVE) != 0)
 977                 m_ispreserve = true;
 978             m_isprevtext = false;
 979 
 980             // fire off the end element event
 981             if (m_tracer != null)
 982                 super.fireEndElem(name);
 983 
 984             // OPTIMIZE-EMPTY
 985             if (elemEmpty)
 986             {
 987                 // a quick exit if the HTML element had no children.
 988                 // This block of code can be removed if the corresponding block of code
 989                 // in startElement() also labeled with "OPTIMIZE-EMPTY" is also removed
 990                 m_elemContext = elemContext.m_prev;
 991                 return;
 992             }
 993 
 994             // some more clean because the element has ended.
 995             if (!elemContext.m_startTagOpen)
 996             {
 997                 if (m_doIndent && !m_preserves.isEmpty())
 998                     m_preserves.pop();
 999             }
1000             m_elemContext = elemContext.m_prev;
1001 //            m_isRawStack.pop();
1002         }
1003         catch (IOException e)
1004         {
1005             throw new SAXException(e);
1006         }
1007     }
1008 
1009     /**
1010      * Process an attribute.
1011      * @param   writer The writer to write the processed output to.
1012      * @param   name   The name of the attribute.
1013      * @param   value   The value of the attribute.
1014      * @param   elemDesc The description of the HTML element
1015      *           that has this attribute.
1016      *
1017      * @throws org.xml.sax.SAXException
1018      */
1019     protected void processAttribute(
1020         java.io.Writer writer,
1021         String name,
1022         String value,
1023         ElemDesc elemDesc)
1024         throws IOException
1025     {
1026         writer.write(' ');
1027 
1028         if (   ((value.length() == 0) || value.equalsIgnoreCase(name))
1029             && elemDesc != null
1030             && elemDesc.isAttrFlagSet(name, ElemDesc.ATTREMPTY))
1031         {
1032             writer.write(name);
1033         }
1034         else
1035         {
1036             // %REVIEW% %OPT%
1037             // Two calls to single-char write may NOT
1038             // be more efficient than one to string-write...
1039             writer.write(name);
1040             writer.write("=\"");
1041             if (   elemDesc != null
1042                 && elemDesc.isAttrFlagSet(name, ElemDesc.ATTRURL))
1043                 writeAttrURI(writer, value, m_specialEscapeURLs);
1044             else
1045                 writeAttrString(writer, value, this.getEncoding());
1046             writer.write('"');
1047 
1048         }
1049     }
1050 
1051     /**
1052      * Tell if a character is an ASCII digit.
1053      */
1054     private boolean isASCIIDigit(char c)
1055     {
1056         return (c >= '0' && c <= '9');
1057     }
1058 
1059     /**
1060      * Make an integer into an HH hex value.
1061      * Does no checking on the size of the input, since this
1062      * is only meant to be used locally by writeAttrURI.
1063      *
1064      * @param i must be a value less than 255.
1065      *
1066      * @return should be a two character string.
1067      */
1068     private static String makeHHString(int i)
1069     {
1070         String s = Integer.toHexString(i).toUpperCase();
1071         if (s.length() == 1)
1072         {
1073             s = "0" + s;
1074         }
1075         return s;
1076     }
1077 
1078     /**
1079     * Dmitri Ilyin: Makes sure if the String is HH encoded sign.
1080     * @param str must be 2 characters long
1081     *
1082     * @return true or false
1083     */
1084     private boolean isHHSign(String str)
1085     {
1086         boolean sign = true;
1087         try
1088         {
1089             char r = (char) Integer.parseInt(str, 16);
1090         }
1091         catch (NumberFormatException e)
1092         {
1093             sign = false;
1094         }
1095         return sign;
1096     }
1097 
1098     /**
1099      * Write the specified <var>string</var> after substituting non ASCII characters,
1100      * with <CODE>%HH</CODE>, where HH is the hex of the byte value.
1101      *
1102      * @param   string      String to convert to XML format.
1103      * @param doURLEscaping True if we should try to encode as
1104      *                      per http://www.ietf.org/rfc/rfc2396.txt.
1105      *
1106      * @throws org.xml.sax.SAXException if a bad surrogate pair is detected.
1107      */
1108     public void writeAttrURI(
1109         final java.io.Writer writer, String string, boolean doURLEscaping)
1110         throws IOException
1111     {
1112         // http://www.ietf.org/rfc/rfc2396.txt says:
1113         // A URI is always in an "escaped" form, since escaping or unescaping a
1114         // completed URI might change its semantics.  Normally, the only time
1115         // escape encodings can safely be made is when the URI is being created
1116         // from its component parts; each component may have its own set of
1117         // characters that are reserved, so only the mechanism responsible for
1118         // generating or interpreting that component can determine whether or
1119         // not escaping a character will change its semantics. Likewise, a URI
1120         // must be separated into its components before the escaped characters
1121         // within those components can be safely decoded.
1122         //
1123         // ...So we do our best to do limited escaping of the URL, without
1124         // causing damage.  If the URL is already properly escaped, in theory, this
1125         // function should not change the string value.
1126 
1127         final int end = string.length();
1128         if (end > m_attrBuff.length)
1129         {
1130            m_attrBuff = new char[end*2 + 1];
1131         }
1132         string.getChars(0,end, m_attrBuff, 0);
1133         final char[] chars = m_attrBuff;
1134 
1135         int cleanStart = 0;
1136         int cleanLength = 0;
1137 
1138 
1139         char ch = 0;
1140         for (int i = 0; i < end; i++)
1141         {
1142             ch = chars[i];
1143 
1144             if ((ch < 32) || (ch > 126))
1145             {
1146                 if (cleanLength > 0)
1147                 {
1148                     writer.write(chars, cleanStart, cleanLength);
1149                     cleanLength = 0;
1150                 }
1151                 if (doURLEscaping)
1152                 {
1153                     // Encode UTF16 to UTF8.
1154                     // Reference is Unicode, A Primer, by Tony Graham.
1155                     // Page 92.
1156 
1157                     // Note that Kay doesn't escape 0x20...
1158                     //  if(ch == 0x20) // Not sure about this... -sb
1159                     //  {
1160                     //    writer.write(ch);
1161                     //  }
1162                     //  else
1163                     if (ch <= 0x7F)
1164                     {
1165                         writer.write('%');
1166                         writer.write(makeHHString(ch));
1167                     }
1168                     else if (ch <= 0x7FF)
1169                     {
1170                         // Clear low 6 bits before rotate, put high 4 bits in low byte,
1171                         // and set two high bits.
1172                         int high = (ch >> 6) | 0xC0;
1173                         int low = (ch & 0x3F) | 0x80;
1174                         // First 6 bits, + high bit
1175                         writer.write('%');
1176                         writer.write(makeHHString(high));
1177                         writer.write('%');
1178                         writer.write(makeHHString(low));
1179                     }
1180                     else if (Encodings.isHighUTF16Surrogate(ch)) // high surrogate
1181                     {
1182                         // I'm sure this can be done in 3 instructions, but I choose
1183                         // to try and do it exactly like it is done in the book, at least
1184                         // until we are sure this is totally clean.  I don't think performance
1185                         // is a big issue with this particular function, though I could be
1186                         // wrong.  Also, the stuff below clearly does more masking than
1187                         // it needs to do.
1188 
1189                         // Clear high 6 bits.
1190                         int highSurrogate = ((int) ch) & 0x03FF;
1191 
1192                         // Middle 4 bits (wwww) + 1
1193                         // "Note that the value of wwww from the high surrogate bit pattern
1194                         // is incremented to make the uuuuu bit pattern in the scalar value
1195                         // so the surrogate pair don't address the BMP."
1196                         int wwww = ((highSurrogate & 0x03C0) >> 6);
1197                         int uuuuu = wwww + 1;
1198 
1199                         // next 4 bits
1200                         int zzzz = (highSurrogate & 0x003C) >> 2;
1201 
1202                         // low 2 bits
1203                         int yyyyyy = ((highSurrogate & 0x0003) << 4) & 0x30;
1204 
1205                         // Get low surrogate character.
1206                         ch = chars[++i];
1207 
1208                         // Clear high 6 bits.
1209                         int lowSurrogate = ((int) ch) & 0x03FF;
1210 
1211                         // put the middle 4 bits into the bottom of yyyyyy (byte 3)
1212                         yyyyyy = yyyyyy | ((lowSurrogate & 0x03C0) >> 6);
1213 
1214                         // bottom 6 bits.
1215                         int xxxxxx = (lowSurrogate & 0x003F);
1216 
1217                         int byte1 = 0xF0 | (uuuuu >> 2); // top 3 bits of uuuuu
1218                         int byte2 =
1219                             0x80 | (((uuuuu & 0x03) << 4) & 0x30) | zzzz;
1220                         int byte3 = 0x80 | yyyyyy;
1221                         int byte4 = 0x80 | xxxxxx;
1222 
1223                         writer.write('%');
1224                         writer.write(makeHHString(byte1));
1225                         writer.write('%');
1226                         writer.write(makeHHString(byte2));
1227                         writer.write('%');
1228                         writer.write(makeHHString(byte3));
1229                         writer.write('%');
1230                         writer.write(makeHHString(byte4));
1231                     }
1232                     else
1233                     {
1234                         int high = (ch >> 12) | 0xE0; // top 4 bits
1235                         int middle = ((ch & 0x0FC0) >> 6) | 0x80;
1236                         // middle 6 bits
1237                         int low = (ch & 0x3F) | 0x80;
1238                         // First 6 bits, + high bit
1239                         writer.write('%');
1240                         writer.write(makeHHString(high));
1241                         writer.write('%');
1242                         writer.write(makeHHString(middle));
1243                         writer.write('%');
1244                         writer.write(makeHHString(low));
1245                     }
1246 
1247                 }
1248                 else if (escapingNotNeeded(ch))
1249                 {
1250                     writer.write(ch);
1251                 }
1252                 else
1253                 {
1254                     writer.write("&#");
1255                     writer.write(Integer.toString(ch));
1256                     writer.write(';');
1257                 }
1258                 // In this character range we have first written out any previously accumulated
1259                 // "clean" characters, then processed the current more complicated character,
1260                 // which may have incremented "i".
1261                 // We now we reset the next possible clean character.
1262                 cleanStart = i + 1;
1263             }
1264             // Since http://www.ietf.org/rfc/rfc2396.txt refers to the URI grammar as
1265             // not allowing quotes in the URI proper syntax, nor in the fragment
1266             // identifier, we believe that it's OK to double escape quotes.
1267             else if (ch == '"')
1268             {
1269                 // If the character is a '%' number number, try to avoid double-escaping.
1270                 // There is a question if this is legal behavior.
1271 
1272                 // Dmitri Ilyin: to check if '%' number number is invalid. It must be checked if %xx is a sign, that would be encoded
1273                 // 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.
1274 
1275                 //        if( ((i+2) < len) && isASCIIDigit(stringArray[i+1]) && isASCIIDigit(stringArray[i+2]) )
1276 
1277                 // We are no longer escaping '%'
1278 
1279                 if (cleanLength > 0)
1280                 {
1281                     writer.write(chars, cleanStart, cleanLength);
1282                     cleanLength = 0;
1283                 }
1284 
1285 
1286                 // Mike Kay encodes this as ", so he may know something I don't?
1287                 if (doURLEscaping)
1288                     writer.write("%22");
1289                 else
1290                     writer.write("&quot;"); // we have to escape this, I guess.
1291 
1292                 // We have written out any clean characters, then the escaped '%' and now we
1293                 // We now we reset the next possible clean character.
1294                 cleanStart = i + 1;
1295             }
1296             else if (ch == '&')
1297             {
1298                 // HTML 4.01 reads, "Authors should use "&amp;" (ASCII decimal 38)
1299                 // instead of "&" to avoid confusion with the beginning of a character
1300                 // reference (entity reference open delimiter).
1301                 if (cleanLength > 0)
1302                 {
1303                     writer.write(chars, cleanStart, cleanLength);
1304                     cleanLength = 0;
1305                 }
1306                 writer.write("&amp;");
1307                 cleanStart = i + 1;
1308             }
1309             else
1310             {
1311                 // no processing for this character, just count how
1312                 // many characters in a row that we have that need no processing
1313                 cleanLength++;
1314             }
1315         }
1316 
1317         // are there any clean characters at the end of the array
1318         // that we haven't processed yet?
1319         if (cleanLength > 1)
1320         {
1321             // if the whole string can be written out as-is do so
1322             // otherwise write out the clean chars at the end of the
1323             // array
1324             if (cleanStart == 0)
1325                 writer.write(string);
1326             else
1327                 writer.write(chars, cleanStart, cleanLength);
1328         }
1329         else if (cleanLength == 1)
1330         {
1331             // a little optimization for 1 clean character
1332             // (we could have let the previous if(...) handle them all)
1333             writer.write(ch);
1334         }
1335     }
1336 
1337     /**
1338      * Writes the specified <var>string</var> after substituting <VAR>specials</VAR>,
1339      * and UTF-16 surrogates for character references <CODE>&amp;#xnn</CODE>.
1340      *
1341      * @param   string      String to convert to XML format.
1342      * @param   encoding    CURRENTLY NOT IMPLEMENTED.
1343      *
1344      * @throws org.xml.sax.SAXException
1345      */
1346     public void writeAttrString(
1347         final java.io.Writer writer, String string, String encoding)
1348         throws IOException
1349     {
1350         final int end = string.length();
1351         if (end > m_attrBuff.length)
1352         {
1353             m_attrBuff = new char[end * 2 + 1];
1354         }
1355         string.getChars(0, end, m_attrBuff, 0);
1356         final char[] chars = m_attrBuff;
1357 
1358 
1359 
1360         int cleanStart = 0;
1361         int cleanLength = 0;
1362 
1363         char ch = 0;
1364         for (int i = 0; i < end; i++)
1365         {
1366             ch = chars[i];
1367 
1368             // System.out.println("SPECIALSSIZE: "+SPECIALSSIZE);
1369             // System.out.println("ch: "+(int)ch);
1370             // System.out.println("m_maxCharacter: "+(int)m_maxCharacter);
1371             // System.out.println("m_attrCharsMap[ch]: "+(int)m_attrCharsMap[ch]);
1372             if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch)))
1373             {
1374                 cleanLength++;
1375             }
1376             else if ('<' == ch || '>' == ch)
1377             {
1378                 cleanLength++; // no escaping in this case, as specified in 15.2
1379             }
1380             else if (
1381                 ('&' == ch) && ((i + 1) < end) && ('{' == chars[i + 1]))
1382             {
1383                 cleanLength++; // no escaping in this case, as specified in 15.2
1384             }
1385             else
1386             {
1387                 if (cleanLength > 0)
1388                 {
1389                     writer.write(chars,cleanStart,cleanLength);
1390                     cleanLength = 0;
1391                 }
1392                 int pos = accumDefaultEntity(writer, ch, i, chars, end, false, true);
1393 
1394                 if (i != pos)
1395                 {
1396                     i = pos - 1;
1397                 }
1398                 else
1399                 {
1400                     if (Encodings.isHighUTF16Surrogate(ch))
1401                     {
1402 
1403                             writeUTF16Surrogate(ch, chars, i, end);
1404                             i++; // two input characters processed
1405                                  // this increments by one and the for()
1406                                  // loop itself increments by another one.
1407                     }
1408 
1409                     // The next is kind of a hack to keep from escaping in the case
1410                     // of Shift_JIS and the like.
1411 
1412                     /*
1413                     else if ((ch < m_maxCharacter) && (m_maxCharacter == 0xFFFF)
1414                     && (ch != 160))
1415                     {
1416                     writer.write(ch);  // no escaping in this case
1417                     }
1418                     else
1419                     */
1420                     String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
1421                     if (null != outputStringForChar)
1422                     {
1423                         writer.write(outputStringForChar);
1424                     }
1425                     else if (escapingNotNeeded(ch))
1426                     {
1427                         writer.write(ch); // no escaping in this case
1428                     }
1429                     else
1430                     {
1431                         writer.write("&#");
1432                         writer.write(Integer.toString(ch));
1433                         writer.write(';');
1434                     }
1435                 }
1436                 cleanStart = i + 1;
1437             }
1438         } // end of for()
1439 
1440         // are there any clean characters at the end of the array
1441         // that we haven't processed yet?
1442         if (cleanLength > 1)
1443         {
1444             // if the whole string can be written out as-is do so
1445             // otherwise write out the clean chars at the end of the
1446             // array
1447             if (cleanStart == 0)
1448                 writer.write(string);
1449             else
1450                 writer.write(chars, cleanStart, cleanLength);
1451         }
1452         else if (cleanLength == 1)
1453         {
1454             // a little optimization for 1 clean character
1455             // (we could have let the previous if(...) handle them all)
1456             writer.write(ch);
1457         }
1458     }
1459 
1460 
1461 
1462     /**
1463      * Receive notification of character data.
1464      *
1465      * <p>The Parser will call this method to report each chunk of
1466      * character data.  SAX parsers may return all contiguous character
1467      * data in a single chunk, or they may split it into several
1468      * chunks; however, all of the characters in any single event
1469      * must come from the same external entity, so that the Locator
1470      * provides useful information.</p>
1471      *
1472      * <p>The application must not attempt to read from the array
1473      * outside of the specified range.</p>
1474      *
1475      * <p>Note that some parsers will report whitespace using the
1476      * ignorableWhitespace() method rather than this one (validating
1477      * parsers must do so).</p>
1478      *
1479      * @param chars The characters from the XML document.
1480      * @param start The start position in the array.
1481      * @param length The number of characters to read from the array.
1482      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1483      *            wrapping another exception.
1484      * @see #ignorableWhitespace
1485      * @see org.xml.sax.Locator
1486      *
1487      * @throws org.xml.sax.SAXException
1488      */
1489     public final void characters(char chars[], int start, int length)
1490         throws org.xml.sax.SAXException
1491     {
1492 
1493         if (m_elemContext.m_isRaw)
1494         {
1495             try
1496             {
1497                 if (m_elemContext.m_startTagOpen)
1498                 {
1499                     closeStartTag();
1500                     m_elemContext.m_startTagOpen = false;
1501                 }
1502                 m_ispreserve = true;
1503 
1504 //              With m_ispreserve just set true it looks like shouldIndent()
1505 //              will always return false, so drop any possible indentation.
1506 //              if (shouldIndent())
1507 //                  indent();
1508 
1509                 // writer.write("<![CDATA[");
1510                 // writer.write(chars, start, length);
1511                 writeNormalizedChars(chars, start, length, false, m_lineSepUse);
1512 
1513                 // writer.write("]]>");
1514 
1515                 // time to generate characters event
1516                 if (m_tracer != null)
1517                     super.fireCharEvent(chars, start, length);
1518 
1519                 return;
1520             }
1521             catch (IOException ioe)
1522             {
1523                 throw new org.xml.sax.SAXException(
1524                     Utils.messages.createMessage(
1525                         MsgKey.ER_OIERROR,
1526                         null),
1527                     ioe);
1528                 //"IO error", ioe);
1529             }
1530         }
1531         else
1532         {
1533             super.characters(chars, start, length);
1534         }
1535     }
1536 
1537     /**
1538      *  Receive notification of cdata.
1539      *
1540      *  <p>The Parser will call this method to report each chunk of
1541      *  character data.  SAX parsers may return all contiguous character
1542      *  data in a single chunk, or they may split it into several
1543      *  chunks; however, all of the characters in any single event
1544      *  must come from the same external entity, so that the Locator
1545      *  provides useful information.</p>
1546      *
1547      *  <p>The application must not attempt to read from the array
1548      *  outside of the specified range.</p>
1549      *
1550      *  <p>Note that some parsers will report whitespace using the
1551      *  ignorableWhitespace() method rather than this one (validating
1552      *  parsers must do so).</p>
1553      *
1554      *  @param ch The characters from the XML document.
1555      *  @param start The start position in the array.
1556      *  @param length The number of characters to read from the array.
1557      *  @throws org.xml.sax.SAXException Any SAX exception, possibly
1558      *             wrapping another exception.
1559      *  @see #ignorableWhitespace
1560      *  @see org.xml.sax.Locator
1561      *
1562      * @throws org.xml.sax.SAXException
1563      */
1564     public final void cdata(char ch[], int start, int length)
1565         throws org.xml.sax.SAXException
1566     {
1567 
1568         if ((null != m_elemContext.m_elementName)
1569             && (m_elemContext.m_elementName.equalsIgnoreCase("SCRIPT")
1570                 || m_elemContext.m_elementName.equalsIgnoreCase("STYLE")))
1571         {
1572             try
1573             {
1574                 if (m_elemContext.m_startTagOpen)
1575                 {
1576                     closeStartTag();
1577                     m_elemContext.m_startTagOpen = false;
1578                 }
1579 
1580                 m_ispreserve = true;
1581 
1582                 if (shouldIndent())
1583                     indent();
1584 
1585                 // writer.write(ch, start, length);
1586                 writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1587             }
1588             catch (IOException ioe)
1589             {
1590                 throw new org.xml.sax.SAXException(
1591                     Utils.messages.createMessage(
1592                         MsgKey.ER_OIERROR,
1593                         null),
1594                     ioe);
1595                 //"IO error", ioe);
1596             }
1597         }
1598         else
1599         {
1600             super.cdata(ch, start, length);
1601         }
1602     }
1603 
1604     /**
1605      *  Receive notification of a processing instruction.
1606      *
1607      *  @param target The processing instruction target.
1608      *  @param data The processing instruction data, or null if
1609      *         none was supplied.
1610      *  @throws org.xml.sax.SAXException Any SAX exception, possibly
1611      *             wrapping another exception.
1612      *
1613      * @throws org.xml.sax.SAXException
1614      */
1615     public void processingInstruction(String target, String data)
1616         throws org.xml.sax.SAXException
1617     {
1618 
1619         // Process any pending starDocument and startElement first.
1620         flushPending();
1621 
1622         // Use a fairly nasty hack to tell if the next node is supposed to be
1623         // unescaped text.
1624         if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
1625         {
1626             startNonEscaping();
1627         }
1628         else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
1629         {
1630             endNonEscaping();
1631         }
1632         else
1633         {
1634             try
1635             {
1636             if (m_elemContext.m_startTagOpen)
1637             {
1638                 closeStartTag();
1639                 m_elemContext.m_startTagOpen = false;
1640             }
1641             else if (m_needToCallStartDocument)
1642                 startDocumentInternal();
1643 
1644             if (shouldIndent())
1645                 indent();
1646 
1647             final java.io.Writer writer = m_writer;
1648             //writer.write("<?" + target);
1649             writer.write("<?");
1650             writer.write(target);
1651 
1652             if (data.length() > 0 && !Character.isSpaceChar(data.charAt(0)))
1653                 writer.write(' ');
1654 
1655             //writer.write(data + ">"); // different from XML
1656             writer.write(data); // different from XML
1657             writer.write('>'); // different from XML
1658 
1659             // Always output a newline char if not inside of an
1660             // element. The whitespace is not significant in that
1661             // case.
1662             if (m_elemContext.m_currentElemDepth <= 0)
1663                 outputLineSep();
1664 
1665             m_startNewLine = true;
1666             }
1667             catch(IOException e)
1668             {
1669                 throw new SAXException(e);
1670             }
1671         }
1672 
1673         // now generate the PI event
1674         if (m_tracer != null)
1675             super.fireEscapingEvent(target, data);
1676      }
1677 
1678     /**
1679      * Receive notivication of a entityReference.
1680      *
1681      * @param name non-null reference to entity name string.
1682      *
1683      * @throws org.xml.sax.SAXException
1684      */
1685     public final void entityReference(String name)
1686         throws org.xml.sax.SAXException
1687     {
1688         try
1689         {
1690 
1691         final java.io.Writer writer = m_writer;
1692         writer.write('&');
1693         writer.write(name);
1694         writer.write(';');
1695 
1696         } catch(IOException e)
1697         {
1698             throw new SAXException(e);
1699         }
1700     }
1701     /**
1702      * @see ExtendedContentHandler#endElement(String)
1703      */
1704     public final void endElement(String elemName) throws SAXException
1705     {
1706         endElement(null, null, elemName);
1707     }
1708 
1709     /**
1710      * Process the attributes, which means to write out the currently
1711      * collected attributes to the writer. The attributes are not
1712      * cleared by this method
1713      *
1714      * @param writer the writer to write processed attributes to.
1715      * @param nAttrs the number of attributes in m_attributes
1716      * to be processed
1717      *
1718      * @throws org.xml.sax.SAXException
1719      */
1720     public void processAttributes(java.io.Writer writer, int nAttrs)
1721         throws IOException,SAXException
1722     {
1723             /*
1724              * process the collected attributes
1725              */
1726             for (int i = 0; i < nAttrs; i++)
1727             {
1728                 processAttribute(
1729                     writer,
1730                     m_attributes.getQName(i),
1731                     m_attributes.getValue(i),
1732                     m_elemContext.m_elementDesc);
1733             }
1734     }
1735 
1736     /**
1737      * For the enclosing elements starting tag write out out any attributes
1738      * followed by ">"
1739      *
1740      *@throws org.xml.sax.SAXException
1741      */
1742     protected void closeStartTag() throws SAXException
1743     {
1744             try
1745             {
1746 
1747             // finish processing attributes, time to fire off the start element event
1748             if (m_tracer != null)
1749                 super.fireStartElem(m_elemContext.m_elementName);
1750 
1751             int nAttrs = m_attributes.getLength();
1752             if (nAttrs>0)
1753             {
1754                 processAttributes(m_writer, nAttrs);
1755                 // clear attributes object for re-use with next element
1756                 m_attributes.clear();
1757             }
1758 
1759             m_writer.write('>');
1760 
1761             /* whether Xalan or XSLTC, we have the prefix mappings now, so
1762              * lets determine if the current element is specified in the cdata-
1763              * section-elements list.
1764              */
1765             if (m_cdataSectionElements != null)
1766                 m_elemContext.m_isCdataSection = isCdataSection();
1767             if (m_doIndent)
1768             {
1769                 m_isprevtext = false;
1770                 m_preserves.push(m_ispreserve);
1771             }
1772 
1773             }
1774             catch(IOException e)
1775             {
1776                 throw new SAXException(e);
1777             }
1778     }
1779     /**
1780      * Initialize the serializer with the specified output stream and output
1781      * format. Must be called before calling any of the serialize methods.
1782      *
1783      * @param output The output stream to use
1784      * @param format The output format
1785      * @throws UnsupportedEncodingException The encoding specified   in the
1786      * output format is not supported
1787      */
1788     protected synchronized void init(OutputStream output, Properties format)
1789         throws UnsupportedEncodingException
1790     {
1791         if (null == format)
1792         {
1793             format = OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
1794          }
1795         super.init(output,format, false);
1796     }
1797 
1798         /**
1799          * Specifies an output stream to which the document should be
1800          * serialized. This method should not be called while the
1801          * serializer is in the process of serializing a document.
1802          * <p>
1803          * The encoding specified in the output properties is used, or
1804          * if no encoding was specified, the default for the selected
1805          * output method.
1806          *
1807          * @param output The output stream
1808          */
1809         public void setOutputStream(OutputStream output)
1810         {
1811 
1812             try
1813             {
1814                 Properties format;
1815                 if (null == m_format)
1816                     format = OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
1817                 else
1818                     format = m_format;
1819                 init(output, format, true);
1820             }
1821             catch (UnsupportedEncodingException uee)
1822             {
1823 
1824                 // Should have been warned in init, I guess...
1825             }
1826         }
1827         /**
1828          * This method is used when a prefix/uri namespace mapping
1829          * is indicated after the element was started with a
1830          * startElement() and before and endElement().
1831          * startPrefixMapping(prefix,uri) would be used before the
1832          * startElement() call.
1833          * @param uri the URI of the namespace
1834          * @param prefix the prefix associated with the given URI.
1835          *
1836          * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
1837          */
1838         public void namespaceAfterStartElement(String prefix, String uri)
1839             throws SAXException
1840         {
1841             // hack for XSLTC with finding URI for default namespace
1842             if (m_elemContext.m_elementURI == null)
1843             {
1844                 String prefix1 = getPrefixPart(m_elemContext.m_elementName);
1845                 if (prefix1 == null && EMPTYSTRING.equals(prefix))
1846                 {
1847                     // the elements URI is not known yet, and it
1848                     // doesn't have a prefix, and we are currently
1849                     // setting the uri for prefix "", so we have
1850                     // the uri for the element... lets remember it
1851                     m_elemContext.m_elementURI = uri;
1852                 }
1853             }
1854             startPrefixMapping(prefix,uri,false);
1855         }
1856 
1857     public void startDTD(String name, String publicId, String systemId)
1858         throws SAXException
1859     {
1860         m_inDTD = true;
1861         super.startDTD(name, publicId, systemId);
1862     }
1863 
1864     /**
1865      * Report the end of DTD declarations.
1866      * @throws org.xml.sax.SAXException The application may raise an exception.
1867      * @see #startDTD
1868      */
1869     public void endDTD() throws org.xml.sax.SAXException
1870     {
1871         m_inDTD = false;
1872         /* for ToHTMLStream the DOCTYPE is entirely output in the
1873          * startDocumentInternal() method, so don't do anything here
1874          */
1875     }
1876     /**
1877      * This method does nothing.
1878      */
1879     public void attributeDecl(
1880         String eName,
1881         String aName,
1882         String type,
1883         String valueDefault,
1884         String value)
1885         throws SAXException
1886     {
1887         // The internal DTD subset is not serialized by the ToHTMLStream serializer
1888     }
1889 
1890     /**
1891      * This method does nothing.
1892      */
1893     public void elementDecl(String name, String model) throws SAXException
1894     {
1895         // The internal DTD subset is not serialized by the ToHTMLStream serializer
1896     }
1897     /**
1898      * This method does nothing.
1899      */
1900     public void internalEntityDecl(String name, String value)
1901         throws SAXException
1902     {
1903         // The internal DTD subset is not serialized by the ToHTMLStream serializer
1904     }
1905     /**
1906      * This method does nothing.
1907      */
1908     public void externalEntityDecl(
1909         String name,
1910         String publicId,
1911         String systemId)
1912         throws SAXException
1913     {
1914         // The internal DTD subset is not serialized by the ToHTMLStream serializer
1915     }
1916 
1917     /**
1918      * This method is used to add an attribute to the currently open element.
1919      * The caller has guaranted that this attribute is unique, which means that it
1920      * not been seen before and will not be seen again.
1921      *
1922      * @param name the qualified name of the attribute
1923      * @param value the value of the attribute which can contain only
1924      * ASCII printable characters characters in the range 32 to 127 inclusive.
1925      * @param flags the bit values of this integer give optimization information.
1926      */
1927     public void addUniqueAttribute(String name, String value, int flags)
1928         throws SAXException
1929     {
1930         try
1931         {
1932             final java.io.Writer writer = m_writer;
1933             if ((flags & NO_BAD_CHARS) > 0 && m_htmlcharInfo.onlyQuotAmpLtGt)
1934             {
1935                 // "flags" has indicated that the characters
1936                 // '>'  '<'   '&'  and '"' are not in the value and
1937                 // m_htmlcharInfo has recorded that there are no other
1938                 // entities in the range 0 to 127 so we write out the
1939                 // value directly
1940                 writer.write(' ');
1941                 writer.write(name);
1942                 writer.write("=\"");
1943                 writer.write(value);
1944                 writer.write('"');
1945             }
1946             else if (
1947                 (flags & HTML_ATTREMPTY) > 0
1948                     && (value.length() == 0 || value.equalsIgnoreCase(name)))
1949             {
1950                 writer.write(' ');
1951                 writer.write(name);
1952             }
1953             else
1954             {
1955                 writer.write(' ');
1956                 writer.write(name);
1957                 writer.write("=\"");
1958                 if ((flags & HTML_ATTRURL) > 0)
1959                 {
1960                     writeAttrURI(writer, value, m_specialEscapeURLs);
1961                 }
1962                 else
1963                 {
1964                     writeAttrString(writer, value, this.getEncoding());
1965                 }
1966                 writer.write('"');
1967             }
1968         } catch (IOException e) {
1969             throw new SAXException(e);
1970         }
1971     }
1972 
1973     public void comment(char ch[], int start, int length)
1974             throws SAXException
1975     {
1976         // The internal DTD subset is not serialized by the ToHTMLStream serializer
1977         if (m_inDTD)
1978             return;
1979         super.comment(ch, start, length);
1980     }
1981 
1982     public boolean reset()
1983     {
1984         boolean ret = super.reset();
1985         if (!ret)
1986             return false;
1987         initToHTMLStream();
1988         return true;
1989     }
1990 
1991     private void initToHTMLStream()
1992     {
1993 //        m_elementDesc = null;
1994         m_inBlockElem = false;
1995         m_inDTD = false;
1996 //        m_isRawStack.clear();
1997         m_omitMetaTag = false;
1998         m_specialEscapeURLs = true;
1999     }
2000 
2001     static class Trie
2002     {
2003         /**
2004          * A digital search trie for 7-bit ASCII text
2005          * The API is a subset of java.util.Hashtable
2006          * The key must be a 7-bit ASCII string
2007          * The value may be any Java Object
2008          * One can get an object stored in a trie from its key,
2009          * but the search is either case sensitive or case
2010          * insensitive to the characters in the key, and this
2011          * choice of sensitivity or insensitivity is made when
2012          * the Trie is created, before any objects are put in it.
2013          *
2014          * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
2015          * It exists to cut the serializers dependancy on that package.
2016          *
2017          * @xsl.usage internal
2018          */
2019 
2020         /** Size of the m_nextChar array.  */
2021         public static final int ALPHA_SIZE = 128;
2022 
2023         /** The root node of the tree.    */
2024         final Node m_Root;
2025 
2026         /** helper buffer to convert Strings to char arrays */
2027         private char[] m_charBuffer = new char[0];
2028 
2029         /** true if the search for an object is lower case only with the key */
2030         private final boolean m_lowerCaseOnly;
2031 
2032         /**
2033          * Construct the trie that has a case insensitive search.
2034          */
2035         public Trie()
2036         {
2037             m_Root = new Node();
2038             m_lowerCaseOnly = false;
2039         }
2040 
2041         /**
2042          * Construct the trie given the desired case sensitivity with the key.
2043          * @param lowerCaseOnly true if the search keys are to be loser case only,
2044          * not case insensitive.
2045          */
2046         public Trie(boolean lowerCaseOnly)
2047         {
2048             m_Root = new Node();
2049             m_lowerCaseOnly = lowerCaseOnly;
2050         }
2051 
2052         /**
2053          * Put an object into the trie for lookup.
2054          *
2055          * @param key must be a 7-bit ASCII string
2056          * @param value any java object.
2057          *
2058          * @return The old object that matched key, or null.
2059          */
2060         public Object put(String key, Object value)
2061         {
2062 
2063             final int len = key.length();
2064             if (len > m_charBuffer.length)
2065             {
2066                 // make the biggest buffer ever needed in get(String)
2067                 m_charBuffer = new char[len];
2068             }
2069 
2070             Node node = m_Root;
2071 
2072             for (int i = 0; i < len; i++)
2073             {
2074                 Node nextNode =
2075                     node.m_nextChar[Character.toLowerCase(key.charAt(i))];
2076 
2077                 if (nextNode != null)
2078                 {
2079                     node = nextNode;
2080                 }
2081                 else
2082                 {
2083                     for (; i < len; i++)
2084                     {
2085                         Node newNode = new Node();
2086                         if (m_lowerCaseOnly)
2087                         {
2088                             // put this value into the tree only with a lower case key
2089                             node.m_nextChar[Character.toLowerCase(
2090                                 key.charAt(i))] =
2091                                 newNode;
2092                         }
2093                         else
2094                         {
2095                             // put this value into the tree with a case insensitive key
2096                             node.m_nextChar[Character.toUpperCase(
2097                                 key.charAt(i))] =
2098                                 newNode;
2099                             node.m_nextChar[Character.toLowerCase(
2100                                 key.charAt(i))] =
2101                                 newNode;
2102                         }
2103                         node = newNode;
2104                     }
2105                     break;
2106                 }
2107             }
2108 
2109             Object ret = node.m_Value;
2110 
2111             node.m_Value = value;
2112 
2113             return ret;
2114         }
2115 
2116         /**
2117          * Get an object that matches the key.
2118          *
2119          * @param key must be a 7-bit ASCII string
2120          *
2121          * @return The object that matches the key, or null.
2122          */
2123         public Object get(final String key)
2124         {
2125 
2126             final int len = key.length();
2127 
2128             /* If the name is too long, we won't find it, this also keeps us
2129              * from overflowing m_charBuffer
2130              */
2131             if (m_charBuffer.length < len)
2132                 return null;
2133 
2134             Node node = m_Root;
2135             switch (len) // optimize the look up based on the number of chars
2136             {
2137                 // case 0 looks silly, but the generated bytecode runs
2138                 // faster for lookup of elements of length 2 with this in
2139                 // and a fair bit faster.  Don't know why.
2140                 case 0 :
2141                     {
2142                         return null;
2143                     }
2144 
2145                 case 1 :
2146                     {
2147                         final char ch = key.charAt(0);
2148                         if (ch < ALPHA_SIZE)
2149                         {
2150                             node = node.m_nextChar[ch];
2151                             if (node != null)
2152                                 return node.m_Value;
2153                         }
2154                         return null;
2155                     }
2156                     //                comment out case 2 because the default is faster
2157                     //                case 2 :
2158                     //                    {
2159                     //                        final char ch0 = key.charAt(0);
2160                     //                        final char ch1 = key.charAt(1);
2161                     //                        if (ch0 < ALPHA_SIZE && ch1 < ALPHA_SIZE)
2162                     //                        {
2163                     //                            node = node.m_nextChar[ch0];
2164                     //                            if (node != null)
2165                     //                            {
2166                     //
2167                     //                                if (ch1 < ALPHA_SIZE)
2168                     //                                {
2169                     //                                    node = node.m_nextChar[ch1];
2170                     //                                    if (node != null)
2171                     //                                        return node.m_Value;
2172                     //                                }
2173                     //                            }
2174                     //                        }
2175                     //                        return null;
2176                     //                   }
2177                 default :
2178                     {
2179                         for (int i = 0; i < len; i++)
2180                         {
2181                             // A thread-safe way to loop over the characters
2182                             final char ch = key.charAt(i);
2183                             if (ALPHA_SIZE <= ch)
2184                             {
2185                                 // the key is not 7-bit ASCII so we won't find it here
2186                                 return null;
2187                             }
2188 
2189                             node = node.m_nextChar[ch];
2190                             if (node == null)
2191                                 return null;
2192                         }
2193 
2194                         return node.m_Value;
2195                     }
2196             }
2197         }
2198 
2199         /**
2200          * The node representation for the trie.
2201          * @xsl.usage internal
2202          */
2203         private class Node
2204         {
2205 
2206             /**
2207              * Constructor, creates a Node[ALPHA_SIZE].
2208              */
2209             Node()
2210             {
2211                 m_nextChar = new Node[ALPHA_SIZE];
2212                 m_Value = null;
2213             }
2214 
2215             /** The next nodes.   */
2216             final Node m_nextChar[];
2217 
2218             /** The value.   */
2219             Object m_Value;
2220         }
2221         /**
2222          * Construct the trie from another Trie.
2223          * Both the existing Trie and this new one share the same table for
2224          * lookup, and it is assumed that the table is fully populated and
2225          * not changing anymore.
2226          *
2227          * @param existingTrie the Trie that this one is a copy of.
2228          */
2229         public Trie(Trie existingTrie)
2230         {
2231             // copy some fields from the existing Trie into this one.
2232             m_Root = existingTrie.m_Root;
2233             m_lowerCaseOnly = existingTrie.m_lowerCaseOnly;
2234 
2235             // get a buffer just big enough to hold the longest key in the table.
2236             int max = existingTrie.getLongestKeyLength();
2237             m_charBuffer = new char[max];
2238         }
2239 
2240         /**
2241          * Get an object that matches the key.
2242          * This method is faster than get(), but is not thread-safe.
2243          *
2244          * @param key must be a 7-bit ASCII string
2245          *
2246          * @return The object that matches the key, or null.
2247          */
2248         public Object get2(final String key)
2249         {
2250 
2251             final int len = key.length();
2252 
2253             /* If the name is too long, we won't find it, this also keeps us
2254              * from overflowing m_charBuffer
2255              */
2256             if (m_charBuffer.length < len)
2257                 return null;
2258 
2259             Node node = m_Root;
2260             switch (len) // optimize the look up based on the number of chars
2261             {
2262                 // case 0 looks silly, but the generated bytecode runs
2263                 // faster for lookup of elements of length 2 with this in
2264                 // and a fair bit faster.  Don't know why.
2265                 case 0 :
2266                     {
2267                         return null;
2268                     }
2269 
2270                 case 1 :
2271                     {
2272                         final char ch = key.charAt(0);
2273                         if (ch < ALPHA_SIZE)
2274                         {
2275                             node = node.m_nextChar[ch];
2276                             if (node != null)
2277                                 return node.m_Value;
2278                         }
2279                         return null;
2280                     }
2281                 default :
2282                     {
2283                         /* Copy string into array. This is not thread-safe because
2284                          * it modifies the contents of m_charBuffer. If multiple
2285                          * threads were to use this Trie they all would be
2286                          * using this same array (not good). So this
2287                          * method is not thread-safe, but it is faster because
2288                          * converting to a char[] and looping over elements of
2289                          * the array is faster than a String's charAt(i).
2290                          */
2291                         key.getChars(0, len, m_charBuffer, 0);
2292 
2293                         for (int i = 0; i < len; i++)
2294                         {
2295                             final char ch = m_charBuffer[i];
2296                             if (ALPHA_SIZE <= ch)
2297                             {
2298                                 // the key is not 7-bit ASCII so we won't find it here
2299                                 return null;
2300                             }
2301 
2302                             node = node.m_nextChar[ch];
2303                             if (node == null)
2304                                 return null;
2305                         }
2306 
2307                         return node.m_Value;
2308                     }
2309             }
2310         }
2311 
2312         /**
2313          * Get the length of the longest key used in the table.
2314          */
2315         public int getLongestKeyLength()
2316         {
2317             return m_charBuffer.length;
2318         }
2319     }
2320 }