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: ToStream.java,v 1.4 2005/11/10 06:43: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.io.Writer;
  29 import java.util.Properties;
  30 import java.util.StringTokenizer;
  31 import java.util.Vector;
  32 
  33 import javax.xml.transform.ErrorListener;
  34 import javax.xml.transform.OutputKeys;
  35 import javax.xml.transform.Transformer;
  36 import javax.xml.transform.TransformerException;
  37 
  38 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  39 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  40 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
  41 import org.w3c.dom.Node;
  42 import org.xml.sax.Attributes;
  43 import org.xml.sax.ContentHandler;
  44 import org.xml.sax.SAXException;
  45 
  46 //import com.sun.media.sound.IESecurity;
  47 
  48 /**
  49  * This abstract class is a base class for other stream
  50  * serializers (xml, html, text ...) that write output to a stream.
  51  *
  52  * @xsl.usage internal
  53  */
  54 abstract public class ToStream extends SerializerBase
  55 {
  56 
  57     private static final String COMMENT_BEGIN = "<!--";
  58     private static final String COMMENT_END = "-->";
  59 
  60     /** Stack to keep track of disabling output escaping. */
  61     protected BoolStack m_disableOutputEscapingStates = new BoolStack();
  62 
  63 
  64     /**
  65      * The encoding information associated with this serializer.
  66      * Although initially there is no encoding,
  67      * there is a dummy EncodingInfo object that will say
  68      * that every character is in the encoding. This is useful
  69      * for a serializer that is in temporary output state and has
  70      * no associated encoding. A serializer in final output state
  71      * will have an encoding, and will worry about whether
  72      * single chars or surrogate pairs of high/low chars form
  73      * characters in the output encoding.
  74      */
  75     EncodingInfo m_encodingInfo = new EncodingInfo(null,null);
  76 
  77     /**
  78      * Method reference to the sun.io.CharToByteConverter#canConvert method
  79      * for this encoding.  Invalid if m_charToByteConverter is null.
  80      */
  81     java.lang.reflect.Method m_canConvertMeth;
  82 
  83 
  84 
  85     /**
  86      * Boolean that tells if we already tried to get the converter.
  87      */
  88     boolean m_triedToGetConverter = false;
  89 
  90 
  91     /**
  92      * Opaque reference to the sun.io.CharToByteConverter for this
  93      * encoding.
  94      */
  95     Object m_charToByteConverter = null;
  96 
  97 
  98     /**
  99      * Stack to keep track of whether or not we need to
 100      * preserve whitespace.
 101      *
 102      * Used to push/pop values used for the field m_ispreserve, but
 103      * m_ispreserve is only relevant if m_doIndent is true.
 104      * If m_doIndent is false this field has no impact.
 105      *
 106      */
 107     protected BoolStack m_preserves = new BoolStack();
 108 
 109     /**
 110      * State flag to tell if preservation of whitespace
 111      * is important.
 112      *
 113      * Used only in shouldIndent() but only if m_doIndent is true.
 114      * If m_doIndent is false this flag has no impact.
 115      *
 116      */
 117     protected boolean m_ispreserve = false;
 118 
 119     /**
 120      * State flag that tells if the previous node processed
 121      * was text, so we can tell if we should preserve whitespace.
 122      *
 123      * Used in endDocument() and shouldIndent() but
 124      * only if m_doIndent is true.
 125      * If m_doIndent is false this flag has no impact.
 126      */
 127     protected boolean m_isprevtext = false;
 128 
 129     /**
 130      * The maximum character size before we have to resort
 131      * to escaping.
 132      */
 133     protected int m_maxCharacter = Encodings.getLastPrintable();
 134 
 135 
 136     /**
 137      * The system line separator for writing out line breaks.
 138      * The default value is from the system property,
 139      * but this value can be set through the xsl:output
 140      * extension attribute xalan:line-separator.
 141      */
 142     protected char[] m_lineSep =
 143         System.getProperty("line.separator").toCharArray();
 144 
 145     /**
 146      * True if the the system line separator is to be used.
 147      */
 148     protected boolean m_lineSepUse = true;
 149 
 150     /**
 151      * The length of the line seperator, since the write is done
 152      * one character at a time.
 153      */
 154     protected int m_lineSepLen = m_lineSep.length;
 155 
 156     /**
 157      * Map that tells which characters should have special treatment, and it
 158      *  provides character to entity name lookup.
 159      */
 160     protected CharInfo m_charInfo;
 161 
 162     /** True if we control the buffer, and we should flush the output on endDocument. */
 163     boolean m_shouldFlush = true;
 164 
 165     /**
 166      * Add space before '/>' for XHTML.
 167      */
 168     protected boolean m_spaceBeforeClose = false;
 169 
 170     /**
 171      * Flag to signal that a newline should be added.
 172      *
 173      * Used only in indent() which is called only if m_doIndent is true.
 174      * If m_doIndent is false this flag has no impact.
 175      */
 176     boolean m_startNewLine;
 177 
 178     /**
 179      * Tells if we're in an internal document type subset.
 180      */
 181     protected boolean m_inDoctype = false;
 182 
 183     /**
 184        * Flag to quickly tell if the encoding is UTF8.
 185        */
 186     boolean m_isUTF8 = false;
 187 
 188     /** The xsl:output properties. */
 189     protected Properties m_format;
 190 
 191     /**
 192      * remembers if we are in between the startCDATA() and endCDATA() callbacks
 193      */
 194     protected boolean m_cdataStartCalled = false;
 195 
 196     /**
 197      * If this flag is true DTD entity references are not left as-is,
 198      * which is exiting older behavior.
 199      */
 200     private boolean m_expandDTDEntities = true;
 201 
 202 
 203     /**
 204      * Default constructor
 205      */
 206     public ToStream()
 207     {
 208     }
 209 
 210     /**
 211      * This helper method to writes out "]]>" when closing a CDATA section.
 212      *
 213      * @throws org.xml.sax.SAXException
 214      */
 215     protected void closeCDATA() throws org.xml.sax.SAXException
 216     {
 217         try
 218         {
 219             m_writer.write(CDATA_DELIMITER_CLOSE);
 220             // write out a CDATA section closing "]]>"
 221             m_cdataTagOpen = false; // Remember that we have done so.
 222         }
 223         catch (IOException e)
 224         {
 225             throw new SAXException(e);
 226         }
 227     }
 228 
 229     /**
 230      * Serializes the DOM node. Throws an exception only if an I/O
 231      * exception occured while serializing.
 232      *
 233      * @param node Node to serialize.
 234      * @throws IOException An I/O exception occured while serializing
 235      */
 236     public void serialize(Node node) throws IOException
 237     {
 238 
 239         try
 240         {
 241             TreeWalker walker =
 242                 new TreeWalker(this);
 243 
 244             walker.traverse(node);
 245         }
 246         catch (org.xml.sax.SAXException se)
 247         {
 248             throw new WrappedRuntimeException(se);
 249         }
 250     }
 251 
 252     /**
 253      * Return true if the character is the high member of a surrogate pair.
 254      *
 255      * NEEDSDOC @param c
 256      *
 257      * NEEDSDOC ($objectName$) @return
 258      */
 259     static final boolean isUTF16Surrogate(char c)
 260     {
 261         return (c & 0xFC00) == 0xD800;
 262     }
 263 
 264     /**
 265      * Taken from XSLTC
 266      */
 267     private boolean m_escaping = true;
 268 
 269     /**
 270      * Flush the formatter's result stream.
 271      *
 272      * @throws org.xml.sax.SAXException
 273      */
 274     protected final void flushWriter() throws org.xml.sax.SAXException
 275     {
 276         final java.io.Writer writer = m_writer;
 277         if (null != writer)
 278         {
 279             try
 280             {
 281                 if (writer instanceof WriterToUTF8Buffered)
 282                 {
 283                     if (m_shouldFlush)
 284                          ((WriterToUTF8Buffered) writer).flush();
 285                     else
 286                          ((WriterToUTF8Buffered) writer).flushBuffer();
 287                 }
 288                 if (writer instanceof WriterToASCI)
 289                 {
 290                     if (m_shouldFlush)
 291                         writer.flush();
 292                 }
 293                 else
 294                 {
 295                     // Flush always.
 296                     // Not a great thing if the writer was created
 297                     // by this class, but don't have a choice.
 298                     writer.flush();
 299                 }
 300             }
 301             catch (IOException ioe)
 302             {
 303                 throw new org.xml.sax.SAXException(ioe);
 304             }
 305         }
 306     }
 307 
 308     /**
 309      * Get the output stream where the events will be serialized to.
 310      *
 311      * @return reference to the result stream, or null of only a writer was
 312      * set.
 313      */
 314     public OutputStream getOutputStream()
 315     {
 316 
 317         if (m_writer instanceof WriterToUTF8Buffered)
 318             return ((WriterToUTF8Buffered) m_writer).getOutputStream();
 319         if (m_writer instanceof WriterToASCI)
 320             return ((WriterToASCI) m_writer).getOutputStream();
 321         else
 322             return null;
 323     }
 324 
 325     // Implement DeclHandler
 326 
 327     /**
 328      *   Report an element type declaration.
 329      *
 330      *   <p>The content model will consist of the string "EMPTY", the
 331      *   string "ANY", or a parenthesised group, optionally followed
 332      *   by an occurrence indicator.  The model will be normalized so
 333      *   that all whitespace is removed,and will include the enclosing
 334      *   parentheses.</p>
 335      *
 336      *   @param name The element type name.
 337      *   @param model The content model as a normalized string.
 338      *   @exception SAXException The application may raise an exception.
 339      */
 340     public void elementDecl(String name, String model) throws SAXException
 341     {
 342         // Do not inline external DTD
 343         if (m_inExternalDTD)
 344             return;
 345         try
 346         {
 347             final java.io.Writer writer = m_writer;
 348             DTDprolog();
 349 
 350             writer.write("<!ELEMENT ");
 351             writer.write(name);
 352             writer.write(' ');
 353             writer.write(model);
 354             writer.write('>');
 355             writer.write(m_lineSep, 0, m_lineSepLen);
 356         }
 357         catch (IOException e)
 358         {
 359             throw new SAXException(e);
 360         }
 361 
 362     }
 363 
 364     /**
 365      * Report an internal entity declaration.
 366      *
 367      * <p>Only the effective (first) declaration for each entity
 368      * will be reported.</p>
 369      *
 370      * @param name The name of the entity.  If it is a parameter
 371      *        entity, the name will begin with '%'.
 372      * @param value The replacement text of the entity.
 373      * @exception SAXException The application may raise an exception.
 374      * @see #externalEntityDecl
 375      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
 376      */
 377     public void internalEntityDecl(String name, String value)
 378         throws SAXException
 379     {
 380         // Do not inline external DTD
 381         if (m_inExternalDTD)
 382             return;
 383         try
 384         {
 385             DTDprolog();
 386             outputEntityDecl(name, value);
 387         }
 388         catch (IOException e)
 389         {
 390             throw new SAXException(e);
 391         }
 392 
 393     }
 394 
 395     /**
 396      * Output the doc type declaration.
 397      *
 398      * @param name non-null reference to document type name.
 399      * NEEDSDOC @param value
 400      *
 401      * @throws org.xml.sax.SAXException
 402      */
 403     void outputEntityDecl(String name, String value) throws IOException
 404     {
 405         final java.io.Writer writer = m_writer;
 406         writer.write("<!ENTITY ");
 407         writer.write(name);
 408         writer.write(" \"");
 409         writer.write(value);
 410         writer.write("\">");
 411         writer.write(m_lineSep, 0, m_lineSepLen);
 412     }
 413 
 414     /**
 415      * Output a system-dependent line break.
 416      *
 417      * @throws org.xml.sax.SAXException
 418      */
 419     protected final void outputLineSep() throws IOException
 420     {
 421 
 422         m_writer.write(m_lineSep, 0, m_lineSepLen);
 423     }
 424 
 425     /**
 426      * Specifies an output format for this serializer. It the
 427      * serializer has already been associated with an output format,
 428      * it will switch to the new format. This method should not be
 429      * called while the serializer is in the process of serializing
 430      * a document.
 431      *
 432      * @param format The output format to use
 433      */
 434     public void setOutputFormat(Properties format)
 435     {
 436 
 437         boolean shouldFlush = m_shouldFlush;
 438 
 439         init(m_writer, format, false, false);
 440 
 441         m_shouldFlush = shouldFlush;
 442     }
 443 
 444     /**
 445      * Initialize the serializer with the specified writer and output format.
 446      * Must be called before calling any of the serialize methods.
 447      * This method can be called multiple times and the xsl:output properties
 448      * passed in the 'format' parameter are accumulated across calls.
 449      *
 450      * @param writer The writer to use
 451      * @param format The output format
 452      * @param shouldFlush True if the writer should be flushed at EndDocument.
 453      */
 454     private synchronized void init(
 455         Writer writer,
 456         Properties format,
 457         boolean defaultProperties,
 458         boolean shouldFlush)
 459     {
 460 
 461         m_shouldFlush = shouldFlush;
 462 
 463 
 464         // if we are tracing events we need to trace what
 465         // characters are written to the output writer.
 466         if (m_tracer != null
 467          && !(writer instanceof SerializerTraceWriter)  )
 468             m_writer = new SerializerTraceWriter(writer, m_tracer);
 469         else
 470             m_writer = writer;
 471 
 472 
 473         m_format = format;
 474         //        m_cdataSectionNames =
 475         //            OutputProperties.getQNameProperties(
 476         //                OutputKeys.CDATA_SECTION_ELEMENTS,
 477         //                format);
 478         setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format);
 479 
 480         setIndentAmount(
 481             OutputPropertyUtils.getIntProperty(
 482                 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,
 483                 format));
 484         setIndent(
 485             OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format));
 486 
 487         {
 488             String sep =
 489                     format.getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR);
 490             if (sep != null) {
 491                 m_lineSep = sep.toCharArray();
 492                 m_lineSepLen = sep.length();
 493             }
 494         }
 495 
 496         boolean shouldNotWriteXMLHeader =
 497             OutputPropertyUtils.getBooleanProperty(
 498                 OutputKeys.OMIT_XML_DECLARATION,
 499                 format);
 500         setOmitXMLDeclaration(shouldNotWriteXMLHeader);
 501         setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
 502         String doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
 503         setDoctypePublic(doctypePublic);
 504 
 505         // if standalone was explicitly specified
 506         if (format.get(OutputKeys.STANDALONE) != null)
 507         {
 508             String val = format.getProperty(OutputKeys.STANDALONE);
 509             if (defaultProperties)
 510                 setStandaloneInternal(val);
 511             else
 512                 setStandalone(val);
 513         }
 514 
 515         setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));
 516 
 517         if (null != doctypePublic)
 518         {
 519             if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
 520                 m_spaceBeforeClose = true;
 521         }
 522 
 523         /*
 524          * This code is added for XML 1.1 Version output.
 525          */
 526         String version = getVersion();
 527         if (null == version)
 528         {
 529             version = format.getProperty(OutputKeys.VERSION);
 530             setVersion(version);
 531         }
 532 
 533         // initCharsMap();
 534         String encoding = getEncoding();
 535         if (null == encoding)
 536         {
 537             encoding =
 538                 Encodings.getMimeEncoding(
 539                     format.getProperty(OutputKeys.ENCODING));
 540             setEncoding(encoding);
 541         }
 542 
 543         m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
 544 
 545         // Access this only from the Hashtable level... we don't want to
 546         // get default properties.
 547         String entitiesFileName =
 548             (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
 549 
 550         if (null != entitiesFileName)
 551         {
 552 
 553             String method =
 554                 (String) format.get(OutputKeys.METHOD);
 555 
 556             m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
 557         }
 558 
 559     }
 560 
 561     /**
 562      * Initialize the serializer with the specified writer and output format.
 563      * Must be called before calling any of the serialize methods.
 564      *
 565      * @param writer The writer to use
 566      * @param format The output format
 567      */
 568     private synchronized void init(Writer writer, Properties format)
 569     {
 570         init(writer, format, false, false);
 571     }
 572     /**
 573      * Initialize the serializer with the specified output stream and output
 574      * format. Must be called before calling any of the serialize methods.
 575      *
 576      * @param output The output stream to use
 577      * @param format The output format
 578      * @param defaultProperties true if the properties are the default
 579      * properties
 580      *
 581      * @throws UnsupportedEncodingException The encoding specified   in the
 582      * output format is not supported
 583      */
 584     protected synchronized void init(
 585         OutputStream output,
 586         Properties format,
 587         boolean defaultProperties)
 588         throws UnsupportedEncodingException
 589     {
 590 
 591         String encoding = getEncoding();
 592         if (encoding == null)
 593         {
 594             // if not already set then get it from the properties
 595             encoding =
 596                 Encodings.getMimeEncoding(
 597                     format.getProperty(OutputKeys.ENCODING));
 598             setEncoding(encoding);
 599         }
 600 
 601         if (encoding.equalsIgnoreCase("UTF-8"))
 602         {
 603             m_isUTF8 = true;
 604             //            if (output instanceof java.io.BufferedOutputStream)
 605             //            {
 606             //                init(new WriterToUTF8(output), format, defaultProperties, true);
 607             //            }
 608             //            else if (output instanceof java.io.FileOutputStream)
 609             //            {
 610             //                init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
 611             //            }
 612             //            else
 613             //            {
 614             //                // Not sure what to do in this case.  I'm going to be conservative
 615             //                // and not buffer.
 616             //                init(new WriterToUTF8(output), format, defaultProperties, true);
 617             //            }
 618 
 619 
 620                 init(
 621                     new WriterToUTF8Buffered(output),
 622                     format,
 623                     defaultProperties,
 624                     true);
 625 
 626 
 627         }
 628         else if (
 629             encoding.equals("WINDOWS-1250")
 630                 || encoding.equals("US-ASCII")
 631                 || encoding.equals("ASCII"))
 632         {
 633             init(new WriterToASCI(output), format, defaultProperties, true);
 634         }
 635         else
 636         {
 637             Writer osw;
 638 
 639             try
 640             {
 641                 osw = Encodings.getWriter(output, encoding);
 642             }
 643             catch (UnsupportedEncodingException uee)
 644             {
 645                 System.out.println(
 646                     "Warning: encoding \""
 647                         + encoding
 648                         + "\" not supported"
 649                         + ", using "
 650                         + Encodings.DEFAULT_MIME_ENCODING);
 651 
 652                 encoding = Encodings.DEFAULT_MIME_ENCODING;
 653                 setEncoding(encoding);
 654                 osw = Encodings.getWriter(output, encoding);
 655             }
 656 
 657             init(osw, format, defaultProperties, true);
 658         }
 659 
 660     }
 661 
 662     /**
 663      * Returns the output format for this serializer.
 664      *
 665      * @return The output format in use
 666      */
 667     public Properties getOutputFormat()
 668     {
 669         return m_format;
 670     }
 671 
 672     /**
 673      * Specifies a writer to which the document should be serialized.
 674      * This method should not be called while the serializer is in
 675      * the process of serializing a document.
 676      *
 677      * @param writer The output writer stream
 678      */
 679     public void setWriter(Writer writer)
 680     {
 681         // if we are tracing events we need to trace what
 682         // characters are written to the output writer.
 683         if (m_tracer != null
 684          && !(writer instanceof SerializerTraceWriter)  )
 685             m_writer = new SerializerTraceWriter(writer, m_tracer);
 686         else
 687             m_writer = writer;
 688     }
 689 
 690     /**
 691      * Set if the operating systems end-of-line line separator should
 692      * be used when serializing.  If set false NL character
 693      * (decimal 10) is left alone, otherwise the new-line will be replaced on
 694      * output with the systems line separator. For example on UNIX this is
 695      * NL, while on Windows it is two characters, CR NL, where CR is the
 696      * carriage-return (decimal 13).
 697      *
 698      * @param use_sytem_line_break True if an input NL is replaced with the
 699      * operating systems end-of-line separator.
 700      * @return The previously set value of the serializer.
 701      */
 702     public boolean setLineSepUse(boolean use_sytem_line_break)
 703     {
 704         boolean oldValue = m_lineSepUse;
 705         m_lineSepUse = use_sytem_line_break;
 706         return oldValue;
 707     }
 708 
 709     /**
 710      * Specifies an output stream to which the document should be
 711      * serialized. This method should not be called while the
 712      * serializer is in the process of serializing a document.
 713      * <p>
 714      * The encoding specified in the output properties is used, or
 715      * if no encoding was specified, the default for the selected
 716      * output method.
 717      *
 718      * @param output The output stream
 719      */
 720     public void setOutputStream(OutputStream output)
 721     {
 722 
 723         try
 724         {
 725             Properties format;
 726             if (null == m_format)
 727                 format =
 728                     OutputPropertiesFactory.getDefaultMethodProperties(
 729                         Method.XML);
 730             else
 731                 format = m_format;
 732             init(output, format, true);
 733         }
 734         catch (UnsupportedEncodingException uee)
 735         {
 736 
 737             // Should have been warned in init, I guess...
 738         }
 739     }
 740 
 741     /**
 742      * @see SerializationHandler#setEscaping(boolean)
 743      */
 744     public boolean setEscaping(boolean escape)
 745     {
 746         final boolean temp = m_escaping;
 747         m_escaping = escape;
 748         return temp;
 749 
 750     }
 751 
 752 
 753     /**
 754      * Might print a newline character and the indentation amount
 755      * of the given depth.
 756      *
 757      * @param depth the indentation depth (element nesting depth)
 758      *
 759      * @throws org.xml.sax.SAXException if an error occurs during writing.
 760      */
 761     protected void indent(int depth) throws IOException
 762     {
 763 
 764         if (m_startNewLine)
 765             outputLineSep();
 766         /* For m_indentAmount > 0 this extra test might be slower
 767          * but Xalan's default value is 0, so this extra test
 768          * will run faster in that situation.
 769          */
 770         if (m_indentAmount > 0)
 771             printSpace(depth * m_indentAmount);
 772 
 773     }
 774 
 775     /**
 776      * Indent at the current element nesting depth.
 777      * @throws IOException
 778      */
 779     protected void indent() throws IOException
 780     {
 781         indent(m_elemContext.m_currentElemDepth);
 782     }
 783     /**
 784      * Prints <var>n</var> spaces.
 785      * @param n         Number of spaces to print.
 786      *
 787      * @throws org.xml.sax.SAXException if an error occurs when writing.
 788      */
 789     private void printSpace(int n) throws IOException
 790     {
 791         final java.io.Writer writer = m_writer;
 792         for (int i = 0; i < n; i++)
 793         {
 794             writer.write(' ');
 795         }
 796 
 797     }
 798 
 799     /**
 800      * Report an attribute type declaration.
 801      *
 802      * <p>Only the effective (first) declaration for an attribute will
 803      * be reported.  The type will be one of the strings "CDATA",
 804      * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
 805      * "ENTITIES", or "NOTATION", or a parenthesized token group with
 806      * the separator "|" and all whitespace removed.</p>
 807      *
 808      * @param eName The name of the associated element.
 809      * @param aName The name of the attribute.
 810      * @param type A string representing the attribute type.
 811      * @param valueDefault A string representing the attribute default
 812      *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
 813      *        none of these applies.
 814      * @param value A string representing the attribute's default value,
 815      *        or null if there is none.
 816      * @exception SAXException The application may raise an exception.
 817      */
 818     public void attributeDecl(
 819         String eName,
 820         String aName,
 821         String type,
 822         String valueDefault,
 823         String value)
 824         throws SAXException
 825     {
 826         // Do not inline external DTD
 827         if (m_inExternalDTD)
 828             return;
 829         try
 830         {
 831             final java.io.Writer writer = m_writer;
 832             DTDprolog();
 833 
 834             writer.write("<!ATTLIST ");
 835             writer.write(eName);
 836             writer.write(' ');
 837 
 838             writer.write(aName);
 839             writer.write(' ');
 840             writer.write(type);
 841             if (valueDefault != null)
 842             {
 843                 writer.write(' ');
 844                 writer.write(valueDefault);
 845             }
 846 
 847             //writer.write(" ");
 848             //writer.write(value);
 849             writer.write('>');
 850             writer.write(m_lineSep, 0, m_lineSepLen);
 851         }
 852         catch (IOException e)
 853         {
 854             throw new SAXException(e);
 855         }
 856     }
 857 
 858     /**
 859      * Get the character stream where the events will be serialized to.
 860      *
 861      * @return Reference to the result Writer, or null.
 862      */
 863     public Writer getWriter()
 864     {
 865         return m_writer;
 866     }
 867 
 868     /**
 869      * Report a parsed external entity declaration.
 870      *
 871      * <p>Only the effective (first) declaration for each entity
 872      * will be reported.</p>
 873      *
 874      * @param name The name of the entity.  If it is a parameter
 875      *        entity, the name will begin with '%'.
 876      * @param publicId The declared public identifier of the entity, or
 877      *        null if none was declared.
 878      * @param systemId The declared system identifier of the entity.
 879      * @exception SAXException The application may raise an exception.
 880      * @see #internalEntityDecl
 881      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
 882      */
 883     public void externalEntityDecl(
 884         String name,
 885         String publicId,
 886         String systemId)
 887         throws SAXException
 888     {
 889         try {
 890             DTDprolog();
 891 
 892             m_writer.write("<!ENTITY ");
 893             m_writer.write(name);
 894             if (publicId != null) {
 895                 m_writer.write(" PUBLIC \"");
 896                 m_writer.write(publicId);
 897 
 898             }
 899             else {
 900                 m_writer.write(" SYSTEM \"");
 901                 m_writer.write(systemId);
 902             }
 903             m_writer.write("\" >");
 904             m_writer.write(m_lineSep, 0, m_lineSepLen);
 905         } catch (IOException e) {
 906             // TODO Auto-generated catch block
 907             e.printStackTrace();
 908         }
 909 
 910     }
 911 
 912     /**
 913      * Tell if this character can be written without escaping.
 914      */
 915     protected boolean escapingNotNeeded(char ch)
 916     {
 917         final boolean ret;
 918         if (ch < 127)
 919         {
 920             // This is the old/fast code here, but is this
 921             // correct for all encodings?
 922             if (ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch))
 923                 ret= true;
 924             else
 925                 ret = false;
 926         }
 927         else {
 928             ret = m_encodingInfo.isInEncoding(ch);
 929         }
 930         return ret;
 931     }
 932 
 933     /**
 934      * Once a surrogate has been detected, write out the pair of
 935      * characters if it is in the encoding, or if there is no
 936      * encoding, otherwise write out an entity reference
 937      * of the value of the unicode code point of the character
 938      * represented by the high/low surrogate pair.
 939      * <p>
 940      * An exception is thrown if there is no low surrogate in the pair,
 941      * because the array ends unexpectely, or if the low char is there
 942      * but its value is such that it is not a low surrogate.
 943      *
 944      * @param c the first (high) part of the surrogate, which
 945      * must be confirmed before calling this method.
 946      * @param ch Character array.
 947      * @param i position Where the surrogate was detected.
 948      * @param end The end index of the significant characters.
 949      * @return 0 if the pair of characters was written out as-is,
 950      * the unicode code point of the character represented by
 951      * the surrogate pair if an entity reference with that value
 952      * was written out.
 953      *
 954      * @throws IOException
 955      * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
 956      */
 957     protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
 958         throws IOException
 959     {
 960         int codePoint = 0;
 961         if (i + 1 >= end)
 962         {
 963             throw new IOException(
 964                 Utils.messages.createMessage(
 965                     MsgKey.ER_INVALID_UTF16_SURROGATE,
 966                     new Object[] { Integer.toHexString((int) c)}));
 967         }
 968 
 969         final char high = c;
 970         final char low = ch[i+1];
 971         if (!Encodings.isLowUTF16Surrogate(low)) {
 972             throw new IOException(
 973                 Utils.messages.createMessage(
 974                     MsgKey.ER_INVALID_UTF16_SURROGATE,
 975                     new Object[] {
 976                         Integer.toHexString((int) c)
 977                             + " "
 978                             + Integer.toHexString(low)}));
 979         }
 980 
 981         final java.io.Writer writer = m_writer;
 982 
 983         // If we make it to here we have a valid high, low surrogate pair
 984         if (m_encodingInfo.isInEncoding(c,low)) {
 985             // If the character formed by the surrogate pair
 986             // is in the encoding, so just write it out
 987             writer.write(ch,i,2);
 988         }
 989         else {
 990             // Don't know what to do with this char, it is
 991             // not in the encoding and not a high char in
 992             // a surrogate pair, so write out as an entity ref
 993             final String encoding = getEncoding();
 994             if (encoding != null) {
 995                 /* The output encoding is known,
 996                  * so somthing is wrong.
 997                   */
 998                 codePoint = Encodings.toCodePoint(high, low);
 999                 // not in the encoding, so write out a character reference
1000                 writer.write('&');
1001                 writer.write('#');
1002                 writer.write(Integer.toString(codePoint));
1003                 writer.write(';');
1004             } else {
1005                 /* The output encoding is not known,
1006                  * so just write it out as-is.
1007                  */
1008                 writer.write(ch, i, 2);
1009             }
1010         }
1011         // non-zero only if character reference was written out.
1012         return codePoint;
1013     }
1014 
1015     /**
1016      * Handle one of the default entities, return false if it
1017      * is not a default entity.
1018      *
1019      * @param ch character to be escaped.
1020      * @param i index into character array.
1021      * @param chars non-null reference to character array.
1022      * @param len length of chars.
1023      * @param fromTextNode true if the characters being processed
1024      * are from a text node, false if they are from an attribute value
1025      * @param escLF true if the linefeed should be escaped.
1026      *
1027      * @return i+1 if the character was written, else i.
1028      *
1029      * @throws java.io.IOException
1030      */
1031     protected int accumDefaultEntity(
1032         java.io.Writer writer,
1033         char ch,
1034         int i,
1035         char[] chars,
1036         int len,
1037         boolean fromTextNode,
1038         boolean escLF)
1039         throws IOException
1040     {
1041 
1042         if (!escLF && CharInfo.S_LINEFEED == ch)
1043         {
1044             writer.write(m_lineSep, 0, m_lineSepLen);
1045         }
1046         else
1047         {
1048             // if this is text node character and a special one of those,
1049             // or if this is a character from attribute value and a special one of those
1050             if ((fromTextNode && m_charInfo.isSpecialTextChar(ch)) || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch)))
1051             {
1052                 String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
1053 
1054                 if (null != outputStringForChar)
1055                 {
1056                     writer.write(outputStringForChar);
1057                 }
1058                 else
1059                     return i;
1060             }
1061             else
1062                 return i;
1063         }
1064 
1065         return i + 1;
1066 
1067     }
1068     /**
1069      * Normalize the characters, but don't escape.
1070      *
1071      * @param ch The characters from the XML document.
1072      * @param start The start position in the array.
1073      * @param length The number of characters to read from the array.
1074      * @param isCData true if a CDATA block should be built around the characters.
1075      * @param useSystemLineSeparator true if the operating systems
1076      * end-of-line separator should be output rather than a new-line character.
1077      *
1078      * @throws IOException
1079      * @throws org.xml.sax.SAXException
1080      */
1081     void writeNormalizedChars(
1082         char ch[],
1083         int start,
1084         int length,
1085         boolean isCData,
1086         boolean useSystemLineSeparator)
1087         throws IOException, org.xml.sax.SAXException
1088     {
1089         final java.io.Writer writer = m_writer;
1090         int end = start + length;
1091 
1092         for (int i = start; i < end; i++)
1093         {
1094             char c = ch[i];
1095 
1096             if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
1097             {
1098                 writer.write(m_lineSep, 0, m_lineSepLen);
1099             }
1100             else if (isCData && (!escapingNotNeeded(c)))
1101             {
1102                 //                if (i != 0)
1103                 if (m_cdataTagOpen)
1104                     closeCDATA();
1105 
1106                 // This needs to go into a function...
1107                 if (Encodings.isHighUTF16Surrogate(c))
1108                 {
1109                     writeUTF16Surrogate(c, ch, i, end);
1110                     i++ ; // process two input characters
1111                 }
1112                 else
1113                 {
1114                     writer.write("&#");
1115 
1116                     String intStr = Integer.toString((int) c);
1117 
1118                     writer.write(intStr);
1119                     writer.write(';');
1120                 }
1121 
1122                 //                if ((i != 0) && (i < (end - 1)))
1123                 //                if (!m_cdataTagOpen && (i < (end - 1)))
1124                 //                {
1125                 //                    writer.write(CDATA_DELIMITER_OPEN);
1126                 //                    m_cdataTagOpen = true;
1127                 //                }
1128             }
1129             else if (
1130                 isCData
1131                     && ((i < (end - 2))
1132                         && (']' == c)
1133                         && (']' == ch[i + 1])
1134                         && ('>' == ch[i + 2])))
1135             {
1136                 writer.write(CDATA_CONTINUE);
1137 
1138                 i += 2;
1139             }
1140             else
1141             {
1142                 if (escapingNotNeeded(c))
1143                 {
1144                     if (isCData && !m_cdataTagOpen)
1145                     {
1146                         writer.write(CDATA_DELIMITER_OPEN);
1147                         m_cdataTagOpen = true;
1148                     }
1149                     writer.write(c);
1150                 }
1151 
1152                 // This needs to go into a function...
1153                 else if (Encodings.isHighUTF16Surrogate(c))
1154                 {
1155                     if (m_cdataTagOpen)
1156                         closeCDATA();
1157                     writeUTF16Surrogate(c, ch, i, end);
1158                     i++; // process two input characters
1159                 }
1160                 else
1161                 {
1162                     if (m_cdataTagOpen)
1163                         closeCDATA();
1164                     writer.write("&#");
1165 
1166                     String intStr = Integer.toString((int) c);
1167 
1168                     writer.write(intStr);
1169                     writer.write(';');
1170                 }
1171             }
1172         }
1173 
1174     }
1175 
1176     /**
1177      * Ends an un-escaping section.
1178      *
1179      * @see #startNonEscaping
1180      *
1181      * @throws org.xml.sax.SAXException
1182      */
1183     public void endNonEscaping() throws org.xml.sax.SAXException
1184     {
1185         m_disableOutputEscapingStates.pop();
1186     }
1187 
1188     /**
1189      * Starts an un-escaping section. All characters printed within an un-
1190      * escaping section are printed as is, without escaping special characters
1191      * into entity references. Only XML and HTML serializers need to support
1192      * this method.
1193      * <p> The contents of the un-escaping section will be delivered through the
1194      * regular <tt>characters</tt> event.
1195      *
1196      * @throws org.xml.sax.SAXException
1197      */
1198     public void startNonEscaping() throws org.xml.sax.SAXException
1199     {
1200         m_disableOutputEscapingStates.push(true);
1201     }
1202 
1203     /**
1204      * Receive notification of cdata.
1205      *
1206      * <p>The Parser will call this method to report each chunk of
1207      * character data.  SAX parsers may return all contiguous character
1208      * data in a single chunk, or they may split it into several
1209      * chunks; however, all of the characters in any single event
1210      * must come from the same external entity, so that the Locator
1211      * provides useful information.</p>
1212      *
1213      * <p>The application must not attempt to read from the array
1214      * outside of the specified range.</p>
1215      *
1216      * <p>Note that some parsers will report whitespace using the
1217      * ignorableWhitespace() method rather than this one (validating
1218      * parsers must do so).</p>
1219      *
1220      * @param ch The characters from the XML document.
1221      * @param start The start position in the array.
1222      * @param length The number of characters to read from the array.
1223      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1224      *            wrapping another exception.
1225      * @see #ignorableWhitespace
1226      * @see org.xml.sax.Locator
1227      *
1228      * @throws org.xml.sax.SAXException
1229      */
1230     protected void cdata(char ch[], int start, final int length)
1231         throws org.xml.sax.SAXException
1232     {
1233 
1234         try
1235         {
1236             final int old_start = start;
1237             if (m_elemContext.m_startTagOpen)
1238             {
1239                 closeStartTag();
1240                 m_elemContext.m_startTagOpen = false;
1241             }
1242             m_ispreserve = true;
1243 
1244             if (shouldIndent())
1245                 indent();
1246 
1247             boolean writeCDataBrackets =
1248                 (((length >= 1) && escapingNotNeeded(ch[start])));
1249 
1250             /* Write out the CDATA opening delimiter only if
1251              * we are supposed to, and if we are not already in
1252              * the middle of a CDATA section
1253              */
1254             if (writeCDataBrackets && !m_cdataTagOpen)
1255             {
1256                 m_writer.write(CDATA_DELIMITER_OPEN);
1257                 m_cdataTagOpen = true;
1258             }
1259 
1260             // writer.write(ch, start, length);
1261             if (isEscapingDisabled())
1262             {
1263                 charactersRaw(ch, start, length);
1264             }
1265             else
1266                 writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1267 
1268             /* used to always write out CDATA closing delimiter here,
1269              * but now we delay, so that we can merge CDATA sections on output.
1270              * need to write closing delimiter later
1271              */
1272             if (writeCDataBrackets)
1273             {
1274                 /* if the CDATA section ends with ] don't leave it open
1275                  * as there is a chance that an adjacent CDATA sections
1276                  * starts with ]>.
1277                  * We don't want to merge ]] with > , or ] with ]>
1278                  */
1279                 if (ch[start + length - 1] == ']')
1280                     closeCDATA();
1281             }
1282 
1283             // time to fire off CDATA event
1284             if (m_tracer != null)
1285                 super.fireCDATAEvent(ch, old_start, length);
1286         }
1287         catch (IOException ioe)
1288         {
1289             throw new org.xml.sax.SAXException(
1290                 Utils.messages.createMessage(
1291                     MsgKey.ER_OIERROR,
1292                     null),
1293                 ioe);
1294             //"IO error", ioe);
1295         }
1296     }
1297 
1298     /**
1299      * Tell if the character escaping should be disabled for the current state.
1300      *
1301      * @return true if the character escaping should be disabled.
1302      */
1303     private boolean isEscapingDisabled()
1304     {
1305         return m_disableOutputEscapingStates.peekOrFalse();
1306     }
1307 
1308     /**
1309      * If available, when the disable-output-escaping attribute is used,
1310      * output raw text without escaping.
1311      *
1312      * @param ch The characters from the XML document.
1313      * @param start The start position in the array.
1314      * @param length The number of characters to read from the array.
1315      *
1316      * @throws org.xml.sax.SAXException
1317      */
1318     protected void charactersRaw(char ch[], int start, int length)
1319         throws org.xml.sax.SAXException
1320     {
1321 
1322         if (m_inEntityRef)
1323             return;
1324         try
1325         {
1326             if (m_elemContext.m_startTagOpen)
1327             {
1328                 closeStartTag();
1329                 m_elemContext.m_startTagOpen = false;
1330             }
1331 
1332             m_ispreserve = true;
1333 
1334             m_writer.write(ch, start, length);
1335         }
1336         catch (IOException e)
1337         {
1338             throw new SAXException(e);
1339         }
1340 
1341     }
1342 
1343     /**
1344      * Receive notification of character data.
1345      *
1346      * <p>The Parser will call this method to report each chunk of
1347      * character data.  SAX parsers may return all contiguous character
1348      * data in a single chunk, or they may split it into several
1349      * chunks; however, all of the characters in any single event
1350      * must come from the same external entity, so that the Locator
1351      * provides useful information.</p>
1352      *
1353      * <p>The application must not attempt to read from the array
1354      * outside of the specified range.</p>
1355      *
1356      * <p>Note that some parsers will report whitespace using the
1357      * ignorableWhitespace() method rather than this one (validating
1358      * parsers must do so).</p>
1359      *
1360      * @param chars The characters from the XML document.
1361      * @param start The start position in the array.
1362      * @param length The number of characters to read from the array.
1363      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1364      *            wrapping another exception.
1365      * @see #ignorableWhitespace
1366      * @see org.xml.sax.Locator
1367      *
1368      * @throws org.xml.sax.SAXException
1369      */
1370     public void characters(final char chars[], final int start, final int length)
1371         throws org.xml.sax.SAXException
1372     {
1373         // It does not make sense to continue with rest of the method if the number of
1374         // characters to read from array is 0.
1375         // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
1376         // is created if string is empty.
1377         if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
1378             return;
1379         if (m_elemContext.m_startTagOpen)
1380         {
1381             closeStartTag();
1382             m_elemContext.m_startTagOpen = false;
1383         }
1384         else if (m_needToCallStartDocument)
1385         {
1386             startDocumentInternal();
1387         }
1388 
1389         if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
1390         {
1391             /* either due to startCDATA() being called or due to
1392              * cdata-section-elements atribute, we need this as cdata
1393              */
1394             cdata(chars, start, length);
1395 
1396             return;
1397         }
1398 
1399         if (m_cdataTagOpen)
1400             closeCDATA();
1401         // the check with _escaping is a bit of a hack for XLSTC
1402 
1403         if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
1404         {
1405             charactersRaw(chars, start, length);
1406 
1407             // time to fire off characters generation event
1408             if (m_tracer != null)
1409                 super.fireCharEvent(chars, start, length);
1410 
1411             return;
1412         }
1413 
1414         if (m_elemContext.m_startTagOpen)
1415         {
1416             closeStartTag();
1417             m_elemContext.m_startTagOpen = false;
1418         }
1419 
1420 
1421         try
1422         {
1423             int i;
1424             char ch1;
1425             int startClean;
1426 
1427             // skip any leading whitspace
1428             // don't go off the end and use a hand inlined version
1429             // of isWhitespace(ch)
1430             final int end = start + length;
1431             int lastDirty = start - 1; // last character that needed processing
1432             for (i = start;
1433                 ((i < end)
1434                     && ((ch1 = chars[i]) == 0x20
1435                         || (ch1 == 0xA && m_lineSepUse)
1436                         || ch1 == 0xD
1437                         || ch1 == 0x09));
1438                 i++)
1439             {
1440                 /*
1441                  * We are processing leading whitespace, but are doing the same
1442                  * processing for dirty characters here as for non-whitespace.
1443                  *
1444                  */
1445                 if (!m_charInfo.isTextASCIIClean(ch1))
1446                 {
1447                     lastDirty = processDirty(chars,end, i,ch1, lastDirty, true);
1448                     i = lastDirty;
1449                 }
1450             }
1451             /* If there is some non-whitespace, mark that we may need
1452              * to preserve this. This is only important if we have indentation on.
1453              */
1454             if (i < end)
1455                 m_ispreserve = true;
1456 
1457 
1458 //            int lengthClean;    // number of clean characters in a row
1459 //            final boolean[] isAsciiClean = m_charInfo.getASCIIClean();
1460 
1461             final boolean isXML10 = XMLVERSION10.equals(getVersion());
1462             // we've skipped the leading whitespace, now deal with the rest
1463             for (; i < end; i++)
1464             {
1465                 {
1466                     // A tight loop to skip over common clean chars
1467                     // This tight loop makes it easier for the JIT
1468                     // to optimize.
1469                     char ch2;
1470                     while (i<end
1471                             && ((ch2 = chars[i])<127)
1472                             && m_charInfo.isTextASCIIClean(ch2))
1473                             i++;
1474                     if (i == end)
1475                         break;
1476                 }
1477 
1478                 final char ch = chars[i];
1479                 /*  The check for isCharacterInC0orC1Ranger and
1480                  *  isNELorLSEPCharacter has been added
1481                  *  to support Control Characters in XML 1.1
1482                  */
1483                 if (!isCharacterInC0orC1Range(ch) &&
1484                     (isXML10 || !isNELorLSEPCharacter(ch)) &&
1485                     (escapingNotNeeded(ch) && (!m_charInfo.isSpecialTextChar(ch)))
1486                         || ('"' == ch))
1487                 {
1488                     ; // a character needing no special processing
1489                 }
1490                 else
1491                 {
1492                     lastDirty = processDirty(chars,end, i, ch, lastDirty, true);
1493                     i = lastDirty;
1494                 }
1495             }
1496 
1497             // we've reached the end. Any clean characters at the
1498             // end of the array than need to be written out?
1499             startClean = lastDirty + 1;
1500             if (i > startClean)
1501             {
1502                 int lengthClean = i - startClean;
1503                 m_writer.write(chars, startClean, lengthClean);
1504             }
1505 
1506             // For indentation purposes, mark that we've just writen text out
1507             m_isprevtext = true;
1508         }
1509         catch (IOException e)
1510         {
1511             throw new SAXException(e);
1512         }
1513 
1514         // time to fire off characters generation event
1515         if (m_tracer != null)
1516             super.fireCharEvent(chars, start, length);
1517     }
1518     /**
1519      * This method checks if a given character is between C0 or C1 range
1520      * of Control characters.
1521      * This method is added to support Control Characters for XML 1.1
1522      * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
1523      * return false. Since they are whitespace characters, no special processing is needed.
1524      *
1525      * @param ch
1526      * @return boolean
1527      */
1528     private static boolean isCharacterInC0orC1Range(char ch)
1529     {
1530         if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
1531                 return false;
1532         else
1533                 return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
1534     }
1535     /**
1536      * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
1537      * These are new end of line charcters added in XML 1.1.  These characters must be
1538      * written as Numeric Character References (NCR) in XML 1.1 output document.
1539      *
1540      * @param ch
1541      * @return boolean
1542      */
1543     private static boolean isNELorLSEPCharacter(char ch)
1544     {
1545         return (ch == 0x85 || ch == 0x2028);
1546     }
1547     /**
1548      * Process a dirty character and any preeceding clean characters
1549      * that were not yet processed.
1550      * @param chars array of characters being processed
1551      * @param end one (1) beyond the last character
1552      * in chars to be processed
1553      * @param i the index of the dirty character
1554      * @param ch the character in chars[i]
1555      * @param lastDirty the last dirty character previous to i
1556      * @param fromTextNode true if the characters being processed are
1557      * from a text node, false if they are from an attribute value.
1558      * @return the index of the last character processed
1559      */
1560     private int processDirty(
1561         char[] chars,
1562         int end,
1563         int i,
1564         char ch,
1565         int lastDirty,
1566         boolean fromTextNode) throws IOException
1567     {
1568         int startClean = lastDirty + 1;
1569         // if we have some clean characters accumulated
1570         // process them before the dirty one.
1571         if (i > startClean)
1572         {
1573             int lengthClean = i - startClean;
1574             m_writer.write(chars, startClean, lengthClean);
1575         }
1576 
1577         // process the "dirty" character
1578         if (CharInfo.S_LINEFEED == ch && fromTextNode)
1579         {
1580             m_writer.write(m_lineSep, 0, m_lineSepLen);
1581         }
1582         else
1583         {
1584             startClean =
1585                 accumDefaultEscape(
1586                     m_writer,
1587                     (char)ch,
1588                     i,
1589                     chars,
1590                     end,
1591                     fromTextNode,
1592                     false);
1593             i = startClean - 1;
1594         }
1595         // Return the index of the last character that we just processed
1596         // which is a dirty character.
1597         return i;
1598     }
1599 
1600     /**
1601      * Receive notification of character data.
1602      *
1603      * @param s The string of characters to process.
1604      *
1605      * @throws org.xml.sax.SAXException
1606      */
1607     public void characters(String s) throws org.xml.sax.SAXException
1608     {
1609         if (m_inEntityRef && !m_expandDTDEntities)
1610             return;
1611         final int length = s.length();
1612         if (length > m_charsBuff.length)
1613         {
1614             m_charsBuff = new char[length * 2 + 1];
1615         }
1616         s.getChars(0, length, m_charsBuff, 0);
1617         characters(m_charsBuff, 0, length);
1618     }
1619 
1620     /**
1621      * Escape and writer.write a character.
1622      *
1623      * @param ch character to be escaped.
1624      * @param i index into character array.
1625      * @param chars non-null reference to character array.
1626      * @param len length of chars.
1627      * @param fromTextNode true if the characters being processed are
1628      * from a text node, false if the characters being processed are from
1629      * an attribute value.
1630      * @param escLF true if the linefeed should be escaped.
1631      *
1632      * @return i+1 if a character was written, i+2 if two characters
1633      * were written out, else return i.
1634      *
1635      * @throws org.xml.sax.SAXException
1636      */
1637     protected int accumDefaultEscape(
1638         Writer writer,
1639         char ch,
1640         int i,
1641         char[] chars,
1642         int len,
1643         boolean fromTextNode,
1644         boolean escLF)
1645         throws IOException
1646     {
1647 
1648         int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
1649 
1650         if (i == pos)
1651         {
1652             if (Encodings.isHighUTF16Surrogate(ch))
1653             {
1654 
1655                 // Should be the UTF-16 low surrogate of the hig/low pair.
1656                 char next;
1657                 // Unicode code point formed from the high/low pair.
1658                 int codePoint = 0;
1659 
1660                 if (i + 1 >= len)
1661                 {
1662                     throw new IOException(
1663                         Utils.messages.createMessage(
1664                             MsgKey.ER_INVALID_UTF16_SURROGATE,
1665                             new Object[] { Integer.toHexString(ch)}));
1666                     //"Invalid UTF-16 surrogate detected: "
1667 
1668                     //+Integer.toHexString(ch)+ " ?");
1669                 }
1670                 else
1671                 {
1672                     next = chars[++i];
1673 
1674                     if (!(Encodings.isLowUTF16Surrogate(next)))
1675                         throw new IOException(
1676                             Utils.messages.createMessage(
1677                                 MsgKey
1678                                     .ER_INVALID_UTF16_SURROGATE,
1679                                 new Object[] {
1680                                     Integer.toHexString(ch)
1681                                         + " "
1682                                         + Integer.toHexString(next)}));
1683                     //"Invalid UTF-16 surrogate detected: "
1684 
1685                     //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
1686                     codePoint = Encodings.toCodePoint(ch,next);
1687                 }
1688 
1689                 writer.write("&#");
1690                 writer.write(Integer.toString(codePoint));
1691                 writer.write(';');
1692                 pos += 2; // count the two characters that went into writing out this entity
1693             }
1694             else
1695             {
1696                 /*  This if check is added to support control characters in XML 1.1.
1697                  *  If a character is a Control Character within C0 and C1 range, it is desirable
1698                  *  to write it out as Numeric Character Reference(NCR) regardless of XML Version
1699                  *  being used for output document.
1700                  */
1701                 if (isCharacterInC0orC1Range(ch) ||
1702                         (XMLVERSION11.equals(getVersion()) && isNELorLSEPCharacter(ch)))
1703                 {
1704                     writer.write("&#");
1705                     writer.write(Integer.toString(ch));
1706                     writer.write(';');
1707                 }
1708                 else if ((!escapingNotNeeded(ch) ||
1709                     (  (fromTextNode && m_charInfo.isSpecialTextChar(ch))
1710                      || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch))))
1711                 && m_elemContext.m_currentElemDepth > 0)
1712                 {
1713                     writer.write("&#");
1714                     writer.write(Integer.toString(ch));
1715                     writer.write(';');
1716                 }
1717                 else
1718                 {
1719                     writer.write(ch);
1720                 }
1721                 pos++;  // count the single character that was processed
1722             }
1723 
1724         }
1725         return pos;
1726     }
1727 
1728     /**
1729      * Receive notification of the beginning of an element, although this is a
1730      * SAX method additional namespace or attribute information can occur before
1731      * or after this call, that is associated with this element.
1732      *
1733      *
1734      * @param namespaceURI The Namespace URI, or the empty string if the
1735      *        element has no Namespace URI or if Namespace
1736      *        processing is not being performed.
1737      * @param localName The local name (without prefix), or the
1738      *        empty string if Namespace processing is not being
1739      *        performed.
1740      * @param name The element type name.
1741      * @param atts The attributes attached to the element, if any.
1742      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1743      *            wrapping another exception.
1744      * @see org.xml.sax.ContentHandler#startElement
1745      * @see org.xml.sax.ContentHandler#endElement
1746      * @see org.xml.sax.AttributeList
1747      *
1748      * @throws org.xml.sax.SAXException
1749      */
1750     public void startElement(
1751         String namespaceURI,
1752         String localName,
1753         String name,
1754         Attributes atts)
1755         throws org.xml.sax.SAXException
1756     {
1757         if (m_inEntityRef)
1758             return;
1759 
1760         if (m_needToCallStartDocument)
1761         {
1762             startDocumentInternal();
1763             m_needToCallStartDocument = false;
1764         }
1765         else if (m_cdataTagOpen)
1766             closeCDATA();
1767         try
1768         {
1769             if ((true == m_needToOutputDocTypeDecl)
1770                 && (null != getDoctypeSystem()))
1771             {
1772                 outputDocTypeDecl(name, true);
1773             }
1774 
1775             m_needToOutputDocTypeDecl = false;
1776 
1777             /* before we over-write the current elementLocalName etc.
1778              * lets close out the old one (if we still need to)
1779              */
1780             if (m_elemContext.m_startTagOpen)
1781             {
1782                 closeStartTag();
1783                 m_elemContext.m_startTagOpen = false;
1784             }
1785 
1786             if (namespaceURI != null)
1787                 ensurePrefixIsDeclared(namespaceURI, name);
1788 
1789             m_ispreserve = false;
1790 
1791             if (shouldIndent() && m_startNewLine)
1792             {
1793                 indent();
1794             }
1795 
1796             m_startNewLine = true;
1797 
1798             final java.io.Writer writer = m_writer;
1799             writer.write('<');
1800             writer.write(name);
1801         }
1802         catch (IOException e)
1803         {
1804             throw new SAXException(e);
1805         }
1806 
1807         // process the attributes now, because after this SAX call they might be gone
1808         if (atts != null)
1809             addAttributes(atts);
1810 
1811         m_elemContext = m_elemContext.push(namespaceURI,localName,name);
1812         m_isprevtext = false;
1813 
1814         if (m_tracer != null){
1815             firePseudoAttributes();
1816         }
1817 
1818     }
1819 
1820     /**
1821       * Receive notification of the beginning of an element, additional
1822       * namespace or attribute information can occur before or after this call,
1823       * that is associated with this element.
1824       *
1825       *
1826       * @param elementNamespaceURI The Namespace URI, or the empty string if the
1827       *        element has no Namespace URI or if Namespace
1828       *        processing is not being performed.
1829       * @param elementLocalName The local name (without prefix), or the
1830       *        empty string if Namespace processing is not being
1831       *        performed.
1832       * @param elementName The element type name.
1833       * @throws org.xml.sax.SAXException Any SAX exception, possibly
1834       *            wrapping another exception.
1835       * @see org.xml.sax.ContentHandler#startElement
1836       * @see org.xml.sax.ContentHandler#endElement
1837       * @see org.xml.sax.AttributeList
1838       *
1839       * @throws org.xml.sax.SAXException
1840       */
1841     public void startElement(
1842         String elementNamespaceURI,
1843         String elementLocalName,
1844         String elementName)
1845         throws SAXException
1846     {
1847         startElement(elementNamespaceURI, elementLocalName, elementName, null);
1848     }
1849 
1850     public void startElement(String elementName) throws SAXException
1851     {
1852         startElement(null, null, elementName, null);
1853     }
1854 
1855     /**
1856      * Output the doc type declaration.
1857      *
1858      * @param name non-null reference to document type name.
1859      * NEEDSDOC @param closeDecl
1860      *
1861      * @throws java.io.IOException
1862      */
1863     void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException
1864     {
1865         if (m_cdataTagOpen)
1866             closeCDATA();
1867         try
1868         {
1869             final java.io.Writer writer = m_writer;
1870             writer.write("<!DOCTYPE ");
1871             writer.write(name);
1872 
1873             String doctypePublic = getDoctypePublic();
1874             if (null != doctypePublic)
1875             {
1876                 writer.write(" PUBLIC \"");
1877                 writer.write(doctypePublic);
1878                 writer.write('\"');
1879             }
1880 
1881             String doctypeSystem = getDoctypeSystem();
1882             if (null != doctypeSystem)
1883             {
1884                 if (null == doctypePublic)
1885                     writer.write(" SYSTEM \"");
1886                 else
1887                     writer.write(" \"");
1888 
1889                 writer.write(doctypeSystem);
1890 
1891                 if (closeDecl)
1892                 {
1893                     writer.write("\">");
1894                     writer.write(m_lineSep, 0, m_lineSepLen);
1895                     closeDecl = false; // done closing
1896                 }
1897                 else
1898                     writer.write('\"');
1899             }
1900             boolean dothis = false;
1901             if (dothis)
1902             {
1903                 // at one point this code seemed right,
1904                 // but not anymore - Brian M.
1905                 if (closeDecl)
1906                 {
1907                     writer.write('>');
1908                     writer.write(m_lineSep, 0, m_lineSepLen);
1909                 }
1910             }
1911         }
1912         catch (IOException e)
1913         {
1914             throw new SAXException(e);
1915         }
1916     }
1917 
1918     /**
1919      * Process the attributes, which means to write out the currently
1920      * collected attributes to the writer. The attributes are not
1921      * cleared by this method
1922      *
1923      * @param writer the writer to write processed attributes to.
1924      * @param nAttrs the number of attributes in m_attributes
1925      * to be processed
1926      *
1927      * @throws java.io.IOException
1928      * @throws org.xml.sax.SAXException
1929      */
1930     public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException
1931     {
1932             /* real SAX attributes are not passed in, so process the
1933              * attributes that were collected after the startElement call.
1934              * _attribVector is a "cheap" list for Stream serializer output
1935              * accumulated over a series of calls to attribute(name,value)
1936              */
1937             String encoding = getEncoding();
1938             for (int i = 0; i < nAttrs; i++)
1939             {
1940                 // elementAt is JDK 1.1.8
1941                 final String name = m_attributes.getQName(i);
1942                 final String value = m_attributes.getValue(i);
1943                 writer.write(' ');
1944                 writer.write(name);
1945                 writer.write("=\"");
1946                 writeAttrString(writer, value, encoding);
1947                 writer.write('\"');
1948             }
1949     }
1950 
1951     /**
1952      * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
1953      * and UTF-16 surrogates for chracter references <CODE>&amp;#xnn</CODE>.
1954      *
1955      * @param   string      String to convert to XML format.
1956      * @param   encoding    CURRENTLY NOT IMPLEMENTED.
1957      *
1958      * @throws java.io.IOException
1959      */
1960     public void writeAttrString(
1961         Writer writer,
1962         String string,
1963         String encoding)
1964         throws IOException
1965     {
1966         final int len = string.length();
1967         if (len > m_attrBuff.length)
1968         {
1969            m_attrBuff = new char[len*2 + 1];
1970         }
1971         string.getChars(0,len, m_attrBuff, 0);
1972         final char[] stringChars = m_attrBuff;
1973 
1974         for (int i = 0; i < len; )
1975         {
1976             char ch = stringChars[i];
1977             if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch)))
1978             {
1979                 writer.write(ch);
1980                 i++;
1981             }
1982             else
1983             { // I guess the parser doesn't normalize cr/lf in attributes. -sb
1984 //                if ((CharInfo.S_CARRIAGERETURN == ch)
1985 //                    && ((i + 1) < len)
1986 //                    && (CharInfo.S_LINEFEED == stringChars[i + 1]))
1987 //                {
1988 //                    i++;
1989 //                    ch = CharInfo.S_LINEFEED;
1990 //                }
1991 
1992                 i = accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
1993             }
1994         }
1995 
1996     }
1997 
1998     /**
1999      * Receive notification of the end of an element.
2000      *
2001      *
2002      * @param namespaceURI The Namespace URI, or the empty string if the
2003      *        element has no Namespace URI or if Namespace
2004      *        processing is not being performed.
2005      * @param localName The local name (without prefix), or the
2006      *        empty string if Namespace processing is not being
2007      *        performed.
2008      * @param name The element type name
2009      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2010      *            wrapping another exception.
2011      *
2012      * @throws org.xml.sax.SAXException
2013      */
2014     public void endElement(String namespaceURI, String localName, String name)
2015         throws org.xml.sax.SAXException
2016     {
2017 
2018         if (m_inEntityRef)
2019             return;
2020 
2021         // namespaces declared at the current depth are no longer valid
2022         // so get rid of them
2023         m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2024 
2025         try
2026         {
2027             final java.io.Writer writer = m_writer;
2028             if (m_elemContext.m_startTagOpen)
2029             {
2030                 if (m_tracer != null)
2031                     super.fireStartElem(m_elemContext.m_elementName);
2032                 int nAttrs = m_attributes.getLength();
2033                 if (nAttrs > 0)
2034                 {
2035                     processAttributes(m_writer, nAttrs);
2036                     // clear attributes object for re-use with next element
2037                     m_attributes.clear();
2038                 }
2039                 if (m_spaceBeforeClose)
2040                     writer.write(" />");
2041                 else
2042                     writer.write("/>");
2043                 /* don't need to pop cdataSectionState because
2044                  * this element ended so quickly that we didn't get
2045                  * to push the state.
2046                  */
2047 
2048             }
2049             else
2050             {
2051                 if (m_cdataTagOpen)
2052                     closeCDATA();
2053 
2054                 if (shouldIndent())
2055                     indent(m_elemContext.m_currentElemDepth - 1);
2056                 writer.write('<');
2057                 writer.write('/');
2058                 writer.write(name);
2059                 writer.write('>');
2060             }
2061         }
2062         catch (IOException e)
2063         {
2064             throw new SAXException(e);
2065         }
2066 
2067         if (!m_elemContext.m_startTagOpen && m_doIndent)
2068         {
2069             m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
2070         }
2071 
2072         m_isprevtext = false;
2073 
2074         // fire off the end element event
2075         if (m_tracer != null)
2076             super.fireEndElem(name);
2077         m_elemContext = m_elemContext.m_prev;
2078     }
2079 
2080     /**
2081      * Receive notification of the end of an element.
2082      * @param name The element type name
2083      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2084      *     wrapping another exception.
2085      */
2086     public void endElement(String name) throws org.xml.sax.SAXException
2087     {
2088         endElement(null, null, name);
2089     }
2090 
2091     /**
2092      * Begin the scope of a prefix-URI Namespace mapping
2093      * just before another element is about to start.
2094      * This call will close any open tags so that the prefix mapping
2095      * will not apply to the current element, but the up comming child.
2096      *
2097      * @see org.xml.sax.ContentHandler#startPrefixMapping
2098      *
2099      * @param prefix The Namespace prefix being declared.
2100      * @param uri The Namespace URI the prefix is mapped to.
2101      *
2102      * @throws org.xml.sax.SAXException The client may throw
2103      *            an exception during processing.
2104      *
2105      */
2106     public void startPrefixMapping(String prefix, String uri)
2107         throws org.xml.sax.SAXException
2108     {
2109         // the "true" causes the flush of any open tags
2110         startPrefixMapping(prefix, uri, true);
2111     }
2112 
2113     /**
2114      * Handle a prefix/uri mapping, which is associated with a startElement()
2115      * that is soon to follow. Need to close any open start tag to make
2116      * sure than any name space attributes due to this event are associated wih
2117      * the up comming element, not the current one.
2118      * @see ExtendedContentHandler#startPrefixMapping
2119      *
2120      * @param prefix The Namespace prefix being declared.
2121      * @param uri The Namespace URI the prefix is mapped to.
2122      * @param shouldFlush true if any open tags need to be closed first, this
2123      * will impact which element the mapping applies to (open parent, or its up
2124      * comming child)
2125      * @return returns true if the call made a change to the current
2126      * namespace information, false if it did not change anything, e.g. if the
2127      * prefix/namespace mapping was already in scope from before.
2128      *
2129      * @throws org.xml.sax.SAXException The client may throw
2130      *            an exception during processing.
2131      *
2132      *
2133      */
2134     public boolean startPrefixMapping(
2135         String prefix,
2136         String uri,
2137         boolean shouldFlush)
2138         throws org.xml.sax.SAXException
2139     {
2140 
2141         /* Remember the mapping, and at what depth it was declared
2142          * This is one greater than the current depth because these
2143          * mappings will apply to the next depth. This is in
2144          * consideration that startElement() will soon be called
2145          */
2146 
2147         boolean pushed;
2148         int pushDepth;
2149         if (shouldFlush)
2150         {
2151             flushPending();
2152             // the prefix mapping applies to the child element (one deeper)
2153             pushDepth = m_elemContext.m_currentElemDepth + 1;
2154         }
2155         else
2156         {
2157             // the prefix mapping applies to the current element
2158             pushDepth = m_elemContext.m_currentElemDepth;
2159         }
2160         pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
2161 
2162         if (pushed)
2163         {
2164             /* Brian M.: don't know if we really needto do this. The
2165              * callers of this object should have injected both
2166              * startPrefixMapping and the attributes.  We are
2167              * just covering our butt here.
2168              */
2169             String name;
2170             if (EMPTYSTRING.equals(prefix))
2171             {
2172                 name = "xmlns";
2173                 addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false);
2174             }
2175             else
2176             {
2177                 if (!EMPTYSTRING.equals(uri))
2178                     // hack for XSLTC attribset16 test
2179                 { // that maps ns1 prefix to "" URI
2180                     name = "xmlns:" + prefix;
2181 
2182                     /* for something like xmlns:abc="w3.pretend.org"
2183                      *  the      uri is the value, that is why we pass it in the
2184                      * value, or 5th slot of addAttributeAlways()
2185                      */
2186                     addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false);
2187                 }
2188             }
2189         }
2190         return pushed;
2191     }
2192 
2193     /**
2194      * Receive notification of an XML comment anywhere in the document. This
2195      * callback will be used for comments inside or outside the document
2196      * element, including comments in the external DTD subset (if read).
2197      * @param ch An array holding the characters in the comment.
2198      * @param start The starting position in the array.
2199      * @param length The number of characters to use from the array.
2200      * @throws org.xml.sax.SAXException The application may raise an exception.
2201      */
2202     public void comment(char ch[], int start, int length)
2203         throws org.xml.sax.SAXException
2204     {
2205 
2206         int start_old = start;
2207         if (m_inEntityRef)
2208             return;
2209         if (m_elemContext.m_startTagOpen)
2210         {
2211             closeStartTag();
2212             m_elemContext.m_startTagOpen = false;
2213         }
2214         else if (m_needToCallStartDocument)
2215         {
2216             startDocumentInternal();
2217             m_needToCallStartDocument = false;
2218         }
2219 
2220         try
2221         {
2222             if (shouldIndent() && m_isStandalone)
2223                 indent();
2224 
2225             final int limit = start + length;
2226             boolean wasDash = false;
2227             if (m_cdataTagOpen)
2228                 closeCDATA();
2229 
2230             if (shouldIndent() && !m_isStandalone)
2231                 indent();
2232 
2233             final java.io.Writer writer = m_writer;
2234             writer.write(COMMENT_BEGIN);
2235             // Detect occurrences of two consecutive dashes, handle as necessary.
2236             for (int i = start; i < limit; i++)
2237             {
2238                 if (wasDash && ch[i] == '-')
2239                 {
2240                     writer.write(ch, start, i - start);
2241                     writer.write(" -");
2242                     start = i + 1;
2243                 }
2244                 wasDash = (ch[i] == '-');
2245             }
2246 
2247             // if we have some chars in the comment
2248             if (length > 0)
2249             {
2250                 // Output the remaining characters (if any)
2251                 final int remainingChars = (limit - start);
2252                 if (remainingChars > 0)
2253                     writer.write(ch, start, remainingChars);
2254                 // Protect comment end from a single trailing dash
2255                 if (ch[limit - 1] == '-')
2256                     writer.write(' ');
2257             }
2258             writer.write(COMMENT_END);
2259         }
2260         catch (IOException e)
2261         {
2262             throw new SAXException(e);
2263         }
2264 
2265         /*
2266          * Don't write out any indentation whitespace now,
2267          * because there may be non-whitespace text after this.
2268          *
2269          * Simply mark that at this point if we do decide
2270          * to indent that we should
2271          * add a newline on the end of the current line before
2272          * the indentation at the start of the next line.
2273          */
2274         m_startNewLine = true;
2275         // time to generate comment event
2276         if (m_tracer != null)
2277             super.fireCommentEvent(ch, start_old,length);
2278     }
2279 
2280     /**
2281      * Report the end of a CDATA section.
2282      * @throws org.xml.sax.SAXException The application may raise an exception.
2283      *
2284      *  @see  #startCDATA
2285      */
2286     public void endCDATA() throws org.xml.sax.SAXException
2287     {
2288         if (m_cdataTagOpen)
2289             closeCDATA();
2290         m_cdataStartCalled = false;
2291     }
2292 
2293     /**
2294      * Report the end of DTD declarations.
2295      * @throws org.xml.sax.SAXException The application may raise an exception.
2296      * @see #startDTD
2297      */
2298     public void endDTD() throws org.xml.sax.SAXException
2299     {
2300         try
2301         {
2302             // Don't output doctype declaration until startDocumentInternal
2303             // has been called. Otherwise, it can appear before XML decl.
2304             if (m_needToCallStartDocument) {
2305                 return;
2306             }
2307 
2308             if (m_needToOutputDocTypeDecl)
2309             {
2310                 outputDocTypeDecl(m_elemContext.m_elementName, false);
2311                 m_needToOutputDocTypeDecl = false;
2312             }
2313             final java.io.Writer writer = m_writer;
2314             if (!m_inDoctype)
2315                 writer.write("]>");
2316             else
2317             {
2318                 writer.write('>');
2319             }
2320 
2321             writer.write(m_lineSep, 0, m_lineSepLen);
2322         }
2323         catch (IOException e)
2324         {
2325             throw new SAXException(e);
2326         }
2327 
2328     }
2329 
2330     /**
2331      * End the scope of a prefix-URI Namespace mapping.
2332      * @see org.xml.sax.ContentHandler#endPrefixMapping
2333      *
2334      * @param prefix The prefix that was being mapping.
2335      * @throws org.xml.sax.SAXException The client may throw
2336      *            an exception during processing.
2337      */
2338     public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
2339     { // do nothing
2340     }
2341 
2342     /**
2343      * Receive notification of ignorable whitespace in element content.
2344      *
2345      * Not sure how to get this invoked quite yet.
2346      *
2347      * @param ch The characters from the XML document.
2348      * @param start The start position in the array.
2349      * @param length The number of characters to read from the array.
2350      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2351      *            wrapping another exception.
2352      * @see #characters
2353      *
2354      * @throws org.xml.sax.SAXException
2355      */
2356     public void ignorableWhitespace(char ch[], int start, int length)
2357         throws org.xml.sax.SAXException
2358     {
2359 
2360         if (0 == length)
2361             return;
2362         characters(ch, start, length);
2363     }
2364 
2365     /**
2366      * Receive notification of a skipped entity.
2367      * @see org.xml.sax.ContentHandler#skippedEntity
2368      *
2369      * @param name The name of the skipped entity.  If it is a
2370      *       parameter                   entity, the name will begin with '%',
2371      * and if it is the external DTD subset, it will be the string
2372      * "[dtd]".
2373      * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2374      * another exception.
2375      */
2376     public void skippedEntity(String name) throws org.xml.sax.SAXException
2377     { // TODO: Should handle
2378     }
2379 
2380     /**
2381      * Report the start of a CDATA section.
2382      *
2383      * @throws org.xml.sax.SAXException The application may raise an exception.
2384      * @see #endCDATA
2385      */
2386     public void startCDATA() throws org.xml.sax.SAXException
2387     {
2388         m_cdataStartCalled = true;
2389     }
2390 
2391     /**
2392      * Report the beginning of an entity.
2393      *
2394      * The start and end of the document entity are not reported.
2395      * The start and end of the external DTD subset are reported
2396      * using the pseudo-name "[dtd]".  All other events must be
2397      * properly nested within start/end entity events.
2398      *
2399      * @param name The name of the entity.  If it is a parameter
2400      *        entity, the name will begin with '%'.
2401      * @throws org.xml.sax.SAXException The application may raise an exception.
2402      * @see #endEntity
2403      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2404      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2405      */
2406     public void startEntity(String name) throws org.xml.sax.SAXException
2407     {
2408         if (name.equals("[dtd]"))
2409             m_inExternalDTD = true;
2410 
2411         if (!m_expandDTDEntities && !m_inExternalDTD) {
2412             /* Only leave the entity as-is if
2413              * we've been told not to expand them
2414              * and this is not the magic [dtd] name.
2415              */
2416             startNonEscaping();
2417             characters("&" + name + ';');
2418             endNonEscaping();
2419         }
2420 
2421         m_inEntityRef = true;
2422     }
2423 
2424     /**
2425      * For the enclosing elements starting tag write out
2426      * out any attributes followed by ">"
2427      *
2428      * @throws org.xml.sax.SAXException
2429      */
2430     protected void closeStartTag() throws SAXException
2431     {
2432         if (m_elemContext.m_startTagOpen)
2433         {
2434 
2435             try
2436             {
2437                 if (m_tracer != null)
2438                     super.fireStartElem(m_elemContext.m_elementName);
2439                 int nAttrs = m_attributes.getLength();
2440                 if (nAttrs > 0)
2441                 {
2442                      processAttributes(m_writer, nAttrs);
2443                     // clear attributes object for re-use with next element
2444                     m_attributes.clear();
2445                 }
2446                 m_writer.write('>');
2447             }
2448             catch (IOException e)
2449             {
2450                 throw new SAXException(e);
2451             }
2452 
2453             /* whether Xalan or XSLTC, we have the prefix mappings now, so
2454              * lets determine if the current element is specified in the cdata-
2455              * section-elements list.
2456              */
2457             if (m_cdataSectionElements != null)
2458                 m_elemContext.m_isCdataSection = isCdataSection();
2459 
2460             if (m_doIndent)
2461             {
2462                 m_isprevtext = false;
2463                 m_preserves.push(m_ispreserve);
2464             }
2465         }
2466 
2467     }
2468 
2469     /**
2470      * Report the start of DTD declarations, if any.
2471      *
2472      * Any declarations are assumed to be in the internal subset unless
2473      * otherwise indicated.
2474      *
2475      * @param name The document type name.
2476      * @param publicId The declared public identifier for the
2477      *        external DTD subset, or null if none was declared.
2478      * @param systemId The declared system identifier for the
2479      *        external DTD subset, or null if none was declared.
2480      * @throws org.xml.sax.SAXException The application may raise an
2481      *            exception.
2482      * @see #endDTD
2483      * @see #startEntity
2484      */
2485     public void startDTD(String name, String publicId, String systemId)
2486         throws org.xml.sax.SAXException
2487     {
2488         setDoctypeSystem(systemId);
2489         setDoctypePublic(publicId);
2490 
2491         m_elemContext.m_elementName = name;
2492         m_inDoctype = true;
2493     }
2494 
2495     /**
2496      * Returns the m_indentAmount.
2497      * @return int
2498      */
2499     public int getIndentAmount()
2500     {
2501         return m_indentAmount;
2502     }
2503 
2504     /**
2505      * Sets the m_indentAmount.
2506      *
2507      * @param m_indentAmount The m_indentAmount to set
2508      */
2509     public void setIndentAmount(int m_indentAmount)
2510     {
2511         this.m_indentAmount = m_indentAmount;
2512     }
2513 
2514     /**
2515      * Tell if, based on space preservation constraints and the doIndent property,
2516      * if an indent should occur.
2517      *
2518      * @return True if an indent should occur.
2519      */
2520     protected boolean shouldIndent()
2521     {
2522         return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2523     }
2524 
2525     /**
2526      * Searches for the list of qname properties with the specified key in the
2527      * property list. If the key is not found in this property list, the default
2528      * property list, and its defaults, recursively, are then checked. The
2529      * method returns <code>null</code> if the property is not found.
2530      *
2531      * @param   key   the property key.
2532      * @param props the list of properties to search in.
2533      *
2534      * Sets the vector of local-name/URI pairs of the cdata section elements
2535      * specified in the cdata-section-elements property.
2536      *
2537      * This method is essentially a copy of getQNameProperties() from
2538      * OutputProperties. Eventually this method should go away and a call
2539      * to setCdataSectionElements(Vector v) should be made directly.
2540      */
2541     private void setCdataSectionElements(String key, Properties props)
2542     {
2543 
2544         String s = props.getProperty(key);
2545 
2546         if (null != s)
2547         {
2548             // Vector of URI/LocalName pairs
2549             Vector v = new Vector();
2550             int l = s.length();
2551             boolean inCurly = false;
2552             StringBuffer buf = new StringBuffer();
2553 
2554             // parse through string, breaking on whitespaces.  I do this instead
2555             // of a tokenizer so I can track whitespace inside of curly brackets,
2556             // which theoretically shouldn't happen if they contain legal URLs.
2557             for (int i = 0; i < l; i++)
2558             {
2559                 char c = s.charAt(i);
2560 
2561                 if (Character.isWhitespace(c))
2562                 {
2563                     if (!inCurly)
2564                     {
2565                         if (buf.length() > 0)
2566                         {
2567                             addCdataSectionElement(buf.toString(), v);
2568                             buf.setLength(0);
2569                         }
2570                         continue;
2571                     }
2572                 }
2573                 else if ('{' == c)
2574                     inCurly = true;
2575                 else if ('}' == c)
2576                     inCurly = false;
2577 
2578                 buf.append(c);
2579             }
2580 
2581             if (buf.length() > 0)
2582             {
2583                 addCdataSectionElement(buf.toString(), v);
2584                 buf.setLength(0);
2585             }
2586             // call the official, public method to set the collected names
2587             setCdataSectionElements(v);
2588         }
2589 
2590     }
2591 
2592     /**
2593      * Adds a URI/LocalName pair of strings to the list.
2594      *
2595      * @param URI_and_localName String of the form "{uri}local" or "local"
2596      *
2597      * @return a QName object
2598      */
2599     private void addCdataSectionElement(String URI_and_localName, Vector v)
2600     {
2601 
2602         StringTokenizer tokenizer =
2603             new StringTokenizer(URI_and_localName, "{}", false);
2604         String s1 = tokenizer.nextToken();
2605         String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2606 
2607         if (null == s2)
2608         {
2609             // add null URI and the local name
2610             v.addElement(null);
2611             v.addElement(s1);
2612         }
2613         else
2614         {
2615             // add URI, then local name
2616             v.addElement(s1);
2617             v.addElement(s2);
2618         }
2619     }
2620 
2621     /**
2622      * Remembers the cdata sections specified in the cdata-section-elements.
2623      * The "official way to set URI and localName pairs.
2624      * This method should be used by both Xalan and XSLTC.
2625      *
2626      * @param URI_and_localNames a vector of pairs of Strings (URI/local)
2627      */
2628     public void setCdataSectionElements(Vector URI_and_localNames)
2629     {
2630         m_cdataSectionElements = URI_and_localNames;
2631     }
2632 
2633     /**
2634      * Makes sure that the namespace URI for the given qualified attribute name
2635      * is declared.
2636      * @param ns the namespace URI
2637      * @param rawName the qualified name
2638      * @return returns null if no action is taken, otherwise it returns the
2639      * prefix used in declaring the namespace.
2640      * @throws SAXException
2641      */
2642     protected String ensureAttributesNamespaceIsDeclared(
2643         String ns,
2644         String localName,
2645         String rawName)
2646         throws org.xml.sax.SAXException
2647     {
2648 
2649         if (ns != null && ns.length() > 0)
2650         {
2651 
2652             // extract the prefix in front of the raw name
2653             int index = 0;
2654             String prefixFromRawName =
2655                 (index = rawName.indexOf(":")) < 0
2656                     ? ""
2657                     : rawName.substring(0, index);
2658 
2659             if (index > 0)
2660             {
2661                 // we have a prefix, lets see if it maps to a namespace
2662                 String uri = m_prefixMap.lookupNamespace(prefixFromRawName);
2663                 if (uri != null && uri.equals(ns))
2664                 {
2665                     // the prefix in the raw name is already maps to the given namespace uri
2666                     // so we don't need to do anything
2667                     return null;
2668                 }
2669                 else
2670                 {
2671                     // The uri does not map to the prefix in the raw name,
2672                     // so lets make the mapping.
2673                     this.startPrefixMapping(prefixFromRawName, ns, false);
2674                     this.addAttribute(
2675                         "http://www.w3.org/2000/xmlns/",
2676                         prefixFromRawName,
2677                         "xmlns:" + prefixFromRawName,
2678                         "CDATA",
2679                         ns, false);
2680                     return prefixFromRawName;
2681                 }
2682             }
2683             else
2684             {
2685                 // we don't have a prefix in the raw name.
2686                 // Does the URI map to a prefix already?
2687                 String prefix = m_prefixMap.lookupPrefix(ns);
2688                 if (prefix == null)
2689                 {
2690                     // uri is not associated with a prefix,
2691                     // so lets generate a new prefix to use
2692                     prefix = m_prefixMap.generateNextPrefix();
2693                     this.startPrefixMapping(prefix, ns, false);
2694                     this.addAttribute(
2695                         "http://www.w3.org/2000/xmlns/",
2696                         prefix,
2697                         "xmlns:" + prefix,
2698                         "CDATA",
2699                         ns, false);
2700                 }
2701 
2702                 return prefix;
2703 
2704             }
2705         }
2706         return null;
2707     }
2708 
2709     void ensurePrefixIsDeclared(String ns, String rawName)
2710         throws org.xml.sax.SAXException
2711     {
2712 
2713         if (ns != null && ns.length() > 0)
2714         {
2715             int index;
2716             final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
2717             String prefix = (no_prefix) ? "" : rawName.substring(0, index);
2718 
2719             if (null != prefix)
2720             {
2721                 String foundURI = m_prefixMap.lookupNamespace(prefix);
2722 
2723                 if ((null == foundURI) || !foundURI.equals(ns))
2724                 {
2725                     this.startPrefixMapping(prefix, ns);
2726 
2727                     // Bugzilla1133: Generate attribute as well as namespace event.
2728                     // SAX does expect both.
2729 
2730                     this.addAttributeAlways(
2731                         "http://www.w3.org/2000/xmlns/",
2732                         no_prefix ? "xmlns" : prefix,  // local name
2733                         no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
2734                         "CDATA",
2735                         ns,
2736                         false);
2737                 }
2738 
2739             }
2740         }
2741     }
2742 
2743     /**
2744      * This method flushes any pending events, which can be startDocument()
2745      * closing the opening tag of an element, or closing an open CDATA section.
2746      */
2747     public void flushPending() throws SAXException
2748     {
2749             if (m_needToCallStartDocument)
2750             {
2751                 startDocumentInternal();
2752                 m_needToCallStartDocument = false;
2753             }
2754             if (m_elemContext.m_startTagOpen)
2755             {
2756                 closeStartTag();
2757                 m_elemContext.m_startTagOpen = false;
2758             }
2759 
2760             if (m_cdataTagOpen)
2761             {
2762                 closeCDATA();
2763                 m_cdataTagOpen = false;
2764             }
2765     }
2766 
2767     public void setContentHandler(ContentHandler ch)
2768     {
2769         // this method is really only useful in the ToSAXHandler classes but it is
2770         // in the interface.  If the method defined here is ever called
2771         // we are probably in trouble.
2772     }
2773 
2774     /**
2775      * Adds the given attribute to the set of attributes, even if there is
2776      * no currently open element. This is useful if a SAX startPrefixMapping()
2777      * should need to add an attribute before the element name is seen.
2778      *
2779      * This method is a copy of its super classes method, except that some
2780      * tracing of events is done.  This is so the tracing is only done for
2781      * stream serializers, not for SAX ones.
2782      *
2783      * @param uri the URI of the attribute
2784      * @param localName the local name of the attribute
2785      * @param rawName   the qualified name of the attribute
2786      * @param type the type of the attribute (probably CDATA)
2787      * @param value the value of the attribute
2788      * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
2789      * @return true if the attribute value was added,
2790      * false if the attribute already existed and the value was
2791      * replaced with the new value.
2792      */
2793     public boolean addAttributeAlways(
2794         String uri,
2795         String localName,
2796         String rawName,
2797         String type,
2798         String value,
2799         boolean xslAttribute)
2800     {
2801         boolean was_added;
2802         int index;
2803         //if (uri == null || localName == null || uri.length() == 0)
2804             index = m_attributes.getIndex(rawName);
2805         // Don't use 'localName' as it gives incorrect value, rely only on 'rawName'
2806         /*else {
2807             index = m_attributes.getIndex(uri, localName);
2808         }*/
2809         if (index >= 0)
2810         {
2811             String old_value = null;
2812             if (m_tracer != null)
2813             {
2814                 old_value = m_attributes.getValue(index);
2815                 if (value.equals(old_value))
2816                     old_value = null;
2817             }
2818 
2819             /* We've seen the attribute before.
2820              * We may have a null uri or localName, but all we really
2821              * want to re-set is the value anyway.
2822              */
2823             m_attributes.setValue(index, value);
2824             was_added = false;
2825             if (old_value != null){
2826                 firePseudoAttributes();
2827             }
2828 
2829         }
2830         else
2831         {
2832             // the attribute doesn't exist yet, create it
2833             if (xslAttribute)
2834             {
2835                 /*
2836                  * This attribute is from an xsl:attribute element so we take some care in
2837                  * adding it, e.g.
2838                  *   <elem1  foo:attr1="1" xmlns:foo="uri1">
2839                  *       <xsl:attribute name="foo:attr2">2</xsl:attribute>
2840                  *   </elem1>
2841                  *
2842                  * We are adding attr1 and attr2 both as attributes of elem1,
2843                  * and this code is adding attr2 (the xsl:attribute ).
2844                  * We could have a collision with the prefix like in the example above.
2845                  */
2846 
2847                 // In the example above, is there a prefix like foo ?
2848                 final int colonIndex = rawName.indexOf(':');
2849                 if (colonIndex > 0)
2850                 {
2851                     String prefix = rawName.substring(0,colonIndex);
2852                     NamespaceMappings.MappingRecord existing_mapping = m_prefixMap.getMappingFromPrefix(prefix);
2853 
2854                     /* Before adding this attribute (foo:attr2),
2855                      * is the prefix for it (foo) already mapped at the current depth?
2856                      */
2857                     if (existing_mapping != null
2858                     && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth
2859                     && !existing_mapping.m_uri.equals(uri))
2860                     {
2861                         /*
2862                          * There is an existing mapping of this prefix,
2863                          * it differs from the one we need,
2864                          * and unfortunately it is at the current depth so we
2865                          * can not over-ride it.
2866                          */
2867 
2868                         /*
2869                          * Are we lucky enough that an existing other prefix maps to this URI ?
2870                          */
2871                         prefix = m_prefixMap.lookupPrefix(uri);
2872                         if (prefix == null)
2873                         {
2874                             /* Unfortunately there is no existing prefix that happens to map to ours,
2875                              * so to avoid a prefix collision we must generated a new prefix to use.
2876                              * This is OK because the prefix URI mapping
2877                              * defined in the xsl:attribute is short in scope,
2878                              * just the xsl:attribute element itself,
2879                              * and at this point in serialization the body of the
2880                              * xsl:attribute, if any, is just a String. Right?
2881                              *   . . . I sure hope so - Brian M.
2882                              */
2883                             prefix = m_prefixMap.generateNextPrefix();
2884                         }
2885 
2886                         rawName = prefix + ':' + localName;
2887                     }
2888                 }
2889 
2890                 try
2891                 {
2892                     /* This is our last chance to make sure the namespace for this
2893                      * attribute is declared, especially if we just generated an alternate
2894                      * prefix to avoid a collision (the new prefix/rawName will go out of scope
2895                      * soon and be lost ...  last chance here.
2896                      */
2897                     String prefixUsed =
2898                         ensureAttributesNamespaceIsDeclared(
2899                             uri,
2900                             localName,
2901                             rawName);
2902                 }
2903                 catch (SAXException e)
2904                 {
2905                     // TODO Auto-generated catch block
2906                     e.printStackTrace();
2907                 }
2908             }
2909             m_attributes.addAttribute(uri, localName, rawName, type, value);
2910             was_added = true;
2911             if (m_tracer != null){
2912                 firePseudoAttributes();
2913             }
2914         }
2915         return was_added;
2916     }
2917 
2918     /**
2919      * To fire off the pseudo characters of attributes, as they currently
2920      * exist. This method should be called everytime an attribute is added,
2921      * or when an attribute value is changed, or an element is created.
2922      */
2923 
2924     protected void firePseudoAttributes()
2925     {
2926         if (m_tracer != null)
2927         {
2928             try
2929             {
2930                 // flush out the "<elemName" if not already flushed
2931                 m_writer.flush();
2932 
2933                 // make a StringBuffer to write the name="value" pairs to.
2934                 StringBuffer sb = new StringBuffer();
2935                 int nAttrs = m_attributes.getLength();
2936                 if (nAttrs > 0)
2937                 {
2938                     // make a writer that internally appends to the same
2939                     // StringBuffer
2940                     java.io.Writer writer =
2941                         new ToStream.WritertoStringBuffer(sb);
2942 
2943                     processAttributes(writer, nAttrs);
2944                     // Don't clear the attributes!
2945                     // We only want to see what would be written out
2946                     // at this point, we don't want to loose them.
2947                 }
2948                 sb.append('>');  // the potential > after the attributes.
2949                 // convert the StringBuffer to a char array and
2950                 // emit the trace event that these characters "might"
2951                 // be written
2952                 char ch[] = sb.toString().toCharArray();
2953                 m_tracer.fireGenerateEvent(
2954                     SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
2955                     ch,
2956                     0,
2957                     ch.length);
2958             }
2959             catch (IOException ioe)
2960             {
2961                 // ignore ?
2962             }
2963             catch (SAXException se)
2964             {
2965                 // ignore ?
2966             }
2967         }
2968     }
2969 
2970     /**
2971      * This inner class is used only to collect attribute values
2972      * written by the method writeAttrString() into a string buffer.
2973      * In this manner trace events, and the real writing of attributes will use
2974      * the same code.
2975      */
2976     private class WritertoStringBuffer extends java.io.Writer
2977     {
2978         final private StringBuffer m_stringbuf;
2979         /**
2980          * @see java.io.Writer#write(char[], int, int)
2981          */
2982         WritertoStringBuffer(StringBuffer sb)
2983         {
2984             m_stringbuf = sb;
2985         }
2986 
2987         public void write(char[] arg0, int arg1, int arg2) throws IOException
2988         {
2989             m_stringbuf.append(arg0, arg1, arg2);
2990         }
2991         /**
2992          * @see java.io.Writer#flush()
2993          */
2994         public void flush() throws IOException
2995         {
2996         }
2997         /**
2998          * @see java.io.Writer#close()
2999          */
3000         public void close() throws IOException
3001         {
3002         }
3003 
3004         public void write(int i)
3005         {
3006             m_stringbuf.append((char) i);
3007         }
3008 
3009         public void write(String s)
3010         {
3011             m_stringbuf.append(s);
3012         }
3013     }
3014 
3015     /**
3016      * @see SerializationHandler#setTransformer(Transformer)
3017      */
3018     public void setTransformer(Transformer transformer) {
3019         super.setTransformer(transformer);
3020         if (m_tracer != null
3021          && !(m_writer instanceof SerializerTraceWriter)  )
3022             m_writer = new SerializerTraceWriter(m_writer, m_tracer);
3023 
3024 
3025     }
3026     /**
3027      * Try's to reset the super class and reset this class for
3028      * re-use, so that you don't need to create a new serializer
3029      * (mostly for performance reasons).
3030      *
3031      * @return true if the class was successfuly reset.
3032      */
3033     public boolean reset()
3034     {
3035         boolean wasReset = false;
3036         if (super.reset())
3037         {
3038             resetToStream();
3039             wasReset = true;
3040         }
3041         return wasReset;
3042     }
3043 
3044     /**
3045      * Reset all of the fields owned by ToStream class
3046      *
3047      */
3048     private void resetToStream()
3049     {
3050          this.m_cdataStartCalled = false;
3051          /* The stream is being reset. It is one of
3052           * ToXMLStream, ToHTMLStream ... and this type can't be changed
3053           * so neither should m_charInfo which is associated with the
3054           * type of Stream. Just leave m_charInfo as-is for the next re-use.
3055           *
3056           */
3057          // this.m_charInfo = null; // don't set to null
3058 
3059          this.m_disableOutputEscapingStates.clear();
3060 
3061          this.m_escaping = true;
3062          // Leave m_format alone for now - Brian M.
3063          // this.m_format = null;
3064          this.m_inDoctype = false;
3065          this.m_ispreserve = false;
3066          this.m_ispreserve = false;
3067          this.m_isprevtext = false;
3068          this.m_isUTF8 = false; //  ?? used anywhere ??
3069          this.m_preserves.clear();
3070          this.m_shouldFlush = true;
3071          this.m_spaceBeforeClose = false;
3072          this.m_startNewLine = false;
3073          this.m_lineSepUse = true;
3074          // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3075          // this.m_writer = null;
3076          this.m_expandDTDEntities = true;
3077 
3078     }
3079 
3080     /**
3081       * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3082       * @param encoding the character encoding
3083       */
3084      public void setEncoding(String encoding)
3085      {
3086          String old = getEncoding();
3087          super.setEncoding(encoding);
3088          if (old == null || !old.equals(encoding)) {
3089             // If we have changed the setting of the
3090             m_encodingInfo = Encodings.getEncodingInfo(encoding);
3091 
3092             if (encoding != null && m_encodingInfo.name == null) {
3093                 // We tried to get an EncodingInfo for Object for the given
3094                 // encoding, but it came back with an internall null name
3095                 // so the encoding is not supported by the JDK, issue a message.
3096                 String msg = Utils.messages.createMessage(
3097                                 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ encoding });
3098                 try
3099                 {
3100                         // Prepare to issue the warning message
3101                         Transformer tran = super.getTransformer();
3102                         if (tran != null) {
3103                                 ErrorListener errHandler = tran.getErrorListener();
3104                                 // Issue the warning message
3105                                 if (null != errHandler && m_sourceLocator != null)
3106                                         errHandler.warning(new TransformerException(msg, m_sourceLocator));
3107                                 else
3108                                         System.out.println(msg);
3109                     }
3110                         else
3111                                 System.out.println(msg);
3112                 }
3113                 catch (Exception e){}
3114             }
3115          }
3116          return;
3117      }
3118 
3119     /**
3120      * Simple stack for boolean values.
3121      *
3122      * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
3123      * It exists to cut the serializers dependancy on that package.
3124      * A minor changes from that package are:
3125      * doesn't implement Clonable
3126      *
3127      * @xsl.usage internal
3128      */
3129     static final class BoolStack
3130     {
3131 
3132       /** Array of boolean values          */
3133       private boolean m_values[];
3134 
3135       /** Array size allocated           */
3136       private int m_allocatedSize;
3137 
3138       /** Index into the array of booleans          */
3139       private int m_index;
3140 
3141       /**
3142        * Default constructor.  Note that the default
3143        * block size is very small, for small lists.
3144        */
3145       public BoolStack()
3146       {
3147         this(32);
3148       }
3149 
3150       /**
3151        * Construct a IntVector, using the given block size.
3152        *
3153        * @param size array size to allocate
3154        */
3155       public BoolStack(int size)
3156       {
3157 
3158         m_allocatedSize = size;
3159         m_values = new boolean[size];
3160         m_index = -1;
3161       }
3162 
3163       /**
3164        * Get the length of the list.
3165        *
3166        * @return Current length of the list
3167        */
3168       public final int size()
3169       {
3170         return m_index + 1;
3171       }
3172 
3173       /**
3174        * Clears the stack.
3175        *
3176        */
3177       public final void clear()
3178       {
3179         m_index = -1;
3180       }
3181 
3182       /**
3183        * Pushes an item onto the top of this stack.
3184        *
3185        *
3186        * @param val the boolean to be pushed onto this stack.
3187        * @return  the <code>item</code> argument.
3188        */
3189       public final boolean push(boolean val)
3190       {
3191 
3192         if (m_index == m_allocatedSize - 1)
3193           grow();
3194 
3195         return (m_values[++m_index] = val);
3196       }
3197 
3198       /**
3199        * Removes the object at the top of this stack and returns that
3200        * object as the value of this function.
3201        *
3202        * @return     The object at the top of this stack.
3203        * @throws  EmptyStackException  if this stack is empty.
3204        */
3205       public final boolean pop()
3206       {
3207         return m_values[m_index--];
3208       }
3209 
3210       /**
3211        * Removes the object at the top of this stack and returns the
3212        * next object at the top as the value of this function.
3213        *
3214        *
3215        * @return Next object to the top or false if none there
3216        */
3217       public final boolean popAndTop()
3218       {
3219 
3220         m_index--;
3221 
3222         return (m_index >= 0) ? m_values[m_index] : false;
3223       }
3224 
3225       /**
3226        * Set the item at the top of this stack
3227        *
3228        *
3229        * @param b Object to set at the top of this stack
3230        */
3231       public final void setTop(boolean b)
3232       {
3233         m_values[m_index] = b;
3234       }
3235 
3236       /**
3237        * Looks at the object at the top of this stack without removing it
3238        * from the stack.
3239        *
3240        * @return     the object at the top of this stack.
3241        * @throws  EmptyStackException  if this stack is empty.
3242        */
3243       public final boolean peek()
3244       {
3245         return m_values[m_index];
3246       }
3247 
3248       /**
3249        * Looks at the object at the top of this stack without removing it
3250        * from the stack.  If the stack is empty, it returns false.
3251        *
3252        * @return     the object at the top of this stack.
3253        */
3254       public final boolean peekOrFalse()
3255       {
3256         return (m_index > -1) ? m_values[m_index] : false;
3257       }
3258 
3259       /**
3260        * Looks at the object at the top of this stack without removing it
3261        * from the stack.  If the stack is empty, it returns true.
3262        *
3263        * @return     the object at the top of this stack.
3264        */
3265       public final boolean peekOrTrue()
3266       {
3267         return (m_index > -1) ? m_values[m_index] : true;
3268       }
3269 
3270       /**
3271        * Tests if this stack is empty.
3272        *
3273        * @return  <code>true</code> if this stack is empty;
3274        *          <code>false</code> otherwise.
3275        */
3276       public boolean isEmpty()
3277       {
3278         return (m_index == -1);
3279       }
3280 
3281       /**
3282        * Grows the size of the stack
3283        *
3284        */
3285       private void grow()
3286       {
3287 
3288         m_allocatedSize *= 2;
3289 
3290         boolean newVector[] = new boolean[m_allocatedSize];
3291 
3292         System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
3293 
3294         m_values = newVector;
3295       }
3296     }
3297 
3298     // Implement DTDHandler
3299     /**
3300      * If this method is called, the serializer is used as a
3301      * DTDHandler, which changes behavior how the serializer
3302      * handles document entities.
3303      * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
3304      */
3305     public void notationDecl(String name, String pubID, String sysID) throws SAXException {
3306         // TODO Auto-generated method stub
3307         try {
3308             DTDprolog();
3309 
3310             m_writer.write("<!NOTATION ");
3311             m_writer.write(name);
3312             if (pubID != null) {
3313                 m_writer.write(" PUBLIC \"");
3314                 m_writer.write(pubID);
3315 
3316             }
3317             else {
3318                 m_writer.write(" SYSTEM \"");
3319                 m_writer.write(sysID);
3320             }
3321             m_writer.write("\" >");
3322             m_writer.write(m_lineSep, 0, m_lineSepLen);
3323         } catch (IOException e) {
3324             // TODO Auto-generated catch block
3325             e.printStackTrace();
3326         }
3327     }
3328 
3329     /**
3330      * If this method is called, the serializer is used as a
3331      * DTDHandler, which changes behavior how the serializer
3332      * handles document entities.
3333      * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
3334      */
3335     public void unparsedEntityDecl(String name, String pubID, String sysID, String notationName) throws SAXException {
3336         // TODO Auto-generated method stub
3337         try {
3338             DTDprolog();
3339 
3340             m_writer.write("<!ENTITY ");
3341             m_writer.write(name);
3342             if (pubID != null) {
3343                 m_writer.write(" PUBLIC \"");
3344                 m_writer.write(pubID);
3345 
3346             }
3347             else {
3348                 m_writer.write(" SYSTEM \"");
3349                 m_writer.write(sysID);
3350             }
3351             m_writer.write("\" NDATA ");
3352             m_writer.write(notationName);
3353             m_writer.write(" >");
3354             m_writer.write(m_lineSep, 0, m_lineSepLen);
3355         } catch (IOException e) {
3356             // TODO Auto-generated catch block
3357             e.printStackTrace();
3358         }
3359     }
3360 
3361     /**
3362      * A private helper method to output the
3363      * @throws SAXException
3364      * @throws IOException
3365      */
3366     private void DTDprolog() throws SAXException, IOException {
3367         final java.io.Writer writer = m_writer;
3368         if (m_needToOutputDocTypeDecl)
3369         {
3370             outputDocTypeDecl(m_elemContext.m_elementName, false);
3371             m_needToOutputDocTypeDecl = false;
3372         }
3373         if (m_inDoctype)
3374         {
3375             writer.write(" [");
3376             writer.write(m_lineSep, 0, m_lineSepLen);
3377             m_inDoctype = false;
3378         }
3379     }
3380 
3381     /**
3382      * If set to false the serializer does not expand DTD entities,
3383      * but leaves them as is, the default value is true;
3384      */
3385     public void setDTDEntityExpansion(boolean expand) {
3386         m_expandDTDEntities = expand;
3387     }
3388 }