You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by jk...@apache.org on 2024/02/21 19:51:57 UTC

(xalan-java) 02/02: This one's working for the test added in 2725. May not be cleanest solution, and I'm not sure whether any of the other surrogate handling needs similar fixes -- I don't know whether they ever run into the buffer break problem.

This is an automated email from the ASF dual-hosted git repository.

jkesselm pushed a commit to branch XALANJ-2725
in repository https://gitbox.apache.org/repos/asf/xalan-java.git

commit ec7f0e25d85192443a9fef2534e7625176fbfa4c
Author: kubycsolutions <ke...@kubyc.solutions>
AuthorDate: Wed Feb 21 14:51:48 2024 -0500

    This one's working for the test added in 2725. May not be cleanest solution, and I'm not sure whether any of the other surrogate handling needs similar fixes -- I don't know whether they ever run into the buffer break problem.
---
 .../java/org/apache/xml/serializer/ToStream.java   | 6735 ++++++++++----------
 1 file changed, 3364 insertions(+), 3371 deletions(-)

diff --git a/serializer/src/main/java/org/apache/xml/serializer/ToStream.java b/serializer/src/main/java/org/apache/xml/serializer/ToStream.java
index 0fde86c4..e4147171 100644
--- a/serializer/src/main/java/org/apache/xml/serializer/ToStream.java
+++ b/serializer/src/main/java/org/apache/xml/serializer/ToStream.java
@@ -47,3632 +47,3625 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 /**
- * This abstract class is a base class for other stream serializers
- * (xml, html, text ...) that write output to a stream.  Note that
- * this is stateful, NOT designed to be multithreaded; each thread and
- * each output stream should have its own instance.
+ * This abstract class is a base class for other stream 
+ * serializers (xml, html, text ...) that write output to a stream.
  * 
  * @xsl.usage internal
  */
 abstract public class ToStream extends SerializerBase
 {
 
-    private static final String COMMENT_BEGIN = "<!--";
-    private static final String COMMENT_END = "-->";
-
-    /** Stack to keep track of disabling output escaping. */
-    protected BoolStack m_disableOutputEscapingStates = new BoolStack();
-
-    /**
-     * The encoding information associated with this serializer.
-     * Although initially there is no encoding,
-     * there is a dummy EncodingInfo object that will say
-     * that every character is in the encoding. This is useful
-     * for a serializer that is in temporary output state and has
-     * no associated encoding. A serializer in final output state
-     * will have an encoding, and will worry about whether 
-     * single chars or surrogate pairs of high/low chars form
-     * characters in the output encoding. 
-     */
-    EncodingInfo m_encodingInfo = new EncodingInfo(null,null, '\u0000');
-    
-    /**
-     * Stack to keep track of whether or not we need to
-     * preserve whitespace.
-     * 
-     * Used to push/pop values used for the field m_ispreserve, but
-     * m_ispreserve is only relevant if m_doIndent is true.
-     * If m_doIndent is false this field has no impact.
-     * 
-     */
-    protected BoolStack m_preserves = new BoolStack();
-
-    /**
-     * State flag to tell if preservation of whitespace
-     * is important. 
-     * 
-     * Used only in shouldIndent() but only if m_doIndent is true.
-     * If m_doIndent is false this flag has no impact.
-     * 
-     */
-    protected boolean m_ispreserve = false;
-
-    /**
-     * State flag that tells if the previous node processed
-     * was text, so we can tell if we should preserve whitespace.
-     * 
-     * Used in endDocument() and shouldIndent() but
-     * only if m_doIndent is true. 
-     * If m_doIndent is false this flag has no impact.
-     */
-    protected boolean m_isprevtext = false;
-        
-    private static final char[] s_systemLineSep;
-    static {
-        s_systemLineSep = SecuritySupport.getSystemProperty("line.separator").toCharArray();
-    }
-    
-    /**
-     * The system line separator for writing out line breaks.
-     * The default value is from the system property,
-     * but this value can be set through the xsl:output
-     * extension attribute xalan:line-separator.
-     */
-    protected char[] m_lineSep = s_systemLineSep;
-        
-        
-    /**
-     * True if the the system line separator is to be used.
-     */    
-    protected boolean m_lineSepUse = true;    
-
-    /**
-     * The length of the line seperator, since the write is done
-     * one character at a time.
-     */
-    protected int m_lineSepLen = m_lineSep.length;
-
-    /**
-     * Map that tells which characters should have special treatment, and it
-     *  provides character to entity name lookup.
-     */
-    protected CharInfo m_charInfo;
-
-    /** True if we control the buffer, and we should flush the output on endDocument. */
-    boolean m_shouldFlush = true;
-
-    /**
-     * Add space before self-closing '/&gt;' syntax for XHTML.
-     */
-    protected boolean m_spaceBeforeClose = false;
-
-    /**
-     * Flag to signal that a newline should be added.
-     * 
-     * Used only in indent() which is called only if m_doIndent is true.
-     * If m_doIndent is false this flag has no impact.
-     */
-    boolean m_startNewLine;
-
-    /**
-     * Tells if we're in an internal document type subset.
-     */
-    protected boolean m_inDoctype = false;
-
-    /**
-       * Flag to quickly tell if the encoding is UTF8.
-       */
-    boolean m_isUTF8 = false;
-
-
-    /**
-     * remembers if we are in between the startCDATA() and endCDATA() callbacks
-     */
-    protected boolean m_cdataStartCalled = false;
-    
-    /**
-     * If this flag is true DTD entity references are not left as-is,
-     * which is exiting older behavior.
-     */
-    private boolean m_expandDTDEntities = true;
-
-    /**
-     * Traditionally, we handled Surrogate Character Pairs by looking
-     * ahead in the input buffer. This could fail if, eg, the pair crossed
-     * between one call to characters() and the next, which can happen
-     * since SAX providers are free to manage buffering as they see fit
-     * and what the XML Data Model considers a single block of text
-     * may be delivered in multiple calls.
-     *
-     * The more robust solution is to maintain state, setting the High
-     * UTF16 Surrogate character aside and processing it when the Low
-     * Surrogate arrives.
-     *
-     * However, handling this robustly this requires recognizing, and
-     * handling, cases where a Surrogate appears but is not adjacent to
-     * the other half of the pair. That's illegal UTF16, but as utility
-     * code we can't guarantee some caller won't attempt it.
-     *
-     * Historically, we have handled this one of two ways, either
-     * generating an IOException with ER_INVALID_UTF18_SURROGATE or
-     * outputting the bad surrogate as a Numeric Character Reference
-     * (and possibly issuing a message to stderr, as in ToTextStream).
-     * The inconsistency annoys me a bit.  Only SGML-based formats
-     * support NCRs, and XML explicitly says that even an NCR may not
-     * represent an isolated surrogate.  Hence, for correctness, we AT
-     * LEAST want the stderr message, and arguably should be throwing
-     * the exception.  However, if we change any of this behavior we
-     * want to be able to revert to the prior response, in case some
-     * user is actually expecting to see that.
-     *
-     * Note that since we process char arrays, the "pending high surrogate"
-     * buffer is a char, with 0 used to indicate "empty buffer".
-     */
-    private char m_pendingUTF16HighSurrogate = 0;
-
-    /**
-     * Default constructor
-     */
-    public ToStream()
-    {
-    }
-
-    /**
-     * This helper method writes the syntax for closing a CDATA section.
-     *
-     * @throws org.xml.sax.SAXException if write fails
-     */
-    protected void closeCDATA() throws org.xml.sax.SAXException
-    {
-        try
-        {
-            m_writer.write(CDATA_DELIMITER_CLOSE);
-            // write out a CDATA section closing "]]>"
-            m_cdataTagOpen = false; // Remember that we have done so.
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
-    }
-
-    /**
-     * Serializes the DOM node. Throws an exception only if an I/O
-     * exception occured while serializing.
-     *
-     * @param node Node to serialize.
-     * @throws IOException An I/O exception occured while serializing
-     */
-    public void serialize(Node node) throws IOException
-    {
-
-        try
-        {
-            TreeWalker walker =
-                new TreeWalker(this);
-
-            walker.traverse(node);
-        }
-        catch (org.xml.sax.SAXException se)
-        {
-            throw new WrappedRuntimeException(se);
-        }
-    }
-
-    /**
-     * Taken from XSLTC 
-     */
-    protected boolean m_escaping = true;
-
-    /**
-     * Flush the formatter's result stream.
-     *
-     * @throws org.xml.sax.SAXException if write fails
-     */
-    protected final void flushWriter() throws org.xml.sax.SAXException
-    {
-        final java.io.Writer writer = m_writer;
-        if (null != writer)
-        {
-            try
-            {
-                if (writer instanceof WriterToUTF8Buffered)
-                {
-                    if (m_shouldFlush)
-                         ((WriterToUTF8Buffered) writer).flush();
-                    else
-                         ((WriterToUTF8Buffered) writer).flushBuffer();
-                }
-                if (writer instanceof WriterToASCI)
-                {
-                    if (m_shouldFlush)
-                        writer.flush();
-                }
-                else
-                {
-                    // Flush always. 
-                    // Not a great thing if the writer was created 
-                    // by this class, but don't have a choice.
-                    writer.flush();
-                }
-            }
-            catch (IOException ioe)
-            {
-                throw new org.xml.sax.SAXException(ioe);
-            }
-        }
-    }
-
-    OutputStream m_outputStream;
-    /**
-     * Get the output stream where the events will be serialized to.
-     *
-     * @return reference to the result stream, or null of only a writer was
-     * set.
-     */
-    public OutputStream getOutputStream()
-    {
-        return m_outputStream;
-    }
-
-    // Implement DeclHandler
-
-    /**
-     *   Report an element type declaration.
-     *  
-     *   <p>The content model will consist of the string "EMPTY", the
-     *   string "ANY", or a parenthesised group, optionally followed
-     *   by an occurrence indicator.  The model will be normalized so
-     *   that all whitespace is removed,and will include the enclosing
-     *   parentheses.</p>
-     *  
-     *   @param name The element type name.
-     *   @param model The content model as a normalized string.
-     *   @exception SAXException The application may raise an exception.
-     */
-    public void elementDecl(String name, String model) throws SAXException
-    {
-        // Do not inline external DTD
-        if (m_inExternalDTD)
-            return;
-        try
-        {
-            final java.io.Writer writer = m_writer;
-            DTDprolog();
-
-            writer.write("<!ELEMENT ");
-            writer.write(name);
-            writer.write(' ');
-            writer.write(model);
-            writer.write('>');
-            writer.write(m_lineSep, 0, m_lineSepLen);
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
-
-    }
-
-    /**
-     * Report an internal entity declaration.
-     *
-     * <p>Only the effective (first) declaration for each entity
-     * will be reported.</p>
-     *
-     * @param name The name of the entity.  If it is a parameter
-     *        entity, the name will begin with '%'.
-     * @param value The replacement text of the entity.
-     * @exception SAXException The application may raise an exception.
-     * @see #externalEntityDecl
-     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
-     */
-    public void internalEntityDecl(String name, String value)
-        throws SAXException
-    {
-        // Do not inline external DTD
-        if (m_inExternalDTD)
-            return;
-        try
-        {
-            DTDprolog();
-            outputEntityDecl(name, value);
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
-
-    }
-
-    /**
-     * Output the doc type declaration.
-     *
-     * @param name non-null reference to document type name.
-     * NEEDSDOC @param value
-     *
-     * @throws IOException if an error occurs during writing.
-     */
-    void outputEntityDecl(String name, String value) throws IOException
-    {
-        final java.io.Writer writer = m_writer;
-        writer.write("<!ENTITY ");
-        writer.write(name);
-        writer.write(" \"");
-        writer.write(value);
-        writer.write("\">");
-        writer.write(m_lineSep, 0, m_lineSepLen);
-    }
-
-    /**
-     * Output a system-dependent line break.
-     *
-     * @throws IOException if an error occurs during writing.
-
-     */
-    protected final void outputLineSep() throws IOException
-    {
-        m_writer.write(m_lineSep, 0, m_lineSepLen);
-    }
-
-    void setProp(String name, String val, boolean defaultVal) {
-        if (val != null) {
-
-
-            char first = getFirstCharLocName(name);
-            switch (first) {
-            case 'c':
-                if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name)) {
-                    String cdataSectionNames = val;
-                    addCdataSectionElements(cdataSectionNames);
-                }
-                break;
-            case 'd':
-                if (OutputKeys.DOCTYPE_SYSTEM.equals(name)) {
-                    this.m_doctypeSystem = val;
-                } else if (OutputKeys.DOCTYPE_PUBLIC.equals(name)) {
-                    this.m_doctypePublic = val;
-                    if (val.startsWith("-//W3C//DTD XHTML"))
-                        m_spaceBeforeClose = true;
-                }
-                break;
-            case 'e':
-                String newEncoding = val;
-                if (OutputKeys.ENCODING.equals(name)) {
-                    String possible_encoding = Encodings.getMimeEncoding(val);
-                    if (possible_encoding != null) {
-                        // if the encoding is being set, try to get the
-                        // preferred
-                        // mime-name and set it too.
-                        super.setProp("mime-name", possible_encoding,
-                                defaultVal);
-                    }
-                    final String oldExplicitEncoding = getOutputPropertyNonDefault(OutputKeys.ENCODING);
-                    final String oldDefaultEncoding  = getOutputPropertyDefault(OutputKeys.ENCODING);
-                    if ( (defaultVal && ( oldDefaultEncoding == null || !oldDefaultEncoding.equalsIgnoreCase(newEncoding)))
-                            || ( !defaultVal && (oldExplicitEncoding == null || !oldExplicitEncoding.equalsIgnoreCase(newEncoding) ))) {
-                       // We are trying to change the default or the non-default setting of the encoding to a different value
-                       // from what it was
-                       
-                       EncodingInfo encodingInfo = Encodings.getEncodingInfo(newEncoding);
-                       if (newEncoding != null && encodingInfo.name == null) {
-                        // We tried to get an EncodingInfo for Object for the given
-                        // encoding, but it came back with an internall null name
-                        // so the encoding is not supported by the JDK, issue a message.
-                        final String msg = Utils.messages.createMessage(
-                                MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ newEncoding });
-                        
-                        final String msg2 = 
-                            "Warning: encoding \"" + newEncoding + "\" not supported, using "
-                                   + Encodings.DEFAULT_MIME_ENCODING;
-                        try {
-                                // Prepare to issue the warning message
-                                final Transformer tran = super.getTransformer();
-                                if (tran != null) {
-                                    final ErrorListener errHandler = tran
-                                            .getErrorListener();
-                                    // Issue the warning message
-                                    if (null != errHandler
-                                            && m_sourceLocator != null) {
-                                        errHandler
-                                                .warning(new TransformerException(
-                                                        msg, m_sourceLocator));
-                                        errHandler
-                                                .warning(new TransformerException(
-                                                        msg2, m_sourceLocator));
-                                    } else {
-                                        System.out.println(msg);
-                                        System.out.println(msg2);
-                                    }
-                                } else {
-                                    System.out.println(msg);
-                                    System.out.println(msg2);
-                                }
-                            } catch (Exception e) {
-                            }
-
-                            // We said we are using UTF-8, so use it
-                            newEncoding = Encodings.DEFAULT_MIME_ENCODING;
-                            val = Encodings.DEFAULT_MIME_ENCODING; // to store the modified value into the properties a little later
-                            encodingInfo = Encodings.getEncodingInfo(newEncoding);
-
-                        } 
-                       // The encoding was good, or was forced to UTF-8 above
-                       
-                       
-                       // If there is already a non-default set encoding and we 
-                       // are trying to set the default encoding, skip the this block
-                       // as the non-default value is already the one to use.
-                       if (defaultVal == false || oldExplicitEncoding == null) {
-                           m_encodingInfo = encodingInfo;
-                           if (newEncoding != null)
-                               m_isUTF8 = newEncoding.equals(Encodings.DEFAULT_MIME_ENCODING);
-                           
-                           // if there was a previously set OutputStream
-                           OutputStream os = getOutputStream();
-                           if (os != null) {
-                               Writer w = getWriter();
-                               
-                               // If the writer was previously set, but
-                               // set by the user, or if the new encoding is the same
-                               // as the old encoding, skip this block
-                               String oldEncoding = getOutputProperty(OutputKeys.ENCODING);
-                               if ((w == null || !m_writer_set_by_user) 
-                                       && !newEncoding.equalsIgnoreCase(oldEncoding)) {
-                                   // Make the change of encoding in our internal
-                                   // table, then call setOutputStreamInternal
-                                   // which will stomp on the old Writer (if any)
-                                   // with a new Writer with the new encoding.
-                                   super.setProp(name, val, defaultVal);
-                                   setOutputStreamInternal(os,false);
-                               }
-                           }
-                       }
-                    }
-                }
-                break;
-            case 'i':
-                if (OutputPropertiesFactory.S_KEY_INDENT_AMOUNT.equals(name)) {
-                    setIndentAmount(Integer.parseInt(val));
-                } else if (OutputKeys.INDENT.equals(name)) {
-                    boolean b = "yes".equals(val) ? true : false;
-                    m_doIndent = b;
-                }
-
-                break;
-            case 'l':
-                if (OutputPropertiesFactory.S_KEY_LINE_SEPARATOR.equals(name)) {
-                    m_lineSep = val.toCharArray();
-                    m_lineSepLen = m_lineSep.length;
-                }
-
-                break;
-            case 'm':
-                if (OutputKeys.MEDIA_TYPE.equals(name)) {
-                    m_mediatype = val;
-                }
-                break;
-            case 'o':
-                if (OutputKeys.OMIT_XML_DECLARATION.equals(name)) {
-                    boolean b = "yes".equals(val) ? true : false;
-                    this.m_shouldNotWriteXMLHeader = b;
-                }
-                break;
-            case 's':
-                // if standalone was explicitly specified
-                if (OutputKeys.STANDALONE.equals(name)) {
-                    if (defaultVal) {
-                        setStandaloneInternal(val);
-                    } else {
-                        m_standaloneWasSpecified = true;
-                        setStandaloneInternal(val);
-                    }
-                }
-
-                break;
-            case 'v':
-                if (OutputKeys.VERSION.equals(name)) {
-                    m_version = val;
-                }
-                break;
-            default:
-                break;
-
-            } 
-            super.setProp(name, val, defaultVal);
-        }
-    }
-    /**
-     * Specifies an output format for this serializer. It the
-     * serializer has already been associated with an output format,
-     * it will switch to the new format. This method should not be
-     * called while the serializer is in the process of serializing
-     * a document.
-     *
-     * @param format The output format to use
-     */
-    public void setOutputFormat(Properties format)
-    {
-
-        boolean shouldFlush = m_shouldFlush;
-        
-        if (format != null)
-        {
-            // Set the default values first,
-            // and the non-default values after that, 
-            // just in case there is some unexpected
-            // residual values left over from over-ridden default values
-            Enumeration propNames;
-            propNames = format.propertyNames();
-            while (propNames.hasMoreElements())
-            {
-                String key = (String) propNames.nextElement();
-                // Get the value, possibly a default value
-                String value = format.getProperty(key);
-                // Get the non-default value (if any).
-                String explicitValue = (String) format.get(key);
-                if (explicitValue == null && value != null) {
-                    // This is a default value
-                    this.setOutputPropertyDefault(key,value);
-                }
-                if (explicitValue != null) {
-                    // This is an explicit non-default value
-                    this.setOutputProperty(key,explicitValue);
-                }
-            } 
-        }   
-
-        // Access this only from the Hashtable level... we don't want to 
-        // get default properties.
-        String entitiesFileName =
-            (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
-
-        if (null != entitiesFileName)
-        {
-
-            String method = 
-                (String) format.get(OutputKeys.METHOD);
-            
-            m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
-        }
-
-
-         
-
-        m_shouldFlush = shouldFlush;
-    }
-
-    /**
-     * Returns the output format for this serializer.
-     *
-     * @return The output format in use
-     */
-    public Properties getOutputFormat() {
-        Properties def = new Properties();
-        {
-            Set s = getOutputPropDefaultKeys();
-            Iterator i = s.iterator();
-            while (i.hasNext()) {
-                String key = (String) i.next();
-                String val = getOutputPropertyDefault(key);
-                def.put(key, val);
-            }
-        }
-        
-        Properties props = new Properties(def);
-        {
-            Set s = getOutputPropKeys();
-            Iterator i = s.iterator();
-            while (i.hasNext()) {
-                String key = (String) i.next();
-                String val = getOutputPropertyNonDefault(key);
-                if (val != null)
-                    props.put(key, val);
-            }
-        }
-        return props;
-    }
-
-    /**
-     * Specifies a writer to which the document should be serialized.
-     * This method should not be called while the serializer is in
-     * the process of serializing a document.
-     *
-     * @param writer The output writer stream
-     */
-    public void setWriter(Writer writer)
-    {        
-        setWriterInternal(writer, true);
-    }
-    
-    private boolean m_writer_set_by_user;
-    private void setWriterInternal(Writer writer, boolean setByUser) {
-
-        m_writer_set_by_user = setByUser;
-        m_writer = writer;
-        // if we are tracing events we need to trace what
-        // characters are written to the output writer.
-        if (m_tracer != null) {
-            boolean noTracerYet = true;
-            Writer w2 = m_writer;
-            while (w2 instanceof WriterChain) {
-                if (w2 instanceof SerializerTraceWriter) {
-                    noTracerYet = false;
-                    break;
-                }
-                w2 = ((WriterChain)w2).getWriter();
-            }
-            if (noTracerYet)
-                m_writer = new SerializerTraceWriter(m_writer, m_tracer);
-        }
-    }
-    
-    /**
-     * Set if the operating systems end-of-line line separator should
-     * be used when serializing.  If set false NL character 
-     * (decimal 10) is left alone, otherwise the new-line will be replaced on
-     * output with the systems line separator. For example on UNIX this is
-     * NL, while on Windows it is two characters, CR NL, where CR is the
-     * carriage-return (decimal 13).
-     *  
-     * @param use_sytem_line_break True if an input NL is replaced with the 
-     * operating systems end-of-line separator.
-     * @return The previously set value of the serializer.
-     */
-    public boolean setLineSepUse(boolean use_sytem_line_break)
-    {
-        boolean oldValue = m_lineSepUse;
-        m_lineSepUse = use_sytem_line_break;
-        return oldValue;
-    }
-
-    /**
-     * Specifies an output stream to which the document should be
-     * serialized. This method should not be called while the
-     * serializer is in the process of serializing a document.
-     * <p>
-     * The encoding specified in the output properties is used, or
-     * if no encoding was specified, the default for the selected
-     * output method.
-     *
-     * @param output The output stream
-     */
-    public void setOutputStream(OutputStream output)
-    {
-        setOutputStreamInternal(output, true);
-    }
-    
-    private void setOutputStreamInternal(OutputStream output, boolean setByUser)
-    {
-        m_outputStream = output;
-        String encoding = getOutputProperty(OutputKeys.ENCODING);        
-        if (Encodings.DEFAULT_MIME_ENCODING.equalsIgnoreCase(encoding))
-        {
-            // We wrap the OutputStream with a writer, but
-            // not one set by the user
-            setWriterInternal(new WriterToUTF8Buffered(output), false);
-        } else if (
-                "WINDOWS-1250".equals(encoding)
-                || "US-ASCII".equals(encoding)
-                || "ASCII".equals(encoding))
-        {
-            setWriterInternal(new WriterToASCI(output), false);
-        } else if (encoding != null) {
-            Writer osw = null;
-                try
-                {
-                    osw = Encodings.getWriter(output, encoding);
-                }
-                catch (UnsupportedEncodingException uee)
-                {
-                    osw = null;
-                }
-
-            
-            if (osw == null) {
-                System.out.println(
-                    "Warning: encoding \""
-                        + encoding
-                        + "\" not supported"
-                        + ", using "
-                        + Encodings.DEFAULT_MIME_ENCODING);
-
-                encoding = Encodings.DEFAULT_MIME_ENCODING;
-                setEncoding(encoding);
-                try {
-                    osw = Encodings.getWriter(output, encoding);
-                } catch (UnsupportedEncodingException e) {
-                    // We can't really get here, UTF-8 is always supported
-                    // This try-catch exists to make the compiler happy
-                    e.printStackTrace();
-                }
-            }
-            setWriterInternal(osw,false);
-        }
-        else {
-            // don't have any encoding, but we have an OutputStream
-            Writer osw = new OutputStreamWriter(output);
-            setWriterInternal(osw,false);
-        }
-    }
-
-    /**
-     * @see SerializationHandler#setEscaping(boolean)
-     */
-    public boolean setEscaping(boolean escape)
-    {
-        final boolean temp = m_escaping;
-        m_escaping = escape;
-        return temp;
-
-    }
-
-
-    /**
-     * Might print a newline character and the indentation amount
-     * of the given depth.
-     * 
-     * @param depth the indentation depth (element nesting depth)
-     *
-     * @throws IOException if an error occurs during writing.
-     */
-    protected void indent(int depth) throws IOException
-    {
-
-        if (m_startNewLine)
-            outputLineSep();
-        /* For m_indentAmount > 0 this extra test might be slower
-         * but Xalan's default value is 0, so this extra test
-         * will run faster in that situation.
-         */
-        if (m_indentAmount > 0)
-            printSpace(depth * m_indentAmount);
-
-    }
-    
-    /**
-     * Indent at the current element nesting depth.
-     * @throws IOException if an error occurs while writing
-     */
-    protected void indent() throws IOException
-    {
-        indent(m_elemContext.m_currentElemDepth);
-    }
-    /**
-     * Prints <var>n</var> spaces.
-     * @param n         Number of spaces to print.
-     *
-     * @throws IOException if an error occurs while writing
-     */
-    private void printSpace(int n) throws IOException
-    {
-        final java.io.Writer writer = m_writer;
-        for (int i = 0; i < n; i++)
-        {
-            writer.write(' ');
-        }
-
-    }
-
-    /**
-     * Report an attribute type declaration.
-     *
-     * <p>Only the effective (first) declaration for an attribute will
-     * be reported.  The type will be one of the strings "CDATA",
-     * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
-     * "ENTITIES", or "NOTATION", or a parenthesized token group with
-     * the separator "|" and all whitespace removed.</p>
-     *
-     * @param eName The name of the associated element.
-     * @param aName The name of the attribute.
-     * @param type A string representing the attribute type.
-     * @param valueDefault A string representing the attribute default
-     *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
-     *        none of these applies.
-     * @param value A string representing the attribute's default value,
-     *        or null if there is none.
-     * @exception SAXException The application may raise an exception.
-     */
-    public void attributeDecl(
-        String eName,
-        String aName,
-        String type,
-        String valueDefault,
-        String value)
-        throws SAXException
-    {
-        // Do not inline external DTD
-        if (m_inExternalDTD)
-            return;
-        try
-        {
-            final java.io.Writer writer = m_writer;
-            DTDprolog();
-
-            writer.write("<!ATTLIST ");
-            writer.write(eName);
-            writer.write(' ');
-
-            writer.write(aName);
-            writer.write(' ');
-            writer.write(type);
-            if (valueDefault != null)
-            {
-                writer.write(' ');
-                writer.write(valueDefault);
-            }
-
-            //writer.write(" ");
-            //writer.write(value);
-            writer.write('>');
-            writer.write(m_lineSep, 0, m_lineSepLen);
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
-    }
-
-    /**
-     * Get the character stream where the events will be serialized to.
-     *
-     * @return Reference to the result Writer, or null.
-     */
-    public Writer getWriter()
-    {
-        return m_writer;
-    }
-
-    /**
-     * Report a parsed external entity declaration.
-     *
-     * <p>Only the effective (first) declaration for each entity
-     * will be reported.</p>
-     *
-     * @param name The name of the entity.  If it is a parameter
-     *        entity, the name will begin with '%'.
-     * @param publicId The declared public identifier of the entity, or
-     *        null if none was declared.
-     * @param systemId The declared system identifier of the entity.
-     * @exception SAXException The application may raise an exception.
-     * @see #internalEntityDecl
-     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
-     */
-    public void externalEntityDecl(
-        String name,
-        String publicId,
-        String systemId)
-        throws SAXException
-    {
-        try {
-            DTDprolog();
-            
-            m_writer.write("<!ENTITY ");            
-            m_writer.write(name);
-            if (publicId != null) {
-                m_writer.write(" PUBLIC \"");
-                m_writer.write(publicId);
-  
-            }
-            else {
-                m_writer.write(" SYSTEM \"");
-                m_writer.write(systemId);
-            }
-            m_writer.write("\" >");
-            m_writer.write(m_lineSep, 0, m_lineSepLen);
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-
-    }
-
-    /**
-     * @return true iff this character can be written without escaping.
-     * @param ch character to be tested
-     */
-    protected boolean escapingNotNeeded(char ch)
-    {
-        final boolean ret;
-        if (ch < 127)
+  private static final String COMMENT_BEGIN = "<!--";
+  private static final String COMMENT_END = "-->";
+
+  /** Stack to keep track of disabling output escaping. */
+  protected BoolStack m_disableOutputEscapingStates = new BoolStack();
+
+
+  /**
+   * The encoding information associated with this serializer.
+   * Although initially there is no encoding,
+   * there is a dummy EncodingInfo object that will say
+   * that every character is in the encoding. This is useful
+   * for a serializer that is in temporary output state and has
+   * no associated encoding. A serializer in final output state
+   * will have an encoding, and will worry about whether 
+   * single chars or surrogate pairs of high/low chars form
+   * characters in the output encoding. 
+   */
+  EncodingInfo m_encodingInfo = new EncodingInfo(null,null, '\u0000');
+
+  /**
+   * Stack to keep track of whether or not we need to
+   * preserve whitespace.
+   * 
+   * Used to push/pop values used for the field m_ispreserve, but
+   * m_ispreserve is only relevant if m_doIndent is true.
+   * If m_doIndent is false this field has no impact.
+   * 
+   */
+  protected BoolStack m_preserves = new BoolStack();
+
+  /**
+   * State flag to tell if preservation of whitespace
+   * is important. 
+   * 
+   * Used only in shouldIndent() but only if m_doIndent is true.
+   * If m_doIndent is false this flag has no impact.
+   * 
+   */
+  protected boolean m_ispreserve = false;
+
+  /**
+   * State flag that tells if the previous node processed
+   * was text, so we can tell if we should preserve whitespace.
+   * 
+   * Used in endDocument() and shouldIndent() but
+   * only if m_doIndent is true. 
+   * If m_doIndent is false this flag has no impact.
+   */
+  protected boolean m_isprevtext = false;
+
+  /**
+   * When non-zero, high UTF16 Surrogate Character parsed
+   * from the input; next input should be the Low Surrogate.
+   * Zero when not in that state.
+   * (Replaces lookahead, which had buffer edge problems, with state.)
+   */
+  protected char m_pendingHighUTF16Surrogate = 0;
+
+  private static final char[] s_systemLineSep;
+  static {
+    s_systemLineSep = SecuritySupport.getSystemProperty("line.separator").toCharArray();
+  }
+
+  /**
+   * The system line separator for writing out line breaks.
+   * The default value is from the system property,
+   * but this value can be set through the xsl:output
+   * extension attribute xalan:line-separator.
+   */
+  protected char[] m_lineSep = s_systemLineSep;
+
+
+  /**
+   * True if the the system line separator is to be used.
+   */    
+  protected boolean m_lineSepUse = true;    
+
+  /**
+   * The length of the line seperator, since the write is done
+   * one character at a time.
+   */
+  protected int m_lineSepLen = m_lineSep.length;
+
+  /**
+   * Map that tells which characters should have special treatment, and it
+   *  provides character to entity name lookup.
+   */
+  protected CharInfo m_charInfo;
+
+  /** True if we control the buffer, and we should flush the output on endDocument. */
+  boolean m_shouldFlush = true;
+
+  /**
+   * Add space before self-closing '/&gt;' syntax for XHTML.
+   */
+  protected boolean m_spaceBeforeClose = false;
+
+  /**
+   * Flag to signal that a newline should be added.
+   * 
+   * Used only in indent() which is called only if m_doIndent is true.
+   * If m_doIndent is false this flag has no impact.
+   */
+  boolean m_startNewLine;
+
+  /**
+   * Tells if we're in an internal document type subset.
+   */
+  protected boolean m_inDoctype = false;
+
+  /**
+   * Flag to quickly tell if the encoding is UTF8.
+   */
+  boolean m_isUTF8 = false;
+
+
+  /**
+   * remembers if we are in between the startCDATA() and endCDATA() callbacks
+   */
+  protected boolean m_cdataStartCalled = false;
+
+  /**
+   * If this flag is true DTD entity references are not left as-is,
+   * which is exiting older behavior.
+   */
+  private boolean m_expandDTDEntities = true;
+
+
+  /**
+   * Default constructor
+   */
+  public ToStream()
+  {
+  }
+
+  /**
+   * This helper method writes the syntax for closing a CDATA section.
+   *
+   * @throws org.xml.sax.SAXException if write fails
+   */
+  protected void closeCDATA() throws org.xml.sax.SAXException
+  {
+    try
+    {
+      m_writer.write(CDATA_DELIMITER_CLOSE);
+      // write out a CDATA section closing "]]>"
+      m_cdataTagOpen = false; // Remember that we have done so.
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+  }
+
+  /**
+   * Serializes the DOM node. Throws an exception only if an I/O
+   * exception occured while serializing.
+   *
+   * @param node Node to serialize.
+   * @throws IOException An I/O exception occured while serializing
+   */
+  public void serialize(Node node) throws IOException
+  {
+
+    try
+    {
+      TreeWalker walker =
+          new TreeWalker(this);
+
+      walker.traverse(node);
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new WrappedRuntimeException(se);
+    }
+  }
+
+  /**
+   * Taken from XSLTC 
+   */
+  protected boolean m_escaping = true;
+
+  /**
+   * Flush the formatter's result stream.
+   *
+   * @throws org.xml.sax.SAXException if write fails
+   */
+  protected final void flushWriter() throws org.xml.sax.SAXException
+  {
+    final java.io.Writer writer = m_writer;
+    if (null != writer)
+    {
+      try
+      {
+        if (writer instanceof WriterToUTF8Buffered)
         {
-            // This is the old/fast code here, but is this 
-            // correct for all encodings?
-            if (ch >= CharInfo.S_SPACE || (CharInfo.S_LINEFEED == ch || 
-                    CharInfo.S_CARRIAGERETURN == ch || CharInfo.S_HORIZONAL_TAB == ch))
-                ret= true;
-            else
-                ret = false;
-        }
-        else {            
-            ret = m_encodingInfo.isInEncoding(ch);
+          if (m_shouldFlush)
+            ((WriterToUTF8Buffered) writer).flush();
+          else
+            ((WriterToUTF8Buffered) writer).flushBuffer();
         }
-        return ret;
-    }
-
-    /**
-     * Once a surrogate has been detected, write out the pair of
-     * characters if it is in the encoding, or if there is no
-     * encoding, otherwise write out an numeric character reference
-     * of the value of the unicode code point of the character
-     * represented by the high/low surrogate pair.
-     * <p>
-     * An exception is thrown if there is no low surrogate in the pair,
-     * because the array ends unexpectely, or if the low char is there
-     * but its value is such that it is not a low surrogate.
-     *
-     * @param high the first (high) part of the surrogate, which
-     * must be confirmed before calling this method.
-     * @param ch Character array.
-     * @param i position Where the surrogate was detected.
-     * @param end The end index of the significant characters.
-     * @return 0 if the pair of characters was written out as-is,
-     * the unicode code point of the character represented by
-     * the surrogate pair if a numeric char ref with that value
-     * was written out. 
-     * 
-     * @throws IOException if  invalid UTF-16 surrogate detected.
-     */
-    protected int writeUTF16Surrogate(final char high, char ch[], int i, int end)
-        throws IOException
-    {
-	// THROWS if surrogate pair crosses input buffers
-	// Should probably handle this better.
-        if (i + 1 >= end)
+        if (writer instanceof WriterToASCI)
         {
-            throw new IOException(
-                Utils.messages.createMessage(
-                    MsgKey.ER_INVALID_UTF16_SURROGATE,
-                    new Object[] { Integer.toHexString((int) high)}));
-        }
-        
-        final char low = ch[i+1];
-	return writeUTF16Surrogate(high, low);
-    }
-	
-
-    /**
-     * Once a surrogate has been detected, write out the pair of
-     * characters if it is in the encoding, or if there is no
-     * encoding, otherwise write out an numeric character reference
-     * of the value of the unicode code point of the character
-     * represented by the high/low surrogate pair.
-     * <p>
-     * An exception is thrown if there is no low surrogate in the pair,
-     * because the array ends unexpectely, or if the low char is there
-     * but its value is such that it is not a low surrogate.
-     *
-     * @param high the first (high) part of the surrogate, which
-     * must be confirmed before calling this method.
-     * @param low the second (low) part of the presumed surrogate
-     * @return 0 if the pair of characters was written out as-is,
-     * or the unicode code point of the character represented by
-     * the surrogate pair if a numeric char ref with that value
-     * was written out. (REVIEW: Is this needed?)
-     * 
-     * @throws IOException if  invalid UTF-16 surrogate detected.
-     */
-    protected int writeUTF16Surrogate(final char high, final char low)
-        throws IOException
-    {
-        if (!Encodings.isLowUTF16Surrogate(low)) {
-            throw new IOException(
-                Utils.messages.createMessage(
-                    MsgKey.ER_INVALID_UTF16_SURROGATE,
-                    new Object[] {
-                        Integer.toHexString((int) high)
-                            + " "
-                            + Integer.toHexString(low)}));
-        }
-
-        final java.io.Writer writer = m_writer;
-        int codePoint = 0; // Nonzero iff written as NCR. REVIEW: Needed?
-                
-        // If we make it to here we have a valid high, low surrogate pair
-        if (m_encodingInfo.isInEncoding(high,low)) {
-            // If the character formed by the surrogate pair
-            // is in the encoding, so just write it out
-	    // NOTE: Assumes same buffer
-            writer.write(high);
-            writer.write(low);
-        }
-        else {
-            // Don't know what to do with this char, it is
-            // not in the encoding and not a high char in
-            // a surrogate pair, so write out as a numeric char ref
-            final String encoding = getEncoding();
-            if (encoding != null) {
-                /* The output encoding is known but does not include
-                 * this character. Fallback: Write as NCR
-                 */
-                codePoint = Encodings.toCodePoint(high, low);
-                writer.write('&');
-                writer.write('#');
-                writer.write(Integer.toString(codePoint));
-                writer.write(';');
-            } else {
-                /* The output encoding is not known, so presume
-                 * Unicode and just write it out. This handles the
-                 * case of serializing to a character buffer.
-                 */
-		writer.write(high);
-		writer.write(low);
-            }
-        }
-
-	// ToTextStream tests this and issues an error message (but
-	// not exception) if the not-in-encoding case arises,
-	// outputting an NCR in passing. 
-        return codePoint;
-    }
-
-    /**
-     * Handle one of the default entities, return false if it
-     * is not a default entity.
-     *
-     * @param ch character to be escaped.
-     * @param i index into character array.
-     * @param chars non-null reference to character array.
-     * @param len length of chars.
-     * @param fromTextNode true if the characters being processed
-     * are from a text node, false if they are from an attribute value
-     * @param escLF true if the linefeed should be escaped.
-     *
-     * @return i+1 if the character was written, else i.
-     *
-     * @throws java.io.IOException if error arose while writing
-     */
-    int accumDefaultEntity(
-        java.io.Writer writer,
-        char ch,
-        int i,
-        char[] chars,
-        int len,
-        boolean fromTextNode,
-        boolean escLF)
-        throws IOException
-    {
-
-        if (!escLF && CharInfo.S_LINEFEED == ch)
-        {
-            writer.write(m_lineSep, 0, m_lineSepLen);
+          if (m_shouldFlush)
+            writer.flush();
         }
         else
         {
-            // if this is text node character and a special one of those,
-            // or if this is a character from attribute value and a special one of those
-            if ((fromTextNode && m_charInfo.shouldMapTextChar(ch)) || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))
-            {
-                String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
-
-                if (null != outputStringForChar)
-                {
-                    writer.write(outputStringForChar);
-                }
-                else
-                    return i;
-            }
-            else
-                return i;
+          // Flush always. 
+          // Not a great thing if the writer was created 
+          // by this class, but don't have a choice.
+          writer.flush();
         }
-
-        return i + 1;
-
-    }
-    /**
-     * Normalize the characters, but don't escape.
-     *
-     * @param ch The characters from the XML document.
-     * @param start The start position in the array.
-     * @param length The number of characters to read from the array.
-     * @param isCData true if a CDATA block should be built around the characters.
-     * @param useSystemLineSeparator true if the operating systems 
-     * end-of-line separator should be output rather than a new-line character.
-     *
-     * @throws IOException if unable to write
-     * @throws org.xml.sax.SAXException never (I think), but signature expects it
-     */
-    void writeNormalizedChars(
-        char ch[],
-        int start,
-        int length,
-        boolean isCData,
-        boolean useSystemLineSeparator)
-        throws IOException, org.xml.sax.SAXException
-    {
-        final java.io.Writer writer = m_writer;
-        int end = start + length;
-
-        for (int i = start; i < end; i++)
-        {
-            char c = ch[i];
-
-            if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
-            {
-                writer.write(m_lineSep, 0, m_lineSepLen);
-            }
-            else if (isCData && (!escapingNotNeeded(c)))
-            {
-                //                if (i != 0)
-                if (m_cdataTagOpen)
-                    closeCDATA();
-
-                // This needs to go into a function... 
-                if (Encodings.isHighUTF16Surrogate(c))
-                {
-                    writeUTF16Surrogate(c, ch, i, end);
-                    i++ ; // process two input characters
+      }
+      catch (IOException ioe)
+      {
+        throw new org.xml.sax.SAXException(ioe);
+      }
+    }
+  }
+
+  OutputStream m_outputStream;
+  /**
+   * Get the output stream where the events will be serialized to.
+   *
+   * @return reference to the result stream, or null of only a writer was
+   * set.
+   */
+  public OutputStream getOutputStream()
+  {
+    return m_outputStream;
+  }
+
+  // Implement DeclHandler
+
+  /**
+   *   Report an element type declaration.
+   *  
+   *   <p>The content model will consist of the string "EMPTY", the
+   *   string "ANY", or a parenthesised group, optionally followed
+   *   by an occurrence indicator.  The model will be normalized so
+   *   that all whitespace is removed,and will include the enclosing
+   *   parentheses.</p>
+   *  
+   *   @param name The element type name.
+   *   @param model The content model as a normalized string.
+   *   @exception SAXException The application may raise an exception.
+   */
+  public void elementDecl(String name, String model) throws SAXException
+  {
+    // Do not inline external DTD
+    if (m_inExternalDTD)
+      return;
+    try
+    {
+      final java.io.Writer writer = m_writer;
+      DTDprolog();
+
+      writer.write("<!ELEMENT ");
+      writer.write(name);
+      writer.write(' ');
+      writer.write(model);
+      writer.write('>');
+      writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+  }
+
+  /**
+   * Report an internal entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param value The replacement text of the entity.
+   * @exception SAXException The application may raise an exception.
+   * @see #externalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void internalEntityDecl(String name, String value)
+      throws SAXException
+  {
+    // Do not inline external DTD
+    if (m_inExternalDTD)
+      return;
+    try
+    {
+      DTDprolog();
+      outputEntityDecl(name, value);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+  }
+
+  /**
+   * Output the doc type declaration.
+   *
+   * @param name non-null reference to document type name.
+   * NEEDSDOC @param value
+   *
+   * @throws IOException if an error occurs during writing.
+   */
+  void outputEntityDecl(String name, String value) throws IOException
+  {
+    final java.io.Writer writer = m_writer;
+    writer.write("<!ENTITY ");
+    writer.write(name);
+    writer.write(" \"");
+    writer.write(value);
+    writer.write("\">");
+    writer.write(m_lineSep, 0, m_lineSepLen);
+  }
+
+  /**
+   * Output a system-dependent line break.
+   *
+   * @throws IOException if an error occurs during writing.
+
+   */
+  protected final void outputLineSep() throws IOException
+  {
+    m_writer.write(m_lineSep, 0, m_lineSepLen);
+  }
+
+  void setProp(String name, String val, boolean defaultVal) {
+    if (val != null) {
+
+
+      char first = getFirstCharLocName(name);
+      switch (first) {
+      case 'c':
+        if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name)) {
+          String cdataSectionNames = val;
+          addCdataSectionElements(cdataSectionNames);
+        }
+        break;
+      case 'd':
+        if (OutputKeys.DOCTYPE_SYSTEM.equals(name)) {
+          this.m_doctypeSystem = val;
+        } else if (OutputKeys.DOCTYPE_PUBLIC.equals(name)) {
+          this.m_doctypePublic = val;
+          if (val.startsWith("-//W3C//DTD XHTML"))
+            m_spaceBeforeClose = true;
+        }
+        break;
+      case 'e':
+        String newEncoding = val;
+        if (OutputKeys.ENCODING.equals(name)) {
+          String possible_encoding = Encodings.getMimeEncoding(val);
+          if (possible_encoding != null) {
+            // if the encoding is being set, try to get the
+            // preferred
+            // mime-name and set it too.
+            super.setProp("mime-name", possible_encoding,
+                defaultVal);
+          }
+          final String oldExplicitEncoding = getOutputPropertyNonDefault(OutputKeys.ENCODING);
+          final String oldDefaultEncoding  = getOutputPropertyDefault(OutputKeys.ENCODING);
+          if ( (defaultVal && ( oldDefaultEncoding == null || !oldDefaultEncoding.equalsIgnoreCase(newEncoding)))
+              || ( !defaultVal && (oldExplicitEncoding == null || !oldExplicitEncoding.equalsIgnoreCase(newEncoding) ))) {
+            // We are trying to change the default or the non-default setting of the encoding to a different value
+            // from what it was
+
+            EncodingInfo encodingInfo = Encodings.getEncodingInfo(newEncoding);
+            if (newEncoding != null && encodingInfo.name == null) {
+              // We tried to get an EncodingInfo for Object for the given
+              // encoding, but it came back with an internall null name
+              // so the encoding is not supported by the JDK, issue a message.
+              final String msg = Utils.messages.createMessage(
+                  MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ newEncoding });
+
+              final String msg2 = 
+                  "Warning: encoding \"" + newEncoding + "\" not supported, using "
+                      + Encodings.DEFAULT_MIME_ENCODING;
+              try {
+                // Prepare to issue the warning message
+                final Transformer tran = super.getTransformer();
+                if (tran != null) {
+                  final ErrorListener errHandler = tran
+                      .getErrorListener();
+                  // Issue the warning message
+                  if (null != errHandler
+                      && m_sourceLocator != null) {
+                    errHandler
+                    .warning(new TransformerException(
+                        msg, m_sourceLocator));
+                    errHandler
+                    .warning(new TransformerException(
+                        msg2, m_sourceLocator));
+                  } else {
+                    System.out.println(msg);
+                    System.out.println(msg2);
+                  }
+                } else {
+                  System.out.println(msg);
+                  System.out.println(msg2);
                 }
-                else
-                {
-                    writer.write("&#");
+              } catch (Exception e) {
+              }
 
-                    String intStr = Integer.toString((int) c);
+              // We said we are using UTF-8, so use it
+              newEncoding = Encodings.DEFAULT_MIME_ENCODING;
+              val = Encodings.DEFAULT_MIME_ENCODING; // to store the modified value into the properties a little later
+              encodingInfo = Encodings.getEncodingInfo(newEncoding);
 
-                    writer.write(intStr);
-                    writer.write(';');
+            } 
+            // The encoding was good, or was forced to UTF-8 above
+
+
+            // If there is already a non-default set encoding and we 
+            // are trying to set the default encoding, skip the this block
+            // as the non-default value is already the one to use.
+            if (defaultVal == false || oldExplicitEncoding == null) {
+              m_encodingInfo = encodingInfo;
+              if (newEncoding != null)
+                m_isUTF8 = newEncoding.equals(Encodings.DEFAULT_MIME_ENCODING);
+
+              // if there was a previously set OutputStream
+              OutputStream os = getOutputStream();
+              if (os != null) {
+                Writer w = getWriter();
+
+                // If the writer was previously set, but
+                // set by the user, or if the new encoding is the same
+                // as the old encoding, skip this block
+                String oldEncoding = getOutputProperty(OutputKeys.ENCODING);
+                if ((w == null || !m_writer_set_by_user) 
+                    && !newEncoding.equalsIgnoreCase(oldEncoding)) {
+                  // Make the change of encoding in our internal
+                  // table, then call setOutputStreamInternal
+                  // which will stomp on the old Writer (if any)
+                  // with a new Writer with the new encoding.
+                  super.setProp(name, val, defaultVal);
+                  setOutputStreamInternal(os,false);
                 }
-
-                //                if ((i != 0) && (i < (end - 1)))
-                //                if (!m_cdataTagOpen && (i < (end - 1)))
-                //                {
-                //                    writer.write(CDATA_DELIMITER_OPEN);
-                //                    m_cdataTagOpen = true;
-                //                }
-            }
-            else if (
-                isCData
-                    && ((i < (end - 2))
-                        && (']' == c)
-                        && (']' == ch[i + 1])
-                        && ('>' == ch[i + 2])))
-            {
-                writer.write(CDATA_CONTINUE);
-
-                i += 2;
+              }
             }
-            else
-            {
-                if (escapingNotNeeded(c))
-                {
-                    if (isCData && !m_cdataTagOpen)
-                    {
-                        writer.write(CDATA_DELIMITER_OPEN);
-                        m_cdataTagOpen = true;
-                    }
-                    writer.write(c);
-                }
+          }
+        }
+        break;
+      case 'i':
+        if (OutputPropertiesFactory.S_KEY_INDENT_AMOUNT.equals(name)) {
+          setIndentAmount(Integer.parseInt(val));
+        } else if (OutputKeys.INDENT.equals(name)) {
+          boolean b = "yes".equals(val) ? true : false;
+          m_doIndent = b;
+        }
+
+        break;
+      case 'l':
+        if (OutputPropertiesFactory.S_KEY_LINE_SEPARATOR.equals(name)) {
+          m_lineSep = val.toCharArray();
+          m_lineSepLen = m_lineSep.length;
+        }
+
+        break;
+      case 'm':
+        if (OutputKeys.MEDIA_TYPE.equals(name)) {
+          m_mediatype = val;
+        }
+        break;
+      case 'o':
+        if (OutputKeys.OMIT_XML_DECLARATION.equals(name)) {
+          boolean b = "yes".equals(val) ? true : false;
+          this.m_shouldNotWriteXMLHeader = b;
+        }
+        break;
+      case 's':
+        // if standalone was explicitly specified
+        if (OutputKeys.STANDALONE.equals(name)) {
+          if (defaultVal) {
+            setStandaloneInternal(val);
+          } else {
+            m_standaloneWasSpecified = true;
+            setStandaloneInternal(val);
+          }
+        }
+
+        break;
+      case 'v':
+        if (OutputKeys.VERSION.equals(name)) {
+          m_version = val;
+        }
+        break;
+      default:
+        break;
+
+      } 
+      super.setProp(name, val, defaultVal);
+    }
+  }
+  /**
+   * Specifies an output format for this serializer. It the
+   * serializer has already been associated with an output format,
+   * it will switch to the new format. This method should not be
+   * called while the serializer is in the process of serializing
+   * a document.
+   *
+   * @param format The output format to use
+   */
+  public void setOutputFormat(Properties format)
+  {
+
+    boolean shouldFlush = m_shouldFlush;
+
+    if (format != null)
+    {
+      // Set the default values first,
+      // and the non-default values after that, 
+      // just in case there is some unexpected
+      // residual values left over from over-ridden default values
+      Enumeration propNames;
+      propNames = format.propertyNames();
+      while (propNames.hasMoreElements())
+      {
+        String key = (String) propNames.nextElement();
+        // Get the value, possibly a default value
+        String value = format.getProperty(key);
+        // Get the non-default value (if any).
+        String explicitValue = (String) format.get(key);
+        if (explicitValue == null && value != null) {
+          // This is a default value
+          this.setOutputPropertyDefault(key,value);
+        }
+        if (explicitValue != null) {
+          // This is an explicit non-default value
+          this.setOutputProperty(key,explicitValue);
+        }
+      } 
+    }   
 
-                // This needs to go into a function... 
-                else if (Encodings.isHighUTF16Surrogate(c))
-                {
-                    if (m_cdataTagOpen)
-                        closeCDATA();
-                    writeUTF16Surrogate(c, ch, i, end);
-                    i++; // process two input characters
-                }
-                else
-                {
-                    if (m_cdataTagOpen)
-                        closeCDATA();
-                    writer.write("&#");
+    // Access this only from the Hashtable level... we don't want to 
+    // get default properties.
+    String entitiesFileName =
+        (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
 
-                    String intStr = Integer.toString((int) c);
+    if (null != entitiesFileName)
+    {
 
-                    writer.write(intStr);
-                    writer.write(';');
-                }
-            }
-        }
+      String method = 
+          (String) format.get(OutputKeys.METHOD);
 
+      m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
     }
 
-    /**
-     * Ends an un-escaping section.
-     *
-     * @see #startNonEscaping
-     *
-     * @throws org.xml.sax.SAXException never, but signature expects it
-     */
-    public void endNonEscaping() throws org.xml.sax.SAXException
-    {
-        m_disableOutputEscapingStates.pop();
-    }
 
-    /**
-     * Starts an un-escaping section. All characters printed within an un-
-     * escaping section are printed as is, without escaping special characters
-     * into entity references. Only XML and HTML serializers need to support
-     * this method.
-     * <p> The contents of the un-escaping section will be delivered through the
-     * regular <tt>characters</tt> event.
-     *
-     * @throws org.xml.sax.SAXException never, but signature expects it
-     */
-    public void startNonEscaping() throws org.xml.sax.SAXException
+
+
+    m_shouldFlush = shouldFlush;
+  }
+
+  /**
+   * Returns the output format for this serializer.
+   *
+   * @return The output format in use
+   */
+  public Properties getOutputFormat() {
+    Properties def = new Properties();
     {
-        m_disableOutputEscapingStates.push(true);
+      Set s = getOutputPropDefaultKeys();
+      Iterator i = s.iterator();
+      while (i.hasNext()) {
+        String key = (String) i.next();
+        String val = getOutputPropertyDefault(key);
+        def.put(key, val);
+      }
     }
 
-    /**
-     * Receive notification of cdata.
-     *
-     * <p>The Parser will call this method to report each chunk of
-     * character data.  SAX parsers may return all contiguous character
-     * data in a single chunk, or they may split it into several
-     * chunks; however, all of the characters in any single event
-     * must come from the same external entity, so that the Locator
-     * provides useful information.</p>
-     *
-     * <p>The application must not attempt to read from the array
-     * outside of the specified range.</p>
-     *
-     * <p>Note that some parsers will report whitespace using the
-     * ignorableWhitespace() method rather than this one (validating
-     * parsers must do so).</p>
-     *
-     * @param ch The characters from the XML document.
-     * @param start The start position in the array.
-     * @param length The number of characters to read from the array.
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *            wrapping another exception.
-     * @see #ignorableWhitespace
-     * @see org.xml.sax.Locator
-     */
-    protected void cdata(char ch[], int start, final int length)
-        throws org.xml.sax.SAXException
+    Properties props = new Properties(def);
     {
+      Set s = getOutputPropKeys();
+      Iterator i = s.iterator();
+      while (i.hasNext()) {
+        String key = (String) i.next();
+        String val = getOutputPropertyNonDefault(key);
+        if (val != null)
+          props.put(key, val);
+      }
+    }
+    return props;
+  }
+
+  /**
+   * Specifies a writer to which the document should be serialized.
+   * This method should not be called while the serializer is in
+   * the process of serializing a document.
+   *
+   * @param writer The output writer stream
+   */
+  public void setWriter(Writer writer)
+  {        
+    setWriterInternal(writer, true);
+  }
+
+  private boolean m_writer_set_by_user;
+  private void setWriterInternal(Writer writer, boolean setByUser) {
+
+    m_writer_set_by_user = setByUser;
+    m_writer = writer;
+    // if we are tracing events we need to trace what
+    // characters are written to the output writer.
+    if (m_tracer != null) {
+      boolean noTracerYet = true;
+      Writer w2 = m_writer;
+      while (w2 instanceof WriterChain) {
+        if (w2 instanceof SerializerTraceWriter) {
+          noTracerYet = false;
+          break;
+        }
+        w2 = ((WriterChain)w2).getWriter();
+      }
+      if (noTracerYet)
+        m_writer = new SerializerTraceWriter(m_writer, m_tracer);
+    }
+  }
+
+  /**
+   * Set if the operating systems end-of-line line separator should
+   * be used when serializing.  If set false NL character 
+   * (decimal 10) is left alone, otherwise the new-line will be replaced on
+   * output with the systems line separator. For example on UNIX this is
+   * NL, while on Windows it is two characters, CR NL, where CR is the
+   * carriage-return (decimal 13).
+   *  
+   * @param use_sytem_line_break True if an input NL is replaced with the 
+   * operating systems end-of-line separator.
+   * @return The previously set value of the serializer.
+   */
+  public boolean setLineSepUse(boolean use_sytem_line_break)
+  {
+    boolean oldValue = m_lineSepUse;
+    m_lineSepUse = use_sytem_line_break;
+    return oldValue;
+  }
+
+  /**
+   * Specifies an output stream to which the document should be
+   * serialized. This method should not be called while the
+   * serializer is in the process of serializing a document.
+   * <p>
+   * The encoding specified in the output properties is used, or
+   * if no encoding was specified, the default for the selected
+   * output method.
+   *
+   * @param output The output stream
+   */
+  public void setOutputStream(OutputStream output)
+  {
+    setOutputStreamInternal(output, true);
+  }
+
+  private void setOutputStreamInternal(OutputStream output, boolean setByUser)
+  {
+    m_outputStream = output;
+    String encoding = getOutputProperty(OutputKeys.ENCODING);        
+    if (Encodings.DEFAULT_MIME_ENCODING.equalsIgnoreCase(encoding))
+    {
+      // We wrap the OutputStream with a writer, but
+      // not one set by the user
+      setWriterInternal(new WriterToUTF8Buffered(output), false);
+    } else if (
+        "WINDOWS-1250".equals(encoding)
+        || "US-ASCII".equals(encoding)
+        || "ASCII".equals(encoding))
+    {
+      setWriterInternal(new WriterToASCI(output), false);
+    } else if (encoding != null) {
+      Writer osw = null;
+      try
+      {
+        osw = Encodings.getWriter(output, encoding);
+      }
+      catch (UnsupportedEncodingException uee)
+      {
+        osw = null;
+      }
 
-        try
-        {
-            final int old_start = start;
-            if (m_elemContext.m_startTagOpen)
-            {
-                closeStartTag();
-                m_elemContext.m_startTagOpen = false;
-            }
-            m_ispreserve = true;
-
-            if (shouldIndent())
-                indent();
 
-            boolean writeCDataBrackets =
-                (((length >= 1) && escapingNotNeeded(ch[start])));
+      if (osw == null) {
+        System.out.println(
+            "Warning: encoding \""
+                + encoding
+                + "\" not supported"
+                + ", using "
+                + Encodings.DEFAULT_MIME_ENCODING);
 
-            /* Write out the CDATA opening delimiter only if
-             * we are supposed to, and if we are not already in
-             * the middle of a CDATA section  
-             */
-            if (writeCDataBrackets && !m_cdataTagOpen)
-            {
-                m_writer.write(CDATA_DELIMITER_OPEN);
-                m_cdataTagOpen = true;
-            }
+        encoding = Encodings.DEFAULT_MIME_ENCODING;
+        setEncoding(encoding);
+        try {
+          osw = Encodings.getWriter(output, encoding);
+        } catch (UnsupportedEncodingException e) {
+          // We can't really get here, UTF-8 is always supported
+          // This try-catch exists to make the compiler happy
+          e.printStackTrace();
+        }
+      }
+      setWriterInternal(osw,false);
+    }
+    else {
+      // don't have any encoding, but we have an OutputStream
+      Writer osw = new OutputStreamWriter(output);
+      setWriterInternal(osw,false);
+    }
+  }
+
+  /**
+   * @see SerializationHandler#setEscaping(boolean)
+   */
+  public boolean setEscaping(boolean escape)
+  {
+    final boolean temp = m_escaping;
+    m_escaping = escape;
+    return temp;
+
+  }
+
+
+  /**
+   * Might print a newline character and the indentation amount
+   * of the given depth.
+   * 
+   * @param depth the indentation depth (element nesting depth)
+   *
+   * @throws IOException if an error occurs during writing.
+   */
+  protected void indent(int depth) throws IOException
+  {
+
+    if (m_startNewLine)
+      outputLineSep();
+    /* For m_indentAmount > 0 this extra test might be slower
+     * but Xalan's default value is 0, so this extra test
+     * will run faster in that situation.
+     */
+    if (m_indentAmount > 0)
+      printSpace(depth * m_indentAmount);
+
+  }
+
+  /**
+   * Indent at the current element nesting depth.
+   * @throws IOException if an error occurs while writing
+   */
+  protected void indent() throws IOException
+  {
+    indent(m_elemContext.m_currentElemDepth);
+  }
+  /**
+   * Prints <var>n</var> spaces.
+   * @param n         Number of spaces to print.
+   *
+   * @throws IOException if an error occurs while writing
+   */
+  private void printSpace(int n) throws IOException
+  {
+    final java.io.Writer writer = m_writer;
+    for (int i = 0; i < n; i++)
+    {
+      writer.write(' ');
+    }
+
+  }
+
+  /**
+   * Report an attribute type declaration.
+   *
+   * <p>Only the effective (first) declaration for an attribute will
+   * be reported.  The type will be one of the strings "CDATA",
+   * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
+   * "ENTITIES", or "NOTATION", or a parenthesized token group with
+   * the separator "|" and all whitespace removed.</p>
+   *
+   * @param eName The name of the associated element.
+   * @param aName The name of the attribute.
+   * @param type A string representing the attribute type.
+   * @param valueDefault A string representing the attribute default
+   *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
+   *        none of these applies.
+   * @param value A string representing the attribute's default value,
+   *        or null if there is none.
+   * @exception SAXException The application may raise an exception.
+   */
+  public void attributeDecl(
+      String eName,
+      String aName,
+      String type,
+      String valueDefault,
+      String value)
+          throws SAXException
+  {
+    // Do not inline external DTD
+    if (m_inExternalDTD)
+      return;
+    try
+    {
+      final java.io.Writer writer = m_writer;
+      DTDprolog();
+
+      writer.write("<!ATTLIST ");
+      writer.write(eName);
+      writer.write(' ');
+
+      writer.write(aName);
+      writer.write(' ');
+      writer.write(type);
+      if (valueDefault != null)
+      {
+        writer.write(' ');
+        writer.write(valueDefault);
+      }
 
-            // writer.write(ch, start, length);
-            if (isEscapingDisabled())
-            {
-                charactersRaw(ch, start, length);
-            }
-            else
-                writeNormalizedChars(ch, start, length, true, m_lineSepUse);
+      //writer.write(" ");
+      //writer.write(value);
+      writer.write('>');
+      writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+  }
+
+  /**
+   * Get the character stream where the events will be serialized to.
+   *
+   * @return Reference to the result Writer, or null.
+   */
+  public Writer getWriter()
+  {
+    return m_writer;
+  }
+
+  /**
+   * Report a parsed external entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param publicId The declared public identifier of the entity, or
+   *        null if none was declared.
+   * @param systemId The declared system identifier of the entity.
+   * @exception SAXException The application may raise an exception.
+   * @see #internalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void externalEntityDecl(
+      String name,
+      String publicId,
+      String systemId)
+          throws SAXException
+  {
+    try {
+      DTDprolog();
+
+      m_writer.write("<!ENTITY ");            
+      m_writer.write(name);
+      if (publicId != null) {
+        m_writer.write(" PUBLIC \"");
+        m_writer.write(publicId);
 
-            /* used to always write out CDATA closing delimiter here,
-             * but now we delay, so that we can merge CDATA sections on output.    
-             * need to write closing delimiter later
-             */
-            if (writeCDataBrackets)
-            {
-                /* if the CDATA section ends with ] don't leave it open
-                 * as there is a chance that an adjacent CDATA sections
-                 * starts with ]>.  
-                 * We don't want to merge ]] with > , or ] with ]> 
-                 */
-                if (ch[start + length - 1] == ']')
-                    closeCDATA();
-            }
+      }
+      else {
+        m_writer.write(" SYSTEM \"");
+        m_writer.write(systemId);
+      }
+      m_writer.write("\" >");
+      m_writer.write(m_lineSep, 0, m_lineSepLen);
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+
+  }
+
+  /**
+   * @return true iff this character can be written without escaping.
+   * @param ch character to be tested
+   */
+  protected boolean escapingNotNeeded(char ch)
+  {
+    final boolean ret;
+    if (ch < 127)
+    {
+      // This is the old/fast code here, but is this 
+      // correct for all encodings?
+      if (ch >= CharInfo.S_SPACE || (CharInfo.S_LINEFEED == ch || 
+          CharInfo.S_CARRIAGERETURN == ch || CharInfo.S_HORIZONAL_TAB == ch))
+        ret= true;
+      else
+        ret = false;
+    }
+    else {            
+      ret = m_encodingInfo.isInEncoding(ch);
+    }
+    return ret;
+  }
+
+  /**
+   * Once a surrogate has been detected, write out the pair of
+   * characters if it is in the encoding, or if there is no
+   * encoding, otherwise write out an entity reference
+   * of the value of the unicode code point of the character
+   * represented by the high/low surrogate pair.
+   * <p>
+   * An exception is thrown if there is no low surrogate in the pair,
+   * because the array ends unexpectely, or if the low char is there
+   * but its value is such that it is not a low surrogate.
+   *
+   * @param c the first (high) part of the surrogate, which
+   * must be confirmed before calling this method.
+   * @param ch Character array.
+   * @param i position Where the surrogate was detected.
+   * @param end The end index of the significant characters.
+   * @return 0 if the pair of characters was written out as-is,
+   * the unicode code point of the character represented by
+   * the surrogate pair if an entity reference with that value
+   * was written out. 
+   * 
+   * @throws IOException if  invalid UTF-16 surrogate detected.
+   */
+  protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
+      throws IOException
+  {
+    if (i + 1 >= end)
+    {
+      throw new IOException(
+          Utils.messages.createMessage(
+              MsgKey.ER_INVALID_UTF16_SURROGATE,
+              new Object[] { Integer.toHexString((int) c)}));
+    }
+
+    final char high = c;
+    final char low = ch[i+1];
+    return writeUTF16Surrogate(high,low);
+  }
+
+  /**
+   * Once a surrogate has been detected, write out the pair of
+   * characters if it is in the encoding, or if there is no
+   * encoding, otherwise write out an entity reference
+   * of the value of the unicode code point of the character
+   * represented by the high/low surrogate pair.
+   * <p>
+   * An exception is thrown if there is no low surrogate in the pair,
+   * because the array ends unexpectely, or if the low char is there
+   * but its value is such that it is not a low surrogate.
+   *
+   * @param high the first (high) part of the surrogate. Currently untested.
+   * @param low the second (low) part of the surrogate. TESTED.
+   * @return codePoint: 0 if the pair of characters was written out as-is, the
+   * unicode code point of the character represented by the
+   * surrogate pair if an entity reference with that value was
+   * written out. (ToText uses this information, I believe. TODO:
+   * REVIEW)
+   * @throws IOException if invalid UTF-16 surrogate detected. Currently
+   * only the low surrogate is checked since the high is presumed
+   * to have been confirmed during parsing. TODO: REVIEW.)
+   */
+  protected int writeUTF16Surrogate(char high, char low)
+      throws IOException
+  {
+    int codePoint = 0;
+
+    if (!Encodings.isLowUTF16Surrogate(low)) {
+      throw new IOException(
+          Utils.messages.createMessage(
+              MsgKey.ER_INVALID_UTF16_SURROGATE,
+              new Object[] {
+                  Integer.toHexString((int) high)
+                  + " "
+                  + Integer.toHexString(low)}));
+    }
+
+    final java.io.Writer writer = m_writer;
+
+    // If we make it to here we have a valid high, low surrogate pair
+    if (m_encodingInfo.isInEncoding(high,low)) {
+      // If the character formed by the surrogate pair
+      // is in the encoding, so just write it out.
+      // Note that writing a 2-character array is DIFFERENT from
+      // writing single chars, as far as encoding is concerned.
+      char[] surrogatePair={high,low};
+      writer.write(surrogatePair);
+    }
+    else {
+      // Don't know what to do with this char, it is
+      // not in the encoding and not a high char in
+      // a surrogate pair, so write out as an entity ref
+      final String encoding = getEncoding();
+      if (encoding != null) {
+        /* The output encoding is known, 
+         * so somthing is wrong.
+         */
+        codePoint = Encodings.toCodePoint(high, low);
+        // not in the encoding, so write out a character reference
+        writer.write('&');
+        writer.write('#');
+        writer.write(Integer.toString(codePoint));
+        writer.write(';');
+      } else {
+        /* The output encoding is not known,
+         * so just write it out as-is.
+         */
+        // Note that writing a 2-character array is DIFFERENT from
+        // writing single chars, as far as encoding is concerned.
+        char[] surrogatePair={high,low};
+        writer.write(surrogatePair);
+      }
+    }
+    // non-zero only if character reference was written out.
+    return codePoint;
+  }
+
+  /**
+   * Handle one of the default entities, return false if it
+   * is not a default entity.
+   *
+   * @param ch character to be escaped.
+   * @param i index into character array.
+   * @param chars non-null reference to character array.
+   * @param len length of chars.
+   * @param fromTextNode true if the characters being processed
+   * are from a text node, false if they are from an attribute value
+   * @param escLF true if the linefeed should be escaped.
+   *
+   * @return i+1 if the character was written, else i.
+   *
+   * @throws java.io.IOException if error arose while writing
+   */
+  int accumDefaultEntity(
+      java.io.Writer writer,
+      char ch,
+      int i,
+      char[] chars,
+      int len,
+      boolean fromTextNode,
+      boolean escLF)
+          throws IOException
+  {
+
+    if (!escLF && CharInfo.S_LINEFEED == ch)
+    {
+      writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+    else
+    {
+      // if this is text node character and a special one of those,
+      // or if this is a character from attribute value and a special one of those
+      if ((fromTextNode && m_charInfo.shouldMapTextChar(ch)) || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))
+      {
+        String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
 
-            // time to fire off CDATA event
-            if (m_tracer != null)
-                super.fireCDATAEvent(ch, old_start, length);
-        }
-        catch (IOException ioe)
+        if (null != outputStringForChar)
         {
-            throw new org.xml.sax.SAXException(
-                Utils.messages.createMessage(
-                    MsgKey.ER_OIERROR,
-                    null),
-                ioe);
-            //"IO error", ioe);
+          writer.write(outputStringForChar);
         }
+        else
+          return i;
+      }
+      else
+        return i;
     }
 
-    /**
-     * Tell if the character escaping should be disabled for the current state.
-     *
-     * @return true if the character escaping should be disabled.
-     */
-    private boolean isEscapingDisabled()
-    {
-        return m_disableOutputEscapingStates.peekOrFalse();
-    }
-
-    /**
-     * If available, when the disable-output-escaping attribute is used,
-     * output raw text without escaping.
-     *
-     * @param ch The characters from the XML document.
-     * @param start The start position in the array.
-     * @param length The number of characters to read from the array.
-     *
-     * @throws org.xml.sax.SAXException containing IOException, if one arises
-     */
-    protected void charactersRaw(char ch[], int start, int length)
-        throws org.xml.sax.SAXException
-    {
+    return i + 1;
+
+  }
+  /**
+   * Normalize the characters, but don't escape.
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @param isCData true if a CDATA block should be built around the characters.
+   * @param useSystemLineSeparator true if the operating systems 
+   * end-of-line separator should be output rather than a new-line character.
+   *
+   * @throws IOException if unable to write
+   * @throws org.xml.sax.SAXException never (I think), but signature expects it
+   */
+  void writeNormalizedChars(
+      char ch[],
+      int start,
+      int length,
+      boolean isCData,
+      boolean useSystemLineSeparator)
+          throws IOException, org.xml.sax.SAXException
+  {
+    final java.io.Writer writer = m_writer;
+    int end = start + length;
+
+    for (int i = start; i < end; i++)
+    {
+      char c = ch[i];
+
+      if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
+      {
+        writer.write(m_lineSep, 0, m_lineSepLen);
+      }
+      else if (isCData && (!escapingNotNeeded(c)))
+      {
+        //                if (i != 0)
+        if (m_cdataTagOpen)
+          closeCDATA();
 
-        if (m_inEntityRef)
-            return;
-        try
+        // This needs to go into a function... 
+        if (Encodings.isHighUTF16Surrogate(c))
         {
-            if (m_elemContext.m_startTagOpen)
-            {
-                closeStartTag();
-                m_elemContext.m_startTagOpen = false;
-            }
-
-            m_ispreserve = true;
-
-            m_writer.write(ch, start, length);
+          writeUTF16Surrogate(c, ch, i, end);
+          i++ ; // process two input characters
         }
-        catch (IOException e)
+        else
         {
-            throw new SAXException(e);
+          writer.write("&#");
+
+          String intStr = Integer.toString((int) c);
+
+          writer.write(intStr);
+          writer.write(';');
         }
 
-    }
+        //                if ((i != 0) && (i < (end - 1)))
+        //                if (!m_cdataTagOpen && (i < (end - 1)))
+        //                {
+        //                    writer.write(CDATA_DELIMITER_OPEN);
+        //                    m_cdataTagOpen = true;
+        //                }
+      }
+      else if (
+          isCData
+          && ((i < (end - 2))
+              && (']' == c)
+              && (']' == ch[i + 1])
+              && ('>' == ch[i + 2])))
+      {
+        writer.write(CDATA_CONTINUE);
 
-    /**
-     * Receive notification of character data.
-     *
-     * <p>The Parser will call this method to report each chunk of
-     * character data.  SAX parsers may return all contiguous character
-     * data in a single chunk, or they may split it into several
-     * chunks; however, all of the characters in any single event
-     * must come from the same external entity, so that the Locator
-     * provides useful information.</p>
-     *
-     * <p>The application must not attempt to read from the array
-     * outside of the specified range.</p>
-     *
-     * <p>Note that some parsers will report whitespace using the
-     * ignorableWhitespace() method rather than this one (validating
-     * parsers must do so).</p>
-     *
-     * @param chars The characters from the XML document.
-     * @param start The start position in the array.
-     * @param length The number of characters to read from the array.
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *            wrapping another exception.
-     * @see #ignorableWhitespace
-     * @see org.xml.sax.Locator
-     */
-    public void characters(final char chars[], final int start, final int length)
-        throws org.xml.sax.SAXException
-    {
-        // It does not make sense to continue with rest of the method if the number of 
-        // characters to read from array is 0.
-        // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
-        // is created if string is empty.	
-        if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
-            return;
-            
-        m_docIsEmpty = false;
-        
-        if (m_elemContext.m_startTagOpen)
+        i += 2;
+      }
+      else
+      {
+        if (escapingNotNeeded(c))
         {
-            closeStartTag();
-            m_elemContext.m_startTagOpen = false;
+          if (isCData && !m_cdataTagOpen)
+          {
+            writer.write(CDATA_DELIMITER_OPEN);
+            m_cdataTagOpen = true;
+          }
+          writer.write(c);
         }
-        else if (m_needToCallStartDocument)
+
+        // This needs to go into a function... 
+        else if (Encodings.isHighUTF16Surrogate(c))
         {
-            startDocumentInternal();
+          if (m_cdataTagOpen)
+            closeCDATA();
+          writeUTF16Surrogate(c, ch, i, end);
+          i++; // process two input characters
         }
-
-        if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
+        else
         {
-            /* either due to startCDATA() being called or due to 
-             * cdata-section-elements atribute, we need this as cdata
-             */
-            cdata(chars, start, length);
+          if (m_cdataTagOpen)
+            closeCDATA();
+          writer.write("&#");
+
+          String intStr = Integer.toString((int) c);
 
-            return;
+          writer.write(intStr);
+          writer.write(';');
         }
+      }
+    }
 
-        if (m_cdataTagOpen)
-            closeCDATA();
-        
-        if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
-        {
-            charactersRaw(chars, start, length);
+  }
+
+  /**
+   * Ends an un-escaping section.
+   *
+   * @see #startNonEscaping
+   *
+   * @throws org.xml.sax.SAXException never, but signature expects it
+   */
+  public void endNonEscaping() throws org.xml.sax.SAXException
+  {
+    m_disableOutputEscapingStates.pop();
+  }
+
+  /**
+   * Starts an un-escaping section. All characters printed within an un-
+   * escaping section are printed as is, without escaping special characters
+   * into entity references. Only XML and HTML serializers need to support
+   * this method.
+   * <p> The contents of the un-escaping section will be delivered through the
+   * regular <tt>characters</tt> event.
+   *
+   * @throws org.xml.sax.SAXException never, but signature expects it
+   */
+  public void startNonEscaping() throws org.xml.sax.SAXException
+  {
+    m_disableOutputEscapingStates.push(true);
+  }
+
+  /**
+   * Receive notification of cdata.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  protected void cdata(char ch[], int start, final int length)
+      throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      final int old_start = start;
+      if (m_elemContext.m_startTagOpen)
+      {
+        closeStartTag();
+        m_elemContext.m_startTagOpen = false;
+      }
+      m_ispreserve = true;
 
-            // time to fire off characters generation event
-            if (m_tracer != null)
-                super.fireCharEvent(chars, start, length);
+      if (shouldIndent())
+        indent();
 
-            return;
-        }
+      boolean writeCDataBrackets =
+          (((length >= 1) && escapingNotNeeded(ch[start])));
 
-        if (m_elemContext.m_startTagOpen)
-        {
-            closeStartTag();
-            m_elemContext.m_startTagOpen = false;
-        }
+      /* Write out the CDATA opening delimiter only if
+       * we are supposed to, and if we are not already in
+       * the middle of a CDATA section  
+       */
+      if (writeCDataBrackets && !m_cdataTagOpen)
+      {
+        m_writer.write(CDATA_DELIMITER_OPEN);
+        m_cdataTagOpen = true;
+      }
 
-        
-        try
-        {
-            int i;
-            int startClean;
-            
-            // skip any leading whitspace 
-            // don't go off the end and use a hand inlined version
-            // of isWhitespace(ch)
-            final int end = start + length;
-            int lastDirtyCharProcessed = start - 1; // last non-clean character that was processed
-													// that was processed
-            final Writer writer = m_writer;
-            boolean isAllWhitespace = true;
-
-            // process any leading whitspace
-            i = start;
-            while (i < end && isAllWhitespace) {
-                char ch1 = chars[i];
-
-                if (m_charInfo.shouldMapTextChar(ch1)) {
-                    // The character is supposed to be replaced by a String
-                    // so write out the clean whitespace characters accumulated
-                    // so far
-                    // then the String.
-                    writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                    String outputStringForChar = m_charInfo
-                            .getOutputStringForChar(ch1);
-                    writer.write(outputStringForChar);
-                    // We can't say that everything we are writing out is
-                    // all whitespace, we just wrote out a String.
-                    isAllWhitespace = false;
-                    lastDirtyCharProcessed = i; // mark the last non-clean
-                    // character processed
-                    i++;
-                } else {
-                    // The character is clean, but is it a whitespace ?
-                    switch (ch1) {
-                    // TODO: Any other whitespace to consider?
-                    case CharInfo.S_SPACE:
-                        // Just accumulate the clean whitespace
-                        i++;
-                        break;
-                    case CharInfo.S_LINEFEED:
-                        lastDirtyCharProcessed = processLineFeed(chars, i,
-                                lastDirtyCharProcessed, writer);
-                        i++;
-                        break;
-                    case CharInfo.S_CARRIAGERETURN:
-                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                        writer.write("&#13;");
-                        lastDirtyCharProcessed = i;
-                        i++;
-                        break;
-                    case CharInfo.S_HORIZONAL_TAB:
-                        // Just accumulate the clean whitespace
-                        i++;
-                        break;
-                    default:
-                        // The character was clean, but not a whitespace
-                        // so break the loop to continue with this character
-                        // (we don't increment index i !!)
-                        isAllWhitespace = false;
-                        break;
-                    }
-                }
-            }
+      // writer.write(ch, start, length);
+      if (isEscapingDisabled())
+      {
+        charactersRaw(ch, start, length);
+      }
+      else
+        writeNormalizedChars(ch, start, length, true, m_lineSepUse);
 
-            /* If there is some non-whitespace, mark that we may need
-             * to preserve this. This is only important if we have indentation on.
-             */            
-            if (i < end || !isAllWhitespace) 
-                m_ispreserve = true;
-            
-            
-            for (; i < end; i++)
-            {
-                char ch = chars[i];
-                
-                if (m_charInfo.shouldMapTextChar(ch)) {
-                    // The character is supposed to be replaced by a String
-                    // e.g.   '&'  -->  "&amp;"
-                    // e.g.   '<'  -->  "&lt;"
-                    writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                    String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
-                    writer.write(outputStringForChar);
-                    lastDirtyCharProcessed = i;
-                }
-                else {
-                    if (ch <= 0x1F) {
-                        // Range 0x00 through 0x1F inclusive
-                        //
-                        // This covers the non-whitespace control characters
-                        // in the range 0x1 to 0x1F inclusive.
-                        // It also covers the whitespace control characters in the same way:
-                        // 0x9   TAB
-                        // 0xA   NEW LINE
-                        // 0xD   CARRIAGE RETURN
-                        //
-                        // We also cover 0x0 ... It isn't valid
-                        // but we will output "&#0;" 
-                        
-                        // The default will handle this just fine, but this
-                        // is a little performance boost to handle the more
-                        // common TAB, NEW-LINE, CARRIAGE-RETURN
-                        switch (ch) {
-
-                        case CharInfo.S_HORIZONAL_TAB:
-                            // Leave whitespace TAB as a real character
-                            break;
-                        case CharInfo.S_LINEFEED:
-                            lastDirtyCharProcessed = processLineFeed(chars, i, lastDirtyCharProcessed, writer);
-                            break;
-                        case CharInfo.S_CARRIAGERETURN:
-                        	writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                        	writer.write("&#13;");
-                        	lastDirtyCharProcessed = i;
-                            // Leave whitespace carriage return as a real character
-                            break;
-                        default:
-                            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                            writer.write("&#");
-                            writer.write(Integer.toString(ch));
-                            writer.write(';');
-                            lastDirtyCharProcessed = i;
-                            break;
-
-                        }
-                    }
-                    else if (ch < 0x7F) {  
-                        // Range 0x20 through 0x7E inclusive
-                        // Normal ASCII chars, do nothing, just add it to
-                        // the clean characters
-                            
-                    }
-                    else if (ch <= 0x9F){
-                        // Range 0x7F through 0x9F inclusive
-                        // More control characters, including NEL (0x85)
-                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                        writer.write("&#");
-                        writer.write(Integer.toString(ch));
-                        writer.write(';');
-                        lastDirtyCharProcessed = i;
-                    }
-                    else if (ch == CharInfo.S_LINE_SEPARATOR) {
-                        // LINE SEPARATOR
-                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                        writer.write("&#8232;");
-                        lastDirtyCharProcessed = i;
-                    }
-                    else if (m_encodingInfo.isInEncoding(ch)) {
-                        // If the character is in the encoding, and
-                        // not in the normal ASCII range, we also
-                        // just leave it get added on to the clean characters
-                    }
-                    else if (Encodings.isHighUTF16Surrogate(ch) && i < end-1 && Encodings.isLowUTF16Surrogate(chars[i+1])) {
-                    	// So, this is a (valid) surrogate pair
-                    	if (! m_encodingInfo.isInEncoding(ch, chars[i+1])) {
-                    		int codepoint = Encodings.toCodePoint(ch, chars[i+1]);
-                    		writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                    		writer.write("&#");
-                    		writer.write(Integer.toString(codepoint));
-                    		writer.write(';');
-                    		lastDirtyCharProcessed = i+1;
-                    	}
-                    	i++; // skip the low surrogate, too
-                    }
-                	else {
-                        // This is a fallback plan, we get here if the
-                    	// encoding doesn't contain ch and it's not part
-                    	// of a surrogate pair
-                        // The right thing is to write out an entity
-                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
-                        writer.write("&#");
-                        writer.write(Integer.toString(ch));
-                        writer.write(';');
-                        lastDirtyCharProcessed = i;
-                    }
-                }
-            }
-            
-            // we've reached the end. Any clean characters at the
-            // end of the array than need to be written out?
-            startClean = lastDirtyCharProcessed + 1;
-            if (i > startClean)
-            {
-                int lengthClean = i - startClean;
-                m_writer.write(chars, startClean, lengthClean);
-            }
+      /* used to always write out CDATA closing delimiter here,
+       * but now we delay, so that we can merge CDATA sections on output.    
+       * need to write closing delimiter later
+       */
+      if (writeCDataBrackets)
+      {
+        /* if the CDATA section ends with ] don't leave it open
+         * as there is a chance that an adjacent CDATA sections
+         * starts with ]>.  
+         * We don't want to merge ]] with > , or ] with ]> 
+         */
+        if (ch[start + length - 1] == ']')
+          closeCDATA();
+      }
 
-            // For indentation purposes, mark that we've just writen text out
-            m_isprevtext = true;
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
+      // time to fire off CDATA event
+      if (m_tracer != null)
+        super.fireCDATAEvent(ch, old_start, length);
+    }
+    catch (IOException ioe)
+    {
+      throw new org.xml.sax.SAXException(
+          Utils.messages.createMessage(
+              MsgKey.ER_OIERROR,
+              null),
+          ioe);
+      //"IO error", ioe);
+    }
+  }
+
+  /**
+   * Tell if the character escaping should be disabled for the current state.
+   *
+   * @return true if the character escaping should be disabled.
+   */
+  private boolean isEscapingDisabled()
+  {
+    return m_disableOutputEscapingStates.peekOrFalse();
+  }
+
+  /**
+   * If available, when the disable-output-escaping attribute is used,
+   * output raw text without escaping.
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @throws org.xml.sax.SAXException containing IOException, if one arises
+   */
+  protected void charactersRaw(char ch[], int start, int length)
+      throws org.xml.sax.SAXException
+  {
+
+    if (m_inEntityRef)
+      return;
+    try
+    {
+      if (m_elemContext.m_startTagOpen)
+      {
+        closeStartTag();
+        m_elemContext.m_startTagOpen = false;
+      }
 
-        // time to fire off characters generation event
-        if (m_tracer != null)
-            super.fireCharEvent(chars, start, length);
-    }
-
-	private int processLineFeed(final char[] chars, int i, int lastProcessed, final Writer writer) throws IOException {
-		if (!m_lineSepUse 
-		|| (m_lineSepLen ==1 && m_lineSep[0] == CharInfo.S_LINEFEED)){
-		    // We are leaving the new-line alone, and it is just
-		    // being added to the 'clean' characters,
-			// so the last dirty character processed remains unchanged
-		}
-		else {
-		    writeOutCleanChars(chars, i, lastProcessed);
-		    writer.write(m_lineSep, 0, m_lineSepLen);
-		    lastProcessed = i;
-		}
-		return lastProcessed;
-	}
-
-    private void writeOutCleanChars(final char[] chars, int i, int lastProcessed) throws IOException {
-        int startClean;
-        startClean = lastProcessed + 1;
-        if (startClean < i)
-        {
-            int lengthClean = i - startClean;
-            m_writer.write(chars, startClean, lengthClean);
-        }
-    }     
-    /**
-     * This method checks if a given character is between C0 or C1 range
-     * of Control characters.
-     * This method is added to support Control Characters for XML 1.1
-     * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
-     * return false. Since they are whitespace characters, no special processing is needed.
-     * 
-     * @param ch
-     * @return boolean
-     */
-    private static boolean isCharacterInC0orC1Range(char ch)
-    {
-        if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
-        	return false;
-        else        	    	
-        	return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
+      m_ispreserve = true;
+
+      m_writer.write(ch, start, length);
     }
-    /**
-     * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
-     * These are new end of line charcters added in XML 1.1.  These characters must be
-     * written as Numeric Character References (NCR) in XML 1.1 output document.
-     * 
-     * @param ch
-     * @return boolean
-     */
-    private static boolean isNELorLSEPCharacter(char ch)
+    catch (IOException e)
     {
-        return (ch == 0x85 || ch == 0x2028);
+      throw new SAXException(e);
     }
-    /**
-     * Process a dirty character and any preeceding clean characters
-     * that were not yet processed.
-     * @param chars array of characters being processed
-     * @param end one (1) beyond the last character 
-     * in chars to be processed
-     * @param i the index of the dirty character
-     * @param ch the character in chars[i]
-     * @param lastDirty the last dirty character previous to i
-     * @param fromTextNode true if the characters being processed are
-     * from a text node, false if they are from an attribute value.
-     * @return the index of the last character processed
-     * @throws IOException if an error occurs while writing
-     */
-    private int processDirty(
-        char[] chars, 
-        int end,
-        int i, 
-        char ch,
-        int lastDirty,
-        boolean fromTextNode) throws IOException
-    {
-        int startClean = lastDirty + 1;
-        // if we have some clean characters accumulated
-        // process them before the dirty one.                   
-        if (i > startClean)
-        {
-            int lengthClean = i - startClean;
-            m_writer.write(chars, startClean, lengthClean);
-        }
 
-        // process the "dirty" character
-        if (CharInfo.S_LINEFEED == ch && fromTextNode)
-        {
-            m_writer.write(m_lineSep, 0, m_lineSepLen);
-        }
-        else
-        {
-            startClean =
-                accumDefaultEscape(
-                    m_writer,
-                    (char)ch,
-                    i,
-                    chars,
-                    end,
-                    fromTextNode,
-                    false);
-            i = startClean - 1;
-        }
-        // Return the index of the last character that we just processed 
-        // which is a dirty character.
-        return i;
-    }
+  }
 
-    /**
-     * Receive notification of character data.
-     *
-     * @param s The string of characters to process.
-     *
-     * @throws org.xml.sax.SAXException if characters could not be handled
-     */
-    public void characters(String s) throws org.xml.sax.SAXException
+  /**
+   * Receive notification of character data.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param chars The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  public void characters(final char chars[], final int start, final int length)
+      throws org.xml.sax.SAXException
+  {
+    // It does not make sense to continue with rest of the method if the number of 
+    // characters to read from array is 0.
+    // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
+    // is created if string is empty.	
+    if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
+      return;
+
+    m_docIsEmpty = false;
+
+    if (m_elemContext.m_startTagOpen)
     {
-        if (m_inEntityRef && !m_expandDTDEntities)
-            return;
-        final int length = s.length();
-        if (length > m_charsBuff.length)
-        {
-            m_charsBuff = new char[length * 2 + 1];
-        }
-        s.getChars(0, length, m_charsBuff, 0);
-        characters(m_charsBuff, 0, length);
+      closeStartTag();
+      m_elemContext.m_startTagOpen = false;
     }
-
-    /**
-     * Escape and writer.write a character.
-     *
-     * @param ch character to be escaped.
-     * @param i index into character array.
-     * @param chars non-null reference to character array.
-     * @param len length of chars.
-     * @param fromTextNode true if the characters being processed are
-     * from a text node, false if the characters being processed are from
-     * an attribute value.
-     * @param escLF true if the linefeed should be escaped.
-     *
-     * @return i+1 if a character was written, i+2 if two characters
-     * were written out, else return i.
-     *
-     * @throws IOException if an error occurs while writing
-     */
-    private int accumDefaultEscape(
-        Writer writer,
-        char ch,
-        int i,
-        char[] chars,
-        int len,
-        boolean fromTextNode,
-        boolean escLF)
-        throws IOException
+    else if (m_needToCallStartDocument)
     {
+      startDocumentInternal();
+    }
 
-        int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
+    if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
+    {
+      /* either due to startCDATA() being called or due to 
+       * cdata-section-elements atribute, we need this as cdata
+       */
+      cdata(chars, start, length);
+
+      return;
+    }
+
+    if (m_cdataTagOpen)
+      closeCDATA();
+
+    if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
+    {
+      charactersRaw(chars, start, length);
+
+      // time to fire off characters generation event
+      if (m_tracer != null)
+        super.fireCharEvent(chars, start, length);
+
+      return;
+    }
+
+    if (m_elemContext.m_startTagOpen)
+    {
+      closeStartTag();
+      m_elemContext.m_startTagOpen = false;
+    }
+
+
+    try
+    {
+      int i;
+      int startClean;
+
+      // skip any leading whitspace 
+      // don't go off the end and use a hand inlined version
+      // of isWhitespace(ch)
+      final int end = start + length;
+      int lastDirtyCharProcessed = start - 1; // last non-clean character that was processed
+      // that was processed
+      final Writer writer = m_writer;
+      boolean isAllWhitespace = true;
+
+      // process any leading whitespace
+      i = start;
+      while (i < end && isAllWhitespace) {
+        char ch1 = chars[i];
+
+        if (m_charInfo.shouldMapTextChar(ch1)) {
+          // The character is supposed to be replaced by a String
+          // so write out the clean whitespace characters accumulated
+          // so far
+          // then the String.
+          writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+          String outputStringForChar = m_charInfo
+              .getOutputStringForChar(ch1);
+          writer.write(outputStringForChar);
+          // We can't say that everything we are writing out is
+          // all whitespace, we just wrote out a String.
+          isAllWhitespace = false;
+          lastDirtyCharProcessed = i; // mark the last non-clean
+          // character processed
+          i++;
+        } else {
+          // The character is clean, but is it a whitespace ?
+          switch (ch1) {
+          // TODO: Any other whitespace to consider?
+          case CharInfo.S_SPACE:
+            // Just accumulate the clean whitespace
+            i++;
+            break;
+          case CharInfo.S_LINEFEED:
+            lastDirtyCharProcessed = processLineFeed(chars, i,
+                lastDirtyCharProcessed, writer);
+            i++;
+            break;
+          case CharInfo.S_CARRIAGERETURN:
+            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+            writer.write("&#13;");
+            lastDirtyCharProcessed = i;
+            i++;
+            break;
+          case CharInfo.S_HORIZONAL_TAB:
+            // Just accumulate the clean whitespace
+            i++;
+            break;
+          default:
+            // The character was clean, but not a whitespace
+            // so break the loop to continue with this character
+            // (we don't increment index i !!)
+            isAllWhitespace = false;
+            break;
+          }
+        }
+      }
 
-        if (i == pos)
-        {
-            if (Encodings.isHighUTF16Surrogate(ch))
-            {
+      /* If there is some non-whitespace, mark that we may need
+       * to preserve this. This is only important if we have indentation on.
+       */            
+      if (i < end || !isAllWhitespace) 
+        m_ispreserve = true;
 
-                // Should be the UTF-16 low surrogate of the hig/low pair.
-                char next;
-                // Unicode code point formed from the high/low pair.
-                int codePoint = 0;
 
-                if (i + 1 >= len)
-                {
-                    throw new IOException(
-                        Utils.messages.createMessage(
-                            MsgKey.ER_INVALID_UTF16_SURROGATE,
-                            new Object[] { Integer.toHexString(ch)}));
-                    //"Invalid UTF-16 surrogate detected: "
+      for (; i < end; i++)
+      {
+        char ch = chars[i];
 
-                    //+Integer.toHexString(ch)+ " ?");
-                }
-                else
-                {
-                    next = chars[++i];
-
-                    if (!(Encodings.isLowUTF16Surrogate(next)))
-                        throw new IOException(
-                            Utils.messages.createMessage(
-                                MsgKey
-                                    .ER_INVALID_UTF16_SURROGATE,
-                                new Object[] {
-                                    Integer.toHexString(ch)
-                                        + " "
-                                        + Integer.toHexString(next)}));
-                    //"Invalid UTF-16 surrogate detected: "
-
-                    //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
-                    codePoint = Encodings.toCodePoint(ch,next);
-                }
+        if (m_charInfo.shouldMapTextChar(ch)) {
+          // The character is supposed to be replaced by a String
+          // e.g.   '&'  -->  "&amp;"
+          // e.g.   '<'  -->  "&lt;"
+          writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+          String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
+          writer.write(outputStringForChar);
+          lastDirtyCharProcessed = i;
+        }
+        else {
+          if (ch <= 0x1F) {
+            // Range 0x00 through 0x1F inclusive
+            //
+            // This covers the non-whitespace control characters
+            // in the range 0x1 to 0x1F inclusive.
+            // It also covers the whitespace control characters in the same way:
+            // 0x9   TAB
+            // 0xA   NEW LINE
+            // 0xD   CARRIAGE RETURN
+            //
+            // We also cover 0x0 ... It isn't valid
+            // but we will output "&#0;" 
+
+            // The default will handle this just fine, but this
+            // is a little performance boost to handle the more
+            // common TAB, NEW-LINE, CARRIAGE-RETURN
+            switch (ch) {
+
+            case CharInfo.S_HORIZONAL_TAB:
+              // Leave whitespace TAB as a real character
+              break;
+            case CharInfo.S_LINEFEED:
+              lastDirtyCharProcessed = processLineFeed(chars, i, lastDirtyCharProcessed, writer);
+              break;
+            case CharInfo.S_CARRIAGERETURN:
+              writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+              writer.write("&#13;");
+              lastDirtyCharProcessed = i;
+              // Leave whitespace carriage return as a real character
+              break;
+            default:
+              writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+              writer.write("&#");
+              writer.write(Integer.toString(ch));
+              writer.write(';');
+              lastDirtyCharProcessed = i;
+              break;
 
-                writer.write("&#");
-                writer.write(Integer.toString(codePoint));
-                writer.write(';');
-                pos += 2; // count the two characters that went into writing out this entity
             }
-            else
-            {
-                /*  This if check is added to support control characters in XML 1.1.
-                 *  If a character is a Control Character within C0 and C1 range, it is desirable
-                 *  to write it out as Numeric Character Reference(NCR) regardless of XML Version
-                 *  being used for output document.
-                 */ 
-                if (isCharacterInC0orC1Range(ch) || isNELorLSEPCharacter(ch))
-                {
-                    writer.write("&#");
-                    writer.write(Integer.toString(ch));
-                    writer.write(';');
-                }
-                else if ((!escapingNotNeeded(ch) || 
-                    (  (fromTextNode && m_charInfo.shouldMapTextChar(ch))
-                     || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))) 
-                && m_elemContext.m_currentElemDepth > 0)
-                {
-                    writer.write("&#");
-                    writer.write(Integer.toString(ch));
-                    writer.write(';');
-                }
-                else
-                {
-                    writer.write(ch);
-                }
-                pos++;  // count the single character that was processed
+          }
+          else if (ch < 0x7F) {  
+            // Range 0x20 through 0x7E inclusive
+            // Normal ASCII chars, do nothing, just add it to
+            // the clean characters
+
+          }
+          else if (ch <= 0x9F){
+            // Range 0x7F through 0x9F inclusive
+            // More control characters, including NEL (0x85)
+            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+            writer.write("&#");
+            writer.write(Integer.toString(ch));
+            writer.write(';');
+            lastDirtyCharProcessed = i;
+          }
+          else if (ch == CharInfo.S_LINE_SEPARATOR) {
+            // LINE SEPARATOR
+            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+            writer.write("&#8232;");
+            lastDirtyCharProcessed = i;
+          }
+          else if (m_encodingInfo.isInEncoding(ch)) {
+            // If the character is in the encoding, and
+            // not in the normal ASCII range, we also
+            // just leave it get added on to the clean characters
+          }
+          //                    else if (Encodings.isHighUTF16Surrogate(ch) && i < end-1 && Encodings.isLowUTF16Surrogate(chars[i+1])) {
+          //                      // So, this is a (valid) surrogate pair
+          //                      if (! m_encodingInfo.isInEncoding(ch, chars[i+1])) {
+          //                        int codepoint = Encodings.toCodePoint(ch, chars[i+1]);
+          //                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+          //                        writer.write("&#");
+          //                        writer.write(Integer.toString(codepoint));
+          //                        writer.write(';');
+          //                        lastDirtyCharProcessed = i+1;
+          //                      }  // Else pair is in encoding, not "dirty", just copy
+          //                      i++; // skip the low surrogate, too
+          //                    }
+          else if (Encodings.isHighUTF16Surrogate(ch)) {
+            writeOutCleanChars(chars,i,lastDirtyCharProcessed);
+            m_pendingHighUTF16Surrogate=ch;
+            lastDirtyCharProcessed=i;
+          }
+          else if (Encodings.isLowUTF16Surrogate(ch)) {
+            if(m_encodingInfo.isInEncoding(m_pendingHighUTF16Surrogate,ch)) {
+              char[] buffer= {m_pendingHighUTF16Surrogate,ch};
+              writer.write(buffer);
+            } else {
+              // Clean characters should have been flushed by high surrogate
+              int codepoint = Encodings.toCodePoint(m_pendingHighUTF16Surrogate,ch);
+              writer.write("&#");
+              writer.write(Integer.toString(codepoint));
+              writer.write(';');
             }
+            m_pendingHighUTF16Surrogate=0;
+            lastDirtyCharProcessed=i;                      
+          }
+          else {
+            // This is a fallback plan, we get here if the
+            // encoding doesn't contain ch and it's not part
+            // of a surrogate pair
+            // The right thing is to write out an entity
+            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+            writer.write("&#");
+            writer.write(Integer.toString(ch));
+            writer.write(';');
+            lastDirtyCharProcessed = i;
+          }
+        }
+      }
+
+      // we've reached the end. Any clean characters at the
+      // end of the array than need to be written out?
+      startClean = lastDirtyCharProcessed + 1;
+      if (i > startClean)
+      {
+        int lengthClean = i - startClean;
+        m_writer.write(chars, startClean, lengthClean);
+      }
+
+      // For indentation purposes, mark that we've just writen text out
+      m_isprevtext = true;
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+    // time to fire off characters generation event
+    if (m_tracer != null)
+      super.fireCharEvent(chars, start, length);
+  }
+
+  private int processLineFeed(final char[] chars, int i, int lastProcessed, final Writer writer) throws IOException {
+    if (!m_lineSepUse 
+        || (m_lineSepLen ==1 && m_lineSep[0] == CharInfo.S_LINEFEED)){
+      // We are leaving the new-line alone, and it is just
+      // being added to the 'clean' characters,
+      // so the last dirty character processed remains unchanged
+    }
+    else {
+      writeOutCleanChars(chars, i, lastProcessed);
+      writer.write(m_lineSep, 0, m_lineSepLen);
+      lastProcessed = i;
+    }
+    return lastProcessed;
+  }
+
+  private void writeOutCleanChars(final char[] chars, int i, int lastProcessed) throws IOException {
+    int startClean;
+    startClean = lastProcessed + 1;
+    if (startClean < i)
+    {
+      int lengthClean = i - startClean;
+      m_writer.write(chars, startClean, lengthClean);
+    }
+  }     
+  /**
+   * This method checks if a given character is between C0 or C1 range
+   * of Control characters.
+   * This method is added to support Control Characters for XML 1.1
+   * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
+   * return false. Since they are whitespace characters, no special processing is needed.
+   * 
+   * @param ch
+   * @return boolean
+   */
+  private static boolean isCharacterInC0orC1Range(char ch)
+  {
+    if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
+      return false;
+    else        	    	
+      return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
+  }
+  /**
+   * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
+   * These are new end of line charcters added in XML 1.1.  These characters must be
+   * written as Numeric Character References (NCR) in XML 1.1 output document.
+   * 
+   * @param ch
+   * @return boolean
+   */
+  private static boolean isNELorLSEPCharacter(char ch)
+  {
+    return (ch == 0x85 || ch == 0x2028);
+  }
+  /**
+   * Process a dirty character and any preeceding clean characters
+   * that were not yet processed.
+   * @param chars array of characters being processed
+   * @param end one (1) beyond the last character 
+   * in chars to be processed
+   * @param i the index of the dirty character
+   * @param ch the character in chars[i]
+   * @param lastDirty the last dirty character previous to i
+   * @param fromTextNode true if the characters being processed are
+   * from a text node, false if they are from an attribute value.
+   * @return the index of the last character processed
+   * @throws IOException if an error occurs while writing
+   */
+  private int processDirty(
+      char[] chars, 
+      int end,
+      int i, 
+      char ch,
+      int lastDirty,
+      boolean fromTextNode) throws IOException
+  {
+    int startClean = lastDirty + 1;
+    // if we have some clean characters accumulated
+    // process them before the dirty one.                   
+    if (i > startClean)
+    {
+      int lengthClean = i - startClean;
+      m_writer.write(chars, startClean, lengthClean);
+    }
+
+    // process the "dirty" character
+    if (CharInfo.S_LINEFEED == ch && fromTextNode)
+    {
+      m_writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+    else
+    {
+      startClean =
+          accumDefaultEscape(
+              m_writer,
+              (char)ch,
+              i,
+              chars,
+              end,
+              fromTextNode,
+              false);
+      i = startClean - 1;
+    }
+    // Return the index of the last character that we just processed 
+    // which is a dirty character.
+    return i;
+  }
+
+  /**
+   * Receive notification of character data.
+   *
+   * @param s The string of characters to process.
+   *
+   * @throws org.xml.sax.SAXException if characters could not be handled
+   */
+  public void characters(String s) throws org.xml.sax.SAXException
+  {
+    if (m_inEntityRef && !m_expandDTDEntities)
+      return;
+    final int length = s.length();
+    if (length > m_charsBuff.length)
+    {
+      m_charsBuff = new char[length * 2 + 1];
+    }
+    s.getChars(0, length, m_charsBuff, 0);
+    characters(m_charsBuff, 0, length);
+  }
+
+  /**
+   * Escape and writer.write a character.
+   *
+   * @param ch character to be escaped.
+   * @param i index into character array.
+   * @param chars non-null reference to character array.
+   * @param len length of chars.
+   * @param fromTextNode true if the characters being processed are
+   * from a text node, false if the characters being processed are from
+   * an attribute value.
+   * @param escLF true if the linefeed should be escaped.
+   *
+   * @return i+1 if a character was written, i+2 if two characters
+   * were written out, else return i.
+   *
+   * @throws IOException if an error occurs while writing
+   */
+  private int accumDefaultEscape(
+      Writer writer,
+      char ch,
+      int i,
+      char[] chars,
+      int len,
+      boolean fromTextNode,
+      boolean escLF)
+          throws IOException
+  {
+
+    int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
+
+    if (i == pos)
+    {
+      if (Encodings.isHighUTF16Surrogate(ch))
+      {
+
+        // Should be the UTF-16 low surrogate of the hig/low pair.
+        char next;
+        // Unicode code point formed from the high/low pair.
+        int codePoint = 0;
+
+        if (i + 1 >= len)
+        {
+          throw new IOException(
+              Utils.messages.createMessage(
+                  MsgKey.ER_INVALID_UTF16_SURROGATE,
+                  new Object[] { Integer.toHexString(ch)}));
+          //"Invalid UTF-16 surrogate detected: "
 
+          //+Integer.toHexString(ch)+ " ?");
         }
-        return pos;
-    }
+        else
+        {
+          next = chars[++i];
 
-    /**
-     * Receive notification of the beginning of an element, although this is a
-     * SAX method additional namespace or attribute information can occur before
-     * or after this call, that is associated with this element.
-     *
-     *
-     * @param namespaceURI The Namespace URI, or the empty string if the
-     *        element has no Namespace URI or if Namespace
-     *        processing is not being performed.
-     * @param localName The local name (without prefix), or the
-     *        empty string if Namespace processing is not being
-     *        performed.
-     * @param name The element type name.
-     * @param atts The attributes attached to the element, if any.
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *            wrapping another exception.
-     * @see org.xml.sax.ContentHandler#startElement
-     * @see org.xml.sax.ContentHandler#endElement
-     * @see org.xml.sax.AttributeList
-     */
-    public void startElement(
-        String namespaceURI,
-        String localName,
-        String name,
-        Attributes atts)
-        throws org.xml.sax.SAXException
-    {
-        if (m_inEntityRef)
-            return;
+          if (!(Encodings.isLowUTF16Surrogate(next)))
+            throw new IOException(
+                Utils.messages.createMessage(
+                    MsgKey
+                    .ER_INVALID_UTF16_SURROGATE,
+                    new Object[] {
+                        Integer.toHexString(ch)
+                        + " "
+                        + Integer.toHexString(next)}));
+          //"Invalid UTF-16 surrogate detected: "
 
-        if (m_needToCallStartDocument)
+          //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
+          codePoint = Encodings.toCodePoint(ch,next);
+        }
+
+        writer.write("&#");
+        writer.write(Integer.toString(codePoint));
+        writer.write(';');
+        pos += 2; // count the two characters that went into writing out this entity
+      }
+      else
+      {
+        /*  This if check is added to support control characters in XML 1.1.
+         *  If a character is a Control Character within C0 and C1 range, it is desirable
+         *  to write it out as Numeric Character Reference(NCR) regardless of XML Version
+         *  being used for output document.
+         */ 
+        if (isCharacterInC0orC1Range(ch) || isNELorLSEPCharacter(ch))
         {
-            startDocumentInternal();
-            m_needToCallStartDocument = false;
-            m_docIsEmpty = false;
+          writer.write("&#");
+          writer.write(Integer.toString(ch));
+          writer.write(';');
         }
-        else if (m_cdataTagOpen)
-            closeCDATA();
-        try
+        else if ((!escapingNotNeeded(ch) || 
+            (  (fromTextNode && m_charInfo.shouldMapTextChar(ch))
+                || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))) 
+            && m_elemContext.m_currentElemDepth > 0)
         {
-            if (m_needToOutputDocTypeDecl) {
-                if(null != getDoctypeSystem()) {
-                    outputDocTypeDecl(name, true);
-                }
-                m_needToOutputDocTypeDecl = false;
-            }
-        
-            /* before we over-write the current elementLocalName etc.
-             * lets close out the old one (if we still need to)
-             */
-            if (m_elemContext.m_startTagOpen)
-            {
-                closeStartTag();
-                m_elemContext.m_startTagOpen = false;
-            }
+          writer.write("&#");
+          writer.write(Integer.toString(ch));
+          writer.write(';');
+        }
+        else
+        {
+          writer.write(ch);
+        }
+        pos++;  // count the single character that was processed
+      }
+
+    }
+    return pos;
+  }
+
+  /**
+   * Receive notification of the beginning of an element, although this is a
+   * SAX method additional namespace or attribute information can occur before
+   * or after this call, that is associated with this element.
+   *
+   *
+   * @param namespaceURI The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param name The element type name.
+   * @param atts The attributes attached to the element, if any.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startElement
+   * @see org.xml.sax.ContentHandler#endElement
+   * @see org.xml.sax.AttributeList
+   */
+  public void startElement(
+      String namespaceURI,
+      String localName,
+      String name,
+      Attributes atts)
+          throws org.xml.sax.SAXException
+  {
+    if (m_inEntityRef)
+      return;
+
+    if (m_needToCallStartDocument)
+    {
+      startDocumentInternal();
+      m_needToCallStartDocument = false;
+      m_docIsEmpty = false;
+    }
+    else if (m_cdataTagOpen)
+      closeCDATA();
+    try
+    {
+      if (m_needToOutputDocTypeDecl) {
+        if(null != getDoctypeSystem()) {
+          outputDocTypeDecl(name, true);
+        }
+        m_needToOutputDocTypeDecl = false;
+      }
+
+      /* before we over-write the current elementLocalName etc.
+       * lets close out the old one (if we still need to)
+       */
+      if (m_elemContext.m_startTagOpen)
+      {
+        closeStartTag();
+        m_elemContext.m_startTagOpen = false;
+      }
+
+      if (namespaceURI != null)
+        ensurePrefixIsDeclared(namespaceURI, name);
+
+      m_ispreserve = false;
+
+      if (shouldIndent() && m_startNewLine)
+      {
+        indent();
+      }
 
-            if (namespaceURI != null)
-                ensurePrefixIsDeclared(namespaceURI, name);
-                
-            m_ispreserve = false;
+      m_startNewLine = true;
+
+      final java.io.Writer writer = m_writer;
+      writer.write('<');
+      writer.write(name);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+    // process the attributes now, because after this SAX call they might be gone
+    if (atts != null)
+      addAttributes(atts);
+
+    m_elemContext = m_elemContext.push(namespaceURI,localName,name);
+    m_isprevtext = false;
+
+    if (m_tracer != null)
+      firePseudoAttributes();
+  }
+
+  /**
+   * Receive notification of the beginning of an element, additional
+   * namespace or attribute information can occur before or after this call,
+   * that is associated with this element.
+   *
+   *
+   * @param elementNamespaceURI The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param elementLocalName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param elementName The element type name.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startElement
+   * @see org.xml.sax.ContentHandler#endElement
+   * @see org.xml.sax.AttributeList
+   *
+   * @throws org.xml.sax.SAXException if characters could not be handled
+   */
+  public void startElement(
+      String elementNamespaceURI,
+      String elementLocalName,
+      String elementName)
+          throws SAXException
+  {
+    startElement(elementNamespaceURI, elementLocalName, elementName, null);
+  }
+
+  public void startElement(String elementName) throws SAXException
+  {
+    startElement(null, null, elementName, null);
+  }
+
+  /**
+   * Output the doc type declaration.
+   *
+   * @param name non-null reference to document type name.
+   * NEEDSDOC @param closeDecl
+   *
+   * @throws java.io.IOException
+   */
+  void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException
+  {
+    if (m_cdataTagOpen)
+      closeCDATA();
+    try
+    {
+      final java.io.Writer writer = m_writer;
+      writer.write("<!DOCTYPE ");
+      writer.write(name);
+
+      String doctypePublic = getDoctypePublic();
+      if (null != doctypePublic)
+      {
+        writer.write(" PUBLIC \"");
+        writer.write(doctypePublic);
+        writer.write('\"');
+      }
 
-            if (shouldIndent() && m_startNewLine)
-            {
-                indent();
-            }
+      String doctypeSystem = getDoctypeSystem();
+      if (null != doctypeSystem)
+      {
+        if (null == doctypePublic)
+          writer.write(" SYSTEM \"");
+        else
+          writer.write(" \"");
 
-            m_startNewLine = true;
+        writer.write(doctypeSystem);
 
-            final java.io.Writer writer = m_writer;
-            writer.write('<');
-            writer.write(name);
-        }
-        catch (IOException e)
+        if (closeDecl)
         {
-            throw new SAXException(e);
+          writer.write("\">");
+          writer.write(m_lineSep, 0, m_lineSepLen);
+          closeDecl = false; // done closing
         }
-
-        // process the attributes now, because after this SAX call they might be gone
-        if (atts != null)
-            addAttributes(atts);
-            
-        m_elemContext = m_elemContext.push(namespaceURI,localName,name);
-        m_isprevtext = false;
-
-        if (m_tracer != null)
-            firePseudoAttributes();
+        else
+          writer.write('\"');
+      }
     }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+  }
+
+  /**
+   * Process the attributes, which means to write out the currently
+   * collected attributes to the writer. The attributes are not
+   * cleared by this method
+   * 
+   * @param writer the writer to write processed attributes to.
+   * @param nAttrs the number of attributes in m_attributes 
+   * to be processed
+   *
+   * @throws IOException if an error occurs while writing
+   * @throws org.xml.sax.SAXException never (I think)
+   */
+  public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException
+  {
+    /* real SAX attributes are not passed in, so process the 
+     * attributes that were collected after the startElement call.
+     * _attribVector is a "cheap" list for Stream serializer output
+     * accumulated over a series of calls to attribute(name,value)
+     */
+
+    String encoding = getEncoding();
+    for (int i = 0; i < nAttrs; i++)
+    {
+      // elementAt is JDK 1.1.8
+      final String name = m_attributes.getQName(i);
+      final String value = m_attributes.getValue(i);
+      writer.write(' ');
+      writer.write(name);
+      writer.write("=\"");
+      writeAttrString(writer, value, encoding);
+      writer.write('\"');
+    }
+  }
+
+  /**
+   * Writes the specified <var>string</var> after substituting <VAR>specials</VAR>,
+   * and UTF-16 surrogates for chracter references <CODE>&amp;#xnn</CODE>.
+   *
+   * @param   writer      to recieve processed string
+   * @param   string      String to convert to XML format.
+   * @param   encoding    CURRENTLY NOT IMPLEMENTED.
+   *
+   * @throws IOException if an error occurs while writing
+   */
+  public void writeAttrString(
+      Writer writer,
+      String string,
+      String encoding)
+          throws IOException
+  {
+    final int len = string.length();
+    if (len > m_attrBuff.length)
+    {
+      m_attrBuff = new char[len*2 + 1];             
+    }
+    string.getChars(0,len, m_attrBuff, 0);   
+    final char[] stringChars = m_attrBuff;
+
+    for (int i = 0; i < len; i++)
+    {
+      char ch = stringChars[i];
+
+      if (m_charInfo.shouldMapAttrChar(ch)) {
+        // The character is supposed to be replaced by a String
+        // e.g.   '&'  -->  "&amp;"
+        // e.g.   '<'  -->  "&lt;"
+        accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
+      }
+      else {
+        if (0x0 <= ch && ch <= 0x1F) {
+          // Range 0x00 through 0x1F inclusive
+          // This covers the non-whitespace control characters
+          // in the range 0x1 to 0x1F inclusive.
+          // It also covers the whitespace control characters in the same way:
+          // 0x9   TAB
+          // 0xA   NEW LINE
+          // 0xD   CARRIAGE RETURN
+          //
+          // We also cover 0x0 ... It isn't valid
+          // but we will output "&#0;" 
+
+          // The default will handle this just fine, but this
+          // is a little performance boost to handle the more
+          // common TAB, NEW-LINE, CARRIAGE-RETURN
+          switch (ch) {
+
+          case CharInfo.S_HORIZONAL_TAB:
+            writer.write("&#9;");
+            break;
+          case CharInfo.S_LINEFEED:
+            writer.write("&#10;");
+            break;
+          case CharInfo.S_CARRIAGERETURN:
+            writer.write("&#13;");
+            break;
+          default:
+            writer.write("&#");
+            writer.write(Integer.toString(ch));
+            writer.write(';');
+            break;
+
+          }
+        }
+        else if (ch < 0x7F) {   
+          // Range 0x20 through 0x7E inclusive
+          // Normal ASCII chars
+          writer.write(ch);
+        }
+        else if (ch <= 0x9F){
+          // Range 0x7F through 0x9F inclusive
+          // More control characters
+          writer.write("&#");
+          writer.write(Integer.toString(ch));
+          writer.write(';');
+        }
+        else if (ch == CharInfo.S_LINE_SEPARATOR) {
+          // LINE SEPARATOR
+          writer.write("&#8232;");
+        }
+        else if (m_encodingInfo.isInEncoding(ch)) {
+          // If the character is in the encoding, and
+          // not in the normal ASCII range, we also
+          // just write it out
+          writer.write(ch);
+        }
+        else if (Encodings.isHighUTF16Surrogate(ch))
+        {
+          writeUTF16Surrogate(ch, stringChars, i, len);
+          i++ ; // process two input characters
+        }
+        else {
+          // This is a fallback plan, we should never get here
+          // but if the character wasn't previously handled
+          // (i.e. isn't in the encoding, etc.) then what
+          // should we do?  We choose to write out a character ref
+          writer.write("&#");
+          writer.write(Integer.toString(ch));
+          writer.write(';');
+        }
 
-    /**
-      * Receive notification of the beginning of an element, additional
-      * namespace or attribute information can occur before or after this call,
-      * that is associated with this element.
-      *
-      *
-      * @param elementNamespaceURI The Namespace URI, or the empty string if the
-      *        element has no Namespace URI or if Namespace
-      *        processing is not being performed.
-      * @param elementLocalName The local name (without prefix), or the
-      *        empty string if Namespace processing is not being
-      *        performed.
-      * @param elementName The element type name.
-      * @throws org.xml.sax.SAXException Any SAX exception, possibly
-      *            wrapping another exception.
-      * @see org.xml.sax.ContentHandler#startElement
-      * @see org.xml.sax.ContentHandler#endElement
-      * @see org.xml.sax.AttributeList
-      *
-      * @throws org.xml.sax.SAXException if characters could not be handled
-      */
-    public void startElement(
-        String elementNamespaceURI,
-        String elementLocalName,
-        String elementName)
-        throws SAXException
-    {
-        startElement(elementNamespaceURI, elementLocalName, elementName, null);
-    }
-
-    public void startElement(String elementName) throws SAXException
-    {
-        startElement(null, null, elementName, null);
+      }
     }
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   *
+   * @param namespaceURI The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param name The element type name
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void endElement(String namespaceURI, String localName, String name)
+      throws org.xml.sax.SAXException
+  {
+    if (m_inEntityRef)
+      return;
+
+    // namespaces declared at the current depth are no longer valid
+    // so get rid of them    
+    m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
+
+    try
+    {
+      final java.io.Writer writer = m_writer;
+      if (m_elemContext.m_startTagOpen)
+      {
+        if (m_tracer != null)
+          super.fireStartElem(m_elemContext.m_elementName);
+        int nAttrs = m_attributes.getLength();
+        if (nAttrs > 0)
+        {
+          processAttributes(m_writer, nAttrs);
+          // clear attributes object for re-use with next element
+          m_attributes.clear();
+        }
+        if (m_spaceBeforeClose)
+          writer.write(" />");
+        else
+          writer.write("/>");
+        /* don't need to pop cdataSectionState because
+         * this element ended so quickly that we didn't get
+         * to push the state.
+         */
 
-    /**
-     * Output the doc type declaration.
-     *
-     * @param name non-null reference to document type name.
-     * NEEDSDOC @param closeDecl
-     *
-     * @throws java.io.IOException
-     */
-    void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException
-    {
+      }
+      else
+      {
         if (m_cdataTagOpen)
-            closeCDATA();
-        try
-        {
-            final java.io.Writer writer = m_writer;
-            writer.write("<!DOCTYPE ");
-            writer.write(name);
+          closeCDATA();
 
-            String doctypePublic = getDoctypePublic();
-            if (null != doctypePublic)
-            {
-                writer.write(" PUBLIC \"");
-                writer.write(doctypePublic);
-                writer.write('\"');
-            }
+        if (shouldIndent())
+          indent(m_elemContext.m_currentElemDepth - 1);
+        writer.write('<');
+        writer.write('/');
+        writer.write(name);
+        writer.write('>');
+      }
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+    if (!m_elemContext.m_startTagOpen && m_doIndent)
+    {
+      m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
+    }
+
+    m_isprevtext = false;
+
+    // fire off the end element event
+    if (m_tracer != null)
+      super.fireEndElem(name);
+    m_elemContext = m_elemContext.m_prev;
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   * @param name The element type name
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *     wrapping another exception.
+   */
+  public void endElement(String name) throws org.xml.sax.SAXException
+  {
+    endElement(null, null, name);
+  }
+
+  /**
+   * Begin the scope of a prefix-URI Namespace mapping
+   * just before another element is about to start.
+   * This call will close any open tags so that the prefix mapping
+   * will not apply to the current element, but the up comming child.
+   * 
+   * @see org.xml.sax.ContentHandler#startPrefixMapping
+   * 
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI the prefix is mapped to.
+   * 
+   * @throws org.xml.sax.SAXException The client may throw
+   *            an exception during processing.
+   * 
+   */
+  public void startPrefixMapping(String prefix, String uri)
+      throws org.xml.sax.SAXException
+  {
+    // the "true" causes the flush of any open tags
+    startPrefixMapping(prefix, uri, true);
+  }
+
+  /**
+   * Handle a prefix/uri mapping, which is associated with a startElement()
+   * that is soon to follow. Need to close any open start tag to make
+   * sure than any name space attributes due to this event are associated wih
+   * the up comming element, not the current one.
+   * @see ExtendedContentHandler#startPrefixMapping
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI the prefix is mapped to.
+   * @param shouldFlush true if any open tags need to be closed first, this
+   * will impact which element the mapping applies to (open parent, or its up
+   * comming child)
+   * @return returns true if the call made a change to the current 
+   * namespace information, false if it did not change anything, e.g. if the
+   * prefix/namespace mapping was already in scope from before.
+   * 
+   * @throws org.xml.sax.SAXException The client may throw
+   *            an exception during processing.
+   *
+   *
+   */
+  public boolean startPrefixMapping(
+      String prefix,
+      String uri,
+      boolean shouldFlush)
+          throws org.xml.sax.SAXException
+  {
+
+    /* Remember the mapping, and at what depth it was declared
+     * This is one greater than the current depth because these
+     * mappings will apply to the next depth. This is in
+     * consideration that startElement() will soon be called
+     */
+
+    boolean pushed;
+    int pushDepth;
+    if (shouldFlush)
+    {
+      flushPending();
+      // the prefix mapping applies to the child element (one deeper)
+      pushDepth = m_elemContext.m_currentElemDepth + 1;
+    }
+    else
+    {
+      // the prefix mapping applies to the current element
+      pushDepth = m_elemContext.m_currentElemDepth;
+    }
+    pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
+
+    if (pushed)
+    {
+      /* Brian M.: don't know if we really needto do this. The
+       * callers of this object should have injected both
+       * startPrefixMapping and the attributes.  We are 
+       * just covering our butt here.
+       */
+      String name;
+      if (EMPTYSTRING.equals(prefix))
+      {
+        name = "xmlns";
+        addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false);
+      }
+      else
+      {
+        if (!EMPTYSTRING.equals(uri))
+          // hack for XSLTC attribset16 test
+        { // that maps ns1 prefix to "" URI
+          name = "xmlns:" + prefix;
 
-            String doctypeSystem = getDoctypeSystem();
-            if (null != doctypeSystem)
-            {
-                if (null == doctypePublic)
-                    writer.write(" SYSTEM \"");
-                else
-                    writer.write(" \"");
-
-                writer.write(doctypeSystem);
-
-                if (closeDecl)
-                {
-                    writer.write("\">");
-                    writer.write(m_lineSep, 0, m_lineSepLen);
-                    closeDecl = false; // done closing
-                }
-                else
-                    writer.write('\"');
-            }
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
+          /* for something like xmlns:abc="w3.pretend.org"
+           *  the      uri is the value, that is why we pass it in the
+           * value, or 5th slot of addAttributeAlways()
+           */
+          addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false);
         }
+      }
     }
+    return pushed;
+  }
 
-    /**
-     * Process the attributes, which means to write out the currently
-     * collected attributes to the writer. The attributes are not
-     * cleared by this method
-     * 
-     * @param writer the writer to write processed attributes to.
-     * @param nAttrs the number of attributes in m_attributes 
-     * to be processed
-     *
-     * @throws IOException if an error occurs while writing
-     * @throws org.xml.sax.SAXException never (I think)
-     */
-    public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException
-    {
-            /* real SAX attributes are not passed in, so process the 
-             * attributes that were collected after the startElement call.
-             * _attribVector is a "cheap" list for Stream serializer output
-             * accumulated over a series of calls to attribute(name,value)
-             */
+  /**
+   * Receive notification of an XML comment anywhere in the document. This
+   * callback will be used for comments inside or outside the document
+   * element, including comments in the external DTD subset (if read).
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length)
+      throws org.xml.sax.SAXException
+  {
 
-            String encoding = getEncoding();
-            for (int i = 0; i < nAttrs; i++)
-            {
-                // elementAt is JDK 1.1.8
-                final String name = m_attributes.getQName(i);
-                final String value = m_attributes.getValue(i);
-                writer.write(' ');
-                writer.write(name);
-                writer.write("=\"");
-                writeAttrString(writer, value, encoding);
-                writer.write('\"');
-            }
+    int start_old = start;
+    if (m_inEntityRef)
+      return;
+    if (m_elemContext.m_startTagOpen)
+    {
+      closeStartTag();
+      m_elemContext.m_startTagOpen = false;
     }
-
-    /**
-     * Writes the specified <var>string</var> after substituting <VAR>specials</VAR>,
-     * and UTF-16 surrogates for chracter references <CODE>&amp;#xnn</CODE>.
-     *
-     * @param   writer      to recieve processed string
-     * @param   string      String to convert to XML format.
-     * @param   encoding    CURRENTLY NOT IMPLEMENTED.
-     *
-     * @throws IOException if an error occurs while writing
-     */
-    public void writeAttrString(
-        Writer writer,
-        String string,
-        String encoding)
-        throws IOException
-    {
-        final int len = string.length();
-        if (len > m_attrBuff.length)
-        {
-           m_attrBuff = new char[len*2 + 1];             
-        }
-        string.getChars(0,len, m_attrBuff, 0);   
-        final char[] stringChars = m_attrBuff;
-
-        for (int i = 0; i < len; i++)
-        {
-            char ch = stringChars[i];
-            
-            if (m_charInfo.shouldMapAttrChar(ch)) {
-                // The character is supposed to be replaced by a String
-                // e.g.   '&'  -->  "&amp;"
-                // e.g.   '<'  -->  "&lt;"
-                accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
-            }
-            else {
-                if (0x0 <= ch && ch <= 0x1F) {
-                    // Range 0x00 through 0x1F inclusive
-                    // This covers the non-whitespace control characters
-                    // in the range 0x1 to 0x1F inclusive.
-                    // It also covers the whitespace control characters in the same way:
-                    // 0x9   TAB
-                    // 0xA   NEW LINE
-                    // 0xD   CARRIAGE RETURN
-                    //
-                    // We also cover 0x0 ... It isn't valid
-                    // but we will output "&#0;" 
-                    
-                    // The default will handle this just fine, but this
-                    // is a little performance boost to handle the more
-                    // common TAB, NEW-LINE, CARRIAGE-RETURN
-                    switch (ch) {
-
-                    case CharInfo.S_HORIZONAL_TAB:
-                        writer.write("&#9;");
-                        break;
-                    case CharInfo.S_LINEFEED:
-                        writer.write("&#10;");
-                        break;
-                    case CharInfo.S_CARRIAGERETURN:
-                        writer.write("&#13;");
-                        break;
-                    default:
-                        writer.write("&#");
-                        writer.write(Integer.toString(ch));
-                        writer.write(';');
-                        break;
-
-                    }
-                }
-                else if (ch < 0x7F) {   
-                    // Range 0x20 through 0x7E inclusive
-                    // Normal ASCII chars
-                        writer.write(ch);
-                }
-                else if (ch <= 0x9F){
-                    // Range 0x7F through 0x9F inclusive
-                    // More control characters
-                    writer.write("&#");
-                    writer.write(Integer.toString(ch));
-                    writer.write(';');
-                }
-                else if (ch == CharInfo.S_LINE_SEPARATOR) {
-                    // LINE SEPARATOR
-                    writer.write("&#8232;");
-                }
-                else if (m_encodingInfo.isInEncoding(ch)) {
-                    // If the character is in the encoding, and
-                    // not in the normal ASCII range, we also
-                    // just write it out
-                    writer.write(ch);
-                }
-                else if (Encodings.isHighUTF16Surrogate(ch))
-                {
-                    writeUTF16Surrogate(ch, stringChars, i, len);
-                    i++ ; // process two input characters
-                }
-                else {
-                    // This is a fallback plan, we should never get here
-                    // but if the character wasn't previously handled
-                    // (i.e. isn't in the encoding, etc.) then what
-                    // should we do?  We choose to write out a character ref
-                    writer.write("&#");
-                    writer.write(Integer.toString(ch));
-                    writer.write(';');
-                }
-                    
-            }
-        }
+    else if (m_needToCallStartDocument)
+    {
+      startDocumentInternal();
+      m_needToCallStartDocument = false;
     }
 
-    /**
-     * Receive notification of the end of an element.
-     *
-     *
-     * @param namespaceURI The Namespace URI, or the empty string if the
-     *        element has no Namespace URI or if Namespace
-     *        processing is not being performed.
-     * @param localName The local name (without prefix), or the
-     *        empty string if Namespace processing is not being
-     *        performed.
-     * @param name The element type name
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *            wrapping another exception.
-     */
-    public void endElement(String namespaceURI, String localName, String name)
-        throws org.xml.sax.SAXException
+    try
     {
-        if (m_inEntityRef)
-            return;
-
-        // namespaces declared at the current depth are no longer valid
-        // so get rid of them    
-        m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
-
-        try
-        {
-            final java.io.Writer writer = m_writer;
-            if (m_elemContext.m_startTagOpen)
-            {
-                if (m_tracer != null)
-                    super.fireStartElem(m_elemContext.m_elementName);
-                int nAttrs = m_attributes.getLength();
-                if (nAttrs > 0)
-                {
-                    processAttributes(m_writer, nAttrs);
-                    // clear attributes object for re-use with next element
-                    m_attributes.clear();
-                }
-                if (m_spaceBeforeClose)
-                    writer.write(" />");
-                else
-                    writer.write("/>");
-                /* don't need to pop cdataSectionState because
-                 * this element ended so quickly that we didn't get
-                 * to push the state.
-                 */
+      final int limit = start + length;
+      boolean wasDash = false;
+      if (m_cdataTagOpen)
+        closeCDATA();
 
-            }
-            else
-            {
-                if (m_cdataTagOpen)
-                    closeCDATA();
-
-                if (shouldIndent())
-                    indent(m_elemContext.m_currentElemDepth - 1);
-                writer.write('<');
-                writer.write('/');
-                writer.write(name);
-                writer.write('>');
-            }
-        }
-        catch (IOException e)
-        {
-            throw new SAXException(e);
-        }
+      if (shouldIndent())
+        indent();
 
-        if (!m_elemContext.m_startTagOpen && m_doIndent)
+      final java.io.Writer writer = m_writer;    
+      writer.write(COMMENT_BEGIN);
+      // Detect occurrences of two consecutive dashes, handle as necessary.
+      for (int i = start; i < limit; i++)
+      {
+        if (wasDash && ch[i] == '-')
         {
-            m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
+          writer.write(ch, start, i - start);
+          writer.write(" -");
+          start = i + 1;
         }
+        wasDash = (ch[i] == '-');
+      }
 
-        m_isprevtext = false;
-
-        // fire off the end element event
-        if (m_tracer != null)
-            super.fireEndElem(name);
-        m_elemContext = m_elemContext.m_prev;
+      // if we have some chars in the comment
+      if (length > 0)
+      {
+        // Output the remaining characters (if any)
+        final int remainingChars = (limit - start);
+        if (remainingChars > 0)
+          writer.write(ch, start, remainingChars);
+        // Protect comment end from a single trailing dash
+        if (ch[limit - 1] == '-')
+          writer.write(' ');
+      }
+      writer.write(COMMENT_END);
     }
-
-    /**
-     * Receive notification of the end of an element.
-     * @param name The element type name
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *     wrapping another exception.
-     */
-    public void endElement(String name) throws org.xml.sax.SAXException
+    catch (IOException e)
     {
-        endElement(null, null, name);
+      throw new SAXException(e);
     }
 
-    /**
-     * Begin the scope of a prefix-URI Namespace mapping
-     * just before another element is about to start.
-     * This call will close any open tags so that the prefix mapping
-     * will not apply to the current element, but the up comming child.
-     * 
-     * @see org.xml.sax.ContentHandler#startPrefixMapping
+    /*
+     * Don't write out any indentation whitespace now,
+     * because there may be non-whitespace text after this.
      * 
-     * @param prefix The Namespace prefix being declared.
-     * @param uri The Namespace URI the prefix is mapped to.
-     * 
-     * @throws org.xml.sax.SAXException The client may throw
-     *            an exception during processing.
-     * 
-     */
-    public void startPrefixMapping(String prefix, String uri)
-        throws org.xml.sax.SAXException
-    {
-        // the "true" causes the flush of any open tags
-        startPrefixMapping(prefix, uri, true);
+     * Simply mark that at this point if we do decide
+     * to indent that we should 
+     * add a newline on the end of the current line before
+     * the indentation at the start of the next line.
+     */ 
+    m_startNewLine = true;
+    // time to generate comment event
+    if (m_tracer != null)
+      super.fireCommentEvent(ch, start_old,length);
+  }
+
+  /**
+   * Report the end of a CDATA section.
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   *
+   *  @see  #startCDATA
+   */
+  public void endCDATA() throws org.xml.sax.SAXException
+  {
+    if (m_cdataTagOpen)
+      closeCDATA();
+    m_cdataStartCalled = false;
+  }
+
+  /**
+   * Report the end of DTD declarations.
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   * @see #startDTD
+   */
+  public void endDTD() throws org.xml.sax.SAXException
+  {
+    try
+    {
+      if (m_needToOutputDocTypeDecl)
+      {
+        outputDocTypeDecl(m_elemContext.m_elementName, false);
+        m_needToOutputDocTypeDecl = false;
+      }
+      final java.io.Writer writer = m_writer;
+      if (!m_inDoctype)
+        writer.write("]>");
+      else
+      {
+        writer.write('>');
+      }
+
+      writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+  }
+
+  /**
+   * End the scope of a prefix-URI Namespace mapping.
+   * @see org.xml.sax.ContentHandler#endPrefixMapping
+   * 
+   * @param prefix The prefix that was being mapping.
+   * @throws org.xml.sax.SAXException The client may throw
+   *            an exception during processing.
+   */
+  public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
+  { // do nothing
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   * 
+   * Not sure how to get this invoked quite yet.
+   * 
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #characters
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+      throws org.xml.sax.SAXException
+  {
+
+    if (0 == length)
+      return;
+    characters(ch, start, length);
+  }
+
+  /**
+   * Receive notification of a skipped entity.
+   * @see org.xml.sax.ContentHandler#skippedEntity
+   * 
+   * @param name The name of the skipped entity.  If it is a
+   *       parameter                   entity, the name will begin with '%',
+   * and if it is the external DTD subset, it will be the string
+   * "[dtd]".
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
+   * another exception.
+   */
+  public void skippedEntity(String name) throws org.xml.sax.SAXException
+  { // TODO: Should handle
+  }
+
+  /**
+   * Report the start of a CDATA section.
+   * 
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   * @see #endCDATA
+   */
+  public void startCDATA() throws org.xml.sax.SAXException
+  {
+    m_cdataStartCalled = true;
+  }
+
+  /**
+   * Report the beginning of an entity.
+   * 
+   * The start and end of the document entity are not reported.
+   * The start and end of the external DTD subset are reported
+   * using the pseudo-name "[dtd]".  All other events must be
+   * properly nested within start/end entity events.
+   * 
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   * @see #endEntity
+   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+   */
+  public void startEntity(String name) throws org.xml.sax.SAXException
+  {
+    if (name.equals("[dtd]"))
+      m_inExternalDTD = true;
+
+    if (!m_expandDTDEntities && !m_inExternalDTD) {
+      /* Only leave the entity as-is if
+       * we've been told not to expand them
+       * and this is not the magic [dtd] name.
+       */
+      startNonEscaping();
+      characters("&" + name + ';');
+      endNonEscaping();
     }
 
-    /**
-     * Handle a prefix/uri mapping, which is associated with a startElement()
-     * that is soon to follow. Need to close any open start tag to make
-     * sure than any name space attributes due to this event are associated wih
-     * the up comming element, not the current one.
-     * @see ExtendedContentHandler#startPrefixMapping
-     *
-     * @param prefix The Namespace prefix being declared.
-     * @param uri The Namespace URI the prefix is mapped to.
-     * @param shouldFlush true if any open tags need to be closed first, this
-     * will impact which element the mapping applies to (open parent, or its up
-     * comming child)
-     * @return returns true if the call made a change to the current 
-     * namespace information, false if it did not change anything, e.g. if the
-     * prefix/namespace mapping was already in scope from before.
-     * 
-     * @throws org.xml.sax.SAXException The client may throw
-     *            an exception during processing.
-     *
-     *
-     */
-    public boolean startPrefixMapping(
-        String prefix,
-        String uri,
-        boolean shouldFlush)
-        throws org.xml.sax.SAXException
-    {
+    m_inEntityRef = true;
+  }
 
-        /* Remember the mapping, and at what depth it was declared
-         * This is one greater than the current depth because these
-         * mappings will apply to the next depth. This is in
-         * consideration that startElement() will soon be called
-         */
+  /**
+   * For the enclosing elements starting tag write out
+   * out any attributes followed by the &gt; character.
+   *
+   * @throws org.xml.sax.SAXException if underlying writer encounters error
+   */
+  protected void closeStartTag() throws SAXException
+  {
 
-        boolean pushed;
-        int pushDepth;
-        if (shouldFlush)
-        {
-            flushPending();
-            // the prefix mapping applies to the child element (one deeper)
-            pushDepth = m_elemContext.m_currentElemDepth + 1;
-        }
-        else
+    if (m_elemContext.m_startTagOpen)
+    {
+
+      try
+      {
+        if (m_tracer != null)
+          super.fireStartElem(m_elemContext.m_elementName);
+        int nAttrs = m_attributes.getLength();
+        if (nAttrs > 0)
         {
-            // the prefix mapping applies to the current element
-            pushDepth = m_elemContext.m_currentElemDepth;
+          processAttributes(m_writer, nAttrs);
+          // clear attributes object for re-use with next element
+          m_attributes.clear();
         }
-        pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
+        m_writer.write('>');
+      }
+      catch (IOException e)
+      {
+        throw new SAXException(e);
+      }
+
+      /* whether Xalan or XSLTC, we have the prefix mappings now, so
+       * lets determine if the current element is specified in the cdata-
+       * section-elements list.
+       */
+      if (m_CdataElems != null)
+        m_elemContext.m_isCdataSection = isCdataSection();
+
+      if (m_doIndent)
+      {
+        m_isprevtext = false;
+        m_preserves.push(m_ispreserve);
+      }
+    }
+
+  }
+
+  /**
+   * Report the start of DTD declarations, if any.
+   *
+   * Any declarations are assumed to be in the internal subset unless
+   * otherwise indicated.
+   * 
+   * @param name The document type name.
+   * @param publicId The declared public identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @param systemId The declared system identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @throws org.xml.sax.SAXException The application may raise an
+   *            exception.
+   * @see #endDTD
+   * @see #startEntity
+   */
+  public void startDTD(String name, String publicId, String systemId)
+      throws org.xml.sax.SAXException
+  {
+    setDoctypeSystem(systemId);
+    setDoctypePublic(publicId);
+
+    m_elemContext.m_elementName = name;
+    m_inDoctype = true;
+  }
+
+  /**
+   * Returns the m_indentAmount.
+   * @return int
+   */
+  public int getIndentAmount()
+  {
+    return m_indentAmount;
+  }
+
+  /**
+   * Sets the m_indentAmount.
+   * 
+   * @param m_indentAmount The m_indentAmount to set
+   */
+  public void setIndentAmount(int m_indentAmount)
+  {
+    this.m_indentAmount = m_indentAmount;
+  }
+
+  /**
+   * Tell if, based on space preservation constraints and the doIndent property,
+   * if an indent should occur.
+   *
+   * @return True if an indent should occur.
+   */
+  protected boolean shouldIndent()
+  {
+    return m_doIndent && (!m_ispreserve && !m_isprevtext) && m_elemContext.m_currentElemDepth > 0;
+  }
+
+  /**
+   * Searches for the list of qname properties with the specified key in the
+   * property list. If the key is not found in this property list, the default
+   * property list, and its defaults, recursively, are then checked. The
+   * method returns <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @param props the list of properties to search in.
+   * 
+   * Sets the vector of local-name/URI pairs of the cdata section elements
+   * specified in the cdata-section-elements property.
+   * 
+   * This method is essentially a copy of getQNameProperties() from
+   * OutputProperties. Eventually this method should go away and a call
+   * to setCdataSectionElements(Vector v) should be made directly.
+   */
+  private void setCdataSectionElements(String key, Properties props)
+  {
+
+    String s = props.getProperty(key);
+
+    if (null != s)
+    {
+      // Vector of URI/LocalName pairs
+      Vector v = new Vector();
+      int l = s.length();
+      boolean inCurly = false;
+      StringBuffer buf = new StringBuffer();
+
+      // parse through string, breaking on whitespaces.  I do this instead
+      // of a tokenizer so I can track whitespace inside of curly brackets,
+      // which theoretically shouldn't happen if they contain legal URLs.
+      for (int i = 0; i < l; i++)
+      {
+        char c = s.charAt(i);
 
-        if (pushed)
+        if (Character.isWhitespace(c))
         {
-            /* Brian M.: don't know if we really needto do this. The
-             * callers of this object should have injected both
-             * startPrefixMapping and the attributes.  We are 
-             * just covering our butt here.
-             */
-            String name;
-            if (EMPTYSTRING.equals(prefix))
-            {
-                name = "xmlns";
-                addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false);
-            }
-            else
+          if (!inCurly)
+          {
+            if (buf.length() > 0)
             {
-                if (!EMPTYSTRING.equals(uri))
-                    // hack for XSLTC attribset16 test
-                { // that maps ns1 prefix to "" URI
-                    name = "xmlns:" + prefix;
-
-                    /* for something like xmlns:abc="w3.pretend.org"
-                     *  the      uri is the value, that is why we pass it in the
-                     * value, or 5th slot of addAttributeAlways()
-                     */
-                    addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false);
-                }
+              addCdataSectionElement(buf.toString(), v);
+              buf.setLength(0);
             }
+            continue;
+          }
         }
-        return pushed;
-    }
+        else if ('{' == c)
+          inCurly = true;
+        else if ('}' == c)
+          inCurly = false;
 
-    /**
-     * Receive notification of an XML comment anywhere in the document. This
-     * callback will be used for comments inside or outside the document
-     * element, including comments in the external DTD subset (if read).
-     * @param ch An array holding the characters in the comment.
-     * @param start The starting position in the array.
-     * @param length The number of characters to use from the array.
-     * @throws org.xml.sax.SAXException The application may raise an exception.
-     */
-    public void comment(char ch[], int start, int length)
-        throws org.xml.sax.SAXException
-    {
+        buf.append(c);
+      }
 
-        int start_old = start;
-        if (m_inEntityRef)
-            return;
-        if (m_elemContext.m_startTagOpen)
-        {
-            closeStartTag();
-            m_elemContext.m_startTagOpen = false;
-        }
-        else if (m_needToCallStartDocument)
+      if (buf.length() > 0)
+      {
+        addCdataSectionElement(buf.toString(), v);
+        buf.setLength(0);
+      }
+      // call the official, public method to set the collected names
+      setCdataSectionElements(v);
+    }
+
+  }
+
+  /**
+   * Adds a URI/LocalName pair of strings to the list.
+   *
+   * @param URI_and_localName String of the form "{uri}local" or "local" 
+   * 
+   * @return a QName object
+   */
+  private void addCdataSectionElement(String URI_and_localName, Vector v)
+  {
+
+    StringTokenizer tokenizer =
+        new StringTokenizer(URI_and_localName, "{}", false);
+    String s1 = tokenizer.nextToken();
+    String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+
+    if (null == s2)
+    {
+      // add null URI and the local name
+      v.addElement(null);
+      v.addElement(s1);
+    }
+    else
+    {
+      // add URI, then local name
+      v.addElement(s1);
+      v.addElement(s2);
+    }
+  }
+
+  /**
+   * Remembers the cdata sections specified in the cdata-section-elements.
+   * The "official way to set URI and localName pairs. 
+   * This method should be used by both Xalan and XSLTC.
+   * 
+   * @param URI_and_localNames a vector of pairs of Strings (URI/local)
+   */
+  public void setCdataSectionElements(Vector URI_and_localNames)
+  {
+    // convert to the new way.
+    if (URI_and_localNames != null)
+    {
+      final int len = URI_and_localNames.size() - 1;
+      if (len > 0)
+      {
+        final StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < len; i += 2)
+        {
+          // whitspace separated "{uri1}local1 {uri2}local2 ..."
+          if (i != 0)
+            sb.append(' ');
+          final String uri = (String) URI_and_localNames.elementAt(i);
+          final String localName =
+              (String) URI_and_localNames.elementAt(i + 1);
+          if (uri != null)
+          {
+            // If there is no URI don't put this in, just the localName then.
+            sb.append('{');
+            sb.append(uri);
+            sb.append('}');
+          }
+          sb.append(localName);
+        }
+        m_StringOfCDATASections = sb.toString();
+      }
+    }
+    initCdataElems(m_StringOfCDATASections);
+  }
+
+  /**
+   * Makes sure that the namespace URI for the given qualified attribute name
+   * is declared.
+   * @param ns the namespace URI
+   * @param localName the local name 
+   * @param rawName the qualified name 
+   * @return returns null if no action is taken, otherwise it returns the
+   * prefix used in declaring the namespace. 
+   * @throws SAXException if attempt to write declaration failed
+   */
+  protected String ensureAttributesNamespaceIsDeclared(
+      String ns,
+      String localName,
+      String rawName)
+          throws org.xml.sax.SAXException
+  {
+
+    if (ns != null && ns.length() > 0)
+    {
+
+      // extract the prefix in front of the raw name
+      int index = 0;
+      String prefixFromRawName =
+          (index = rawName.indexOf(":")) < 0
+          ? ""
+              : rawName.substring(0, index);
+
+      if (index > 0)
+      {
+        // we have a prefix, lets see if it maps to a namespace 
+        String uri = m_prefixMap.lookupNamespace(prefixFromRawName);
+        if (uri != null && uri.equals(ns))
         {
-            startDocumentInternal();
-            m_needToCallStartDocument = false;
+          // the prefix in the raw name is already maps to the given namespace uri
+          // so we don't need to do anything
+          return null;
         }
-
-        try
+        else
         {
-            final int limit = start + length;
-            boolean wasDash = false;
-            if (m_cdataTagOpen)
-                closeCDATA();
-            
-            if (shouldIndent())
-                indent();
-            
-            final java.io.Writer writer = m_writer;    
-            writer.write(COMMENT_BEGIN);
-            // Detect occurrences of two consecutive dashes, handle as necessary.
-            for (int i = start; i < limit; i++)
-            {
-                if (wasDash && ch[i] == '-')
-                {
-                    writer.write(ch, start, i - start);
-                    writer.write(" -");
-                    start = i + 1;
-                }
-                wasDash = (ch[i] == '-');
-            }
-
-            // if we have some chars in the comment
-            if (length > 0)
-            {
-                // Output the remaining characters (if any)
-                final int remainingChars = (limit - start);
-                if (remainingChars > 0)
-                    writer.write(ch, start, remainingChars);
-                // Protect comment end from a single trailing dash
-                if (ch[limit - 1] == '-')
-                    writer.write(' ');
-            }
-            writer.write(COMMENT_END);
+          // The uri does not map to the prefix in the raw name,
+          // so lets make the mapping.
+          this.startPrefixMapping(prefixFromRawName, ns, false);
+          this.addAttribute(
+              "http://www.w3.org/2000/xmlns/",
+              prefixFromRawName,
+              "xmlns:" + prefixFromRawName,
+              "CDATA",
+              ns, false);
+          return prefixFromRawName;
         }
-        catch (IOException e)
+      }
+      else
+      {
+        // we don't have a prefix in the raw name.
+        // Does the URI map to a prefix already?
+        String prefix = m_prefixMap.lookupPrefix(ns);
+        if (prefix == null)
         {
-            throw new SAXException(e);
+          // uri is not associated with a prefix,
+          // so lets generate a new prefix to use
+          prefix = m_prefixMap.generateNextPrefix();
+          this.startPrefixMapping(prefix, ns, false);
+          this.addAttribute(
+              "http://www.w3.org/2000/xmlns/",
+              prefix,
+              "xmlns:" + prefix,
+              "CDATA",
+              ns, false);
         }
 
-        /*
-         * Don't write out any indentation whitespace now,
-         * because there may be non-whitespace text after this.
-         * 
-         * Simply mark that at this point if we do decide
-         * to indent that we should 
-         * add a newline on the end of the current line before
-         * the indentation at the start of the next line.
-         */ 
-        m_startNewLine = true;
-        // time to generate comment event
-        if (m_tracer != null)
-            super.fireCommentEvent(ch, start_old,length);
-    }
+        return prefix;
 
-    /**
-     * Report the end of a CDATA section.
-     * @throws org.xml.sax.SAXException The application may raise an exception.
-     *
-     *  @see  #startCDATA
-     */
-    public void endCDATA() throws org.xml.sax.SAXException
-    {
-        if (m_cdataTagOpen)
-            closeCDATA();
-        m_cdataStartCalled = false;
+      }
     }
+    return null;
+  }
 
-    /**
-     * Report the end of DTD declarations.
-     * @throws org.xml.sax.SAXException The application may raise an exception.
-     * @see #startDTD
-     */
-    public void endDTD() throws org.xml.sax.SAXException
+  void ensurePrefixIsDeclared(String ns, String rawName)
+      throws org.xml.sax.SAXException
+  {
+
+    if (ns != null && ns.length() > 0)
     {
-        try
-        {
-            if (m_needToOutputDocTypeDecl)
-            {
-                outputDocTypeDecl(m_elemContext.m_elementName, false);
-                m_needToOutputDocTypeDecl = false;
-            }
-            final java.io.Writer writer = m_writer;
-            if (!m_inDoctype)
-                writer.write("]>");
-            else
-            {
-                writer.write('>');
-            }
+      int index;
+      final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
+      String prefix = (no_prefix) ? "" : rawName.substring(0, index);
 
-            writer.write(m_lineSep, 0, m_lineSepLen);
-        }
-        catch (IOException e)
+      if (null != prefix)
+      {
+        String foundURI = m_prefixMap.lookupNamespace(prefix);
+
+        if ((null == foundURI) || !foundURI.equals(ns))
         {
-            throw new SAXException(e);
-        }
+          this.startPrefixMapping(prefix, ns);
 
-    }
+          // Bugzilla1133: Generate attribute as well as namespace event.
+          // SAX does expect both.
 
-    /**
-     * End the scope of a prefix-URI Namespace mapping.
-     * @see org.xml.sax.ContentHandler#endPrefixMapping
-     * 
-     * @param prefix The prefix that was being mapping.
-     * @throws org.xml.sax.SAXException The client may throw
-     *            an exception during processing.
-     */
-    public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
-    { // do nothing
+          this.addAttributeAlways(
+              "http://www.w3.org/2000/xmlns/",
+              no_prefix ? "xmlns" : prefix,  // local name
+                  no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
+                      "CDATA",
+                      ns,
+                      false);
+        }
+
+      }
     }
+  }
 
-    /**
-     * Receive notification of ignorable whitespace in element content.
-     * 
-     * Not sure how to get this invoked quite yet.
-     * 
-     * @param ch The characters from the XML document.
-     * @param start The start position in the array.
-     * @param length The number of characters to read from the array.
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly
-     *            wrapping another exception.
-     * @see #characters
-     */
-    public void ignorableWhitespace(char ch[], int start, int length)
-        throws org.xml.sax.SAXException
+  /**
+   * This method flushes any pending events, which can be startDocument()
+   * closing the opening tag of an element, or closing an open CDATA section.
+   */
+  public void flushPending() throws SAXException
+  {
+    if (m_needToCallStartDocument)
     {
-
-        if (0 == length)
-            return;
-        characters(ch, start, length);
+      startDocumentInternal();
+      m_needToCallStartDocument = false;
     }
-
-    /**
-     * Receive notification of a skipped entity.
-     * @see org.xml.sax.ContentHandler#skippedEntity
-     * 
-     * @param name The name of the skipped entity.  If it is a
-     *       parameter                   entity, the name will begin with '%',
-     * and if it is the external DTD subset, it will be the string
-     * "[dtd]".
-     * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
-     * another exception.
-     */
-    public void skippedEntity(String name) throws org.xml.sax.SAXException
-    { // TODO: Should handle
+    if (m_elemContext.m_startTagOpen)
+    {
+      closeStartTag();
+      m_elemContext.m_startTagOpen = false;
     }
 
-    /**
-     * Report the start of a CDATA section.
-     * 
-     * @throws org.xml.sax.SAXException The application may raise an exception.
-     * @see #endCDATA
-     */
-    public void startCDATA() throws org.xml.sax.SAXException
+    if (m_cdataTagOpen)
     {
-        m_cdataStartCalled = true;
+      closeCDATA();
+      m_cdataTagOpen = false;
+    }
+    if (m_writer != null) {
+      try {
+        m_writer.flush();
+      }
+      catch(IOException e) {
+        // what? me worry?
+      }
     }
+  }
+
+  public void setContentHandler(ContentHandler ch)
+  {
+    // this method is really only useful in the ToSAXHandler classes but it is
+    // in the interface.  If the method defined here is ever called
+    // we are probably in trouble.
+  }
+
+  /**
+   * Adds the given attribute to the set of attributes, even if there is
+   * no currently open element. This is useful if a SAX startPrefixMapping()
+   * should need to add an attribute before the element name is seen.
+   * 
+   * This method is a copy of its super classes method, except that some
+   * tracing of events is done.  This is so the tracing is only done for
+   * stream serializers, not for SAX ones.
+   *
+   * @param uri the URI of the attribute
+   * @param localName the local name of the attribute
+   * @param rawName   the qualified name of the attribute
+   * @param type the type of the attribute (probably CDATA)
+   * @param value the value of the attribute
+   * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
+   * @return true if the attribute value was added, 
+   * false if the attribute already existed and the value was
+   * replaced with the new value.
+   */
+  public boolean addAttributeAlways(
+      String uri,
+      String localName,
+      String rawName,
+      String type,
+      String value,
+      boolean xslAttribute)
+  {
+    boolean was_added;
+    int index;
+    if (uri == null || localName == null || uri.length() == 0)
+      index = m_attributes.getIndex(rawName);
+    else {
+      index = m_attributes.getIndex(uri, localName);
+    }
+
+    if (index >= 0)
+    {
+      String old_value = null;
+      if (m_tracer != null)
+      {
+        old_value = m_attributes.getValue(index);
+        if (value.equals(old_value))
+          old_value = null;
+      }
 
-    /**
-     * Report the beginning of an entity.
-     * 
-     * The start and end of the document entity are not reported.
-     * The start and end of the external DTD subset are reported
-     * using the pseudo-name "[dtd]".  All other events must be
-     * properly nested within start/end entity events.
-     * 
-     * @param name The name of the entity.  If it is a parameter
-     *        entity, the name will begin with '%'.
-     * @throws org.xml.sax.SAXException The application may raise an exception.
-     * @see #endEntity
-     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
-     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
-     */
-    public void startEntity(String name) throws org.xml.sax.SAXException
+      /* We've seen the attribute before.
+       * We may have a null uri or localName, but all we really
+       * want to re-set is the value anyway.
+       */
+      m_attributes.setValue(index, value);
+      was_added = false;
+      if (old_value != null)
+        firePseudoAttributes();
+
+    }
+    else
     {
-        if (name.equals("[dtd]"))
-            m_inExternalDTD = true;
+      // the attribute doesn't exist yet, create it
+      if (xslAttribute)
+      {
+        /*
+         * This attribute is from an xsl:attribute element so we take some care in
+         * adding it, e.g.
+         *   <elem1  foo:attr1="1" xmlns:foo="uri1">
+         *       <xsl:attribute name="foo:attr2">2</xsl:attribute>
+         *   </elem1>
+         * 
+         * We are adding attr1 and attr2 both as attributes of elem1,
+         * and this code is adding attr2 (the xsl:attribute ).
+         * We could have a collision with the prefix like in the example above.
+         */
+
+        // In the example above, is there a prefix like foo ?
+        final int colonIndex = rawName.indexOf(':');
+        if (colonIndex > 0)
+        {
+          String prefix = rawName.substring(0,colonIndex);
+          NamespaceMappings.MappingRecord existing_mapping = m_prefixMap.getMappingFromPrefix(prefix);
+
+          /* Before adding this attribute (foo:attr2),
+           * is the prefix for it (foo) already mapped at the current depth?
+           */
+          if (existing_mapping != null 
+              && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth
+              && !existing_mapping.m_uri.equals(uri))
+          {
+            /*
+             * There is an existing mapping of this prefix,
+             * it differs from the one we need,
+             * and unfortunately it is at the current depth so we 
+             * can not over-ride it.
+             */
 
-        if (!m_expandDTDEntities && !m_inExternalDTD) {
-            /* Only leave the entity as-is if
-             * we've been told not to expand them
-             * and this is not the magic [dtd] name.
+            /*
+             * Are we lucky enough that an existing other prefix maps to this URI ?
              */
-            startNonEscaping();
-            characters("&" + name + ';');
-            endNonEscaping();
+            prefix = m_prefixMap.lookupPrefix(uri);
+            if (prefix == null)
+            {
+              /* Unfortunately there is no existing prefix that happens to map to ours,
+               * so to avoid a prefix collision we must generated a new prefix to use. 
+               * This is OK because the prefix URI mapping
+               * defined in the xsl:attribute is short in scope, 
+               * just the xsl:attribute element itself, 
+               * and at this point in serialization the body of the
+               * xsl:attribute, if any, is just a String. Right?
+               *   . . . I sure hope so - Brian M. 
+               */
+              prefix = m_prefixMap.generateNextPrefix();
+            }
+
+            rawName = prefix + ':' + localName;
+          }
         }
 
-        m_inEntityRef = true;
+        try
+        {
+          /* This is our last chance to make sure the namespace for this
+           * attribute is declared, especially if we just generated an alternate
+           * prefix to avoid a collision (the new prefix/rawName will go out of scope
+           * soon and be lost ...  last chance here.
+           */
+          String prefixUsed =
+              ensureAttributesNamespaceIsDeclared(
+                  uri,
+                  localName,
+                  rawName);
+        }
+        catch (SAXException e)
+        {
+          // TODO Auto-generated catch block
+          e.printStackTrace();
+        }
+      }
+      m_attributes.addAttribute(uri, localName, rawName, type, value);
+      was_added = true;
+      if (m_tracer != null)
+        firePseudoAttributes();
+    }
+    return was_added;
+  }
+
+  /**
+   * To fire off the pseudo characters of attributes, as they currently
+   * exist. This method should be called everytime an attribute is added,
+   * or when an attribute value is changed, or an element is created.
+   */
+
+  protected void firePseudoAttributes()
+  {
+    if (m_tracer != null)
+    {
+      try
+      {
+        // flush out the "<elemName" if not already flushed
+        m_writer.flush();
+
+        // make a StringBuffer to write the name="value" pairs to.
+        StringBuffer sb = new StringBuffer();
+        int nAttrs = m_attributes.getLength();
+        if (nAttrs > 0)
+        {
+          // make a writer that internally appends to the same
+          // StringBuffer
+          java.io.Writer writer =
+              new ToStream.WritertoStringBuffer(sb);
+
+          processAttributes(writer, nAttrs);
+          // Don't clear the attributes! 
+          // We only want to see what would be written out
+          // at this point, we don't want to loose them.
+        }
+        sb.append('>');  // the potential > after the attributes.
+        // convert the StringBuffer to a char array and
+        // emit the trace event that these characters "might"
+        // be written                
+        char ch[] = sb.toString().toCharArray();
+        m_tracer.fireGenerateEvent(
+            SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
+            ch,
+            0,
+            ch.length);                
+      }
+      catch (IOException ioe)
+      {
+        // ignore ?
+      }
+      catch (SAXException se)
+      {
+        // ignore ?
+      }
     }
+  }
 
+  /**
+   * This inner class is used only to collect attribute values
+   * written by the method writeAttrString() into a string buffer.
+   * In this manner trace events, and the real writing of attributes will use
+   * the same code.
+   */
+  private static class WritertoStringBuffer extends java.io.Writer
+  {
+    final private StringBuffer m_stringbuf;
     /**
-     * For the enclosing elements starting tag write out
-     * out any attributes followed by the &gt; character.
-     *
-     * @throws org.xml.sax.SAXException if underlying writer encounters error
+     * @see java.io.Writer#write(char[], int, int)
      */
-    protected void closeStartTag() throws SAXException
+    WritertoStringBuffer(StringBuffer sb)
     {
-
-        if (m_elemContext.m_startTagOpen)
-        {
-
-            try
-            {
-                if (m_tracer != null)
-                    super.fireStartElem(m_elemContext.m_elementName);
-                int nAttrs = m_attributes.getLength();
-                if (nAttrs > 0)
-                {
-                    processAttributes(m_writer, nAttrs);
-                    // clear attributes object for re-use with next element
-                    m_attributes.clear();
-                }
-                m_writer.write('>');
-            }
-            catch (IOException e)
-            {
-                throw new SAXException(e);
-            }
-
-            /* whether Xalan or XSLTC, we have the prefix mappings now, so
-             * lets determine if the current element is specified in the cdata-
-             * section-elements list.
-             */
-            if (m_CdataElems != null)
-                m_elemContext.m_isCdataSection = isCdataSection();
-
-            if (m_doIndent)
-            {
-                m_isprevtext = false;
-                m_preserves.push(m_ispreserve);
-            }
-        }
-
+      m_stringbuf = sb;
     }
 
+    public void write(char[] arg0, int arg1, int arg2) throws IOException
+    {
+      m_stringbuf.append(arg0, arg1, arg2);
+    }
     /**
-     * Report the start of DTD declarations, if any.
-     *
-     * Any declarations are assumed to be in the internal subset unless
-     * otherwise indicated.
-     * 
-     * @param name The document type name.
-     * @param publicId The declared public identifier for the
-     *        external DTD subset, or null if none was declared.
-     * @param systemId The declared system identifier for the
-     *        external DTD subset, or null if none was declared.
-     * @throws org.xml.sax.SAXException The application may raise an
-     *            exception.
-     * @see #endDTD
-     * @see #startEntity
+     * @see java.io.Writer#flush()
      */
-    public void startDTD(String name, String publicId, String systemId)
-        throws org.xml.sax.SAXException
+    public void flush() throws IOException
     {
-        setDoctypeSystem(systemId);
-        setDoctypePublic(publicId);
-
-        m_elemContext.m_elementName = name;
-        m_inDoctype = true;
     }
-
     /**
-     * Returns the m_indentAmount.
-     * @return int
+     * @see java.io.Writer#close()
      */
-    public int getIndentAmount()
+    public void close() throws IOException
     {
-        return m_indentAmount;
     }
 
-    /**
-     * Sets the m_indentAmount.
-     * 
-     * @param m_indentAmount The m_indentAmount to set
-     */
-    public void setIndentAmount(int m_indentAmount)
+    public void write(int i)
     {
-        this.m_indentAmount = m_indentAmount;
+      m_stringbuf.append((char) i);
     }
 
-    /**
-     * Tell if, based on space preservation constraints and the doIndent property,
-     * if an indent should occur.
-     *
-     * @return True if an indent should occur.
-     */
-    protected boolean shouldIndent()
+    public void write(String s)
     {
-        return m_doIndent && (!m_ispreserve && !m_isprevtext) && m_elemContext.m_currentElemDepth > 0;
+      m_stringbuf.append(s);
     }
+  }
 
-    /**
-     * Searches for the list of qname properties with the specified key in the
-     * property list. If the key is not found in this property list, the default
-     * property list, and its defaults, recursively, are then checked. The
-     * method returns <code>null</code> if the property is not found.
-     *
-     * @param   key   the property key.
-     * @param props the list of properties to search in.
-     * 
-     * Sets the vector of local-name/URI pairs of the cdata section elements
-     * specified in the cdata-section-elements property.
+  /**
+   * @see SerializationHandler#setTransformer(Transformer)
+   */
+  public void setTransformer(Transformer transformer) {
+    super.setTransformer(transformer);
+    if (m_tracer != null
+        && !(m_writer instanceof SerializerTraceWriter)  )
+      setWriterInternal(new SerializerTraceWriter(m_writer, m_tracer), false);        
+
+
+  }
+  /**
+   * Try's to reset the super class and reset this class for 
+   * re-use, so that you don't need to create a new serializer 
+   * (mostly for performance reasons).
+   * 
+   * @return true if the class was successfuly reset.
+   */
+  public boolean reset()
+  {
+    boolean wasReset = false;
+    if (super.reset())
+    {
+      resetToStream();
+      wasReset = true;
+    }
+    return wasReset;
+  }
+
+  /**
+   * Reset all of the fields owned by ToStream class
+   *
+   */
+  private void resetToStream()
+  {
+    this.m_cdataStartCalled = false;
+    /* The stream is being reset. It is one of
+     * ToXMLStream, ToHTMLStream ... and this type can't be changed
+     * so neither should m_charInfo which is associated with the
+     * type of Stream. Just leave m_charInfo as-is for the next re-use.
      * 
-     * This method is essentially a copy of getQNameProperties() from
-     * OutputProperties. Eventually this method should go away and a call
-     * to setCdataSectionElements(Vector v) should be made directly.
      */
-    private void setCdataSectionElements(String key, Properties props)
-    {
+    // this.m_charInfo = null; // don't set to null 
+    this.m_disableOutputEscapingStates.clear();
+    // this.m_encodingInfo = null; // don't set to null
 
-        String s = props.getProperty(key);
+    this.m_escaping = true;
+    // Leave m_format alone for now - Brian M.
+    // this.m_format = null;
+    this.m_expandDTDEntities = true; 
+    this.m_inDoctype = false;
+    this.m_ispreserve = false;
+    this.m_isprevtext = false;
+    this.m_isUTF8 = false; //  ?? used anywhere ??
+    this.m_lineSep = s_systemLineSep;
+    this.m_lineSepLen = s_systemLineSep.length;
+    this.m_lineSepUse = true;
+    // this.m_outputStream = null; // Don't reset it may be re-used
+    this.m_preserves.clear();
+    this.m_shouldFlush = true;
+    this.m_spaceBeforeClose = false;
+    this.m_startNewLine = false;
+    this.m_writer_set_by_user = false;
+  }        
 
-        if (null != s)
-        {
-            // Vector of URI/LocalName pairs
-            Vector v = new Vector();
-            int l = s.length();
-            boolean inCurly = false;
-            StringBuffer buf = new StringBuffer();
-
-            // parse through string, breaking on whitespaces.  I do this instead
-            // of a tokenizer so I can track whitespace inside of curly brackets,
-            // which theoretically shouldn't happen if they contain legal URLs.
-            for (int i = 0; i < l; i++)
-            {
-                char c = s.charAt(i);
-
-                if (Character.isWhitespace(c))
-                {
-                    if (!inCurly)
-                    {
-                        if (buf.length() > 0)
-                        {
-                            addCdataSectionElement(buf.toString(), v);
-                            buf.setLength(0);
-                        }
-                        continue;
-                    }
-                }
-                else if ('{' == c)
-                    inCurly = true;
-                else if ('}' == c)
-                    inCurly = false;
+  /**
+   * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
+   * @param encoding the character encoding
+   */
+  public void setEncoding(String encoding)
+  {
+    setOutputProperty(OutputKeys.ENCODING,encoding);
+  }
 
-                buf.append(c);
-            }
+  /**
+   * Simple stack for boolean values.
+   * 
+   * This class is a copy of the one in org.apache.xml.utils. 
+   * It exists to cut the serializers dependancy on that package.
+   * A minor changes from that package are:
+   * doesn't implement Clonable
+   *  
+   * @xsl.usage internal
+   */
+  static final class BoolStack
+  {
 
-            if (buf.length() > 0)
-            {
-                addCdataSectionElement(buf.toString(), v);
-                buf.setLength(0);
-            }
-            // call the official, public method to set the collected names
-            setCdataSectionElements(v);
-        }
+    /** Array of boolean values          */
+    private boolean m_values[];
+
+    /** Array size allocated           */
+    private int m_allocatedSize;
 
+    /** Index into the array of booleans          */
+    private int m_index;
+
+    /**
+     * Default constructor.  Note that the default
+     * block size is very small, for small lists.
+     */
+    public BoolStack()
+    {
+      this(32);
     }
 
     /**
-     * Adds a URI/LocalName pair of strings to the list.
+     * Construct a IntVector, using the given block size.
      *
-     * @param URI_and_localName String of the form "{uri}local" or "local" 
-     * 
-     * @return a QName object
+     * @param size array size to allocate
      */
-    private void addCdataSectionElement(String URI_and_localName, Vector v)
+    public BoolStack(int size)
     {
 
-        StringTokenizer tokenizer =
-            new StringTokenizer(URI_and_localName, "{}", false);
-        String s1 = tokenizer.nextToken();
-        String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
-
-        if (null == s2)
-        {
-            // add null URI and the local name
-            v.addElement(null);
-            v.addElement(s1);
-        }
-        else
-        {
-            // add URI, then local name
-            v.addElement(s1);
-            v.addElement(s2);
-        }
+      m_allocatedSize = size;
+      m_values = new boolean[size];
+      m_index = -1;
     }
 
     /**
-     * Remembers the cdata sections specified in the cdata-section-elements.
-     * The "official way to set URI and localName pairs. 
-     * This method should be used by both Xalan and XSLTC.
-     * 
-     * @param URI_and_localNames a vector of pairs of Strings (URI/local)
+     * Get the length of the list.
+     *
+     * @return Current length of the list
      */
-    public void setCdataSectionElements(Vector URI_and_localNames)
+    public final int size()
     {
-        // convert to the new way.
-        if (URI_and_localNames != null)
-        {
-            final int len = URI_and_localNames.size() - 1;
-            if (len > 0)
-            {
-                final StringBuffer sb = new StringBuffer();
-                for (int i = 0; i < len; i += 2)
-                {
-                    // whitspace separated "{uri1}local1 {uri2}local2 ..."
-                    if (i != 0)
-                        sb.append(' ');
-                    final String uri = (String) URI_and_localNames.elementAt(i);
-                    final String localName =
-                        (String) URI_and_localNames.elementAt(i + 1);
-                    if (uri != null)
-                    {
-                        // If there is no URI don't put this in, just the localName then.
-                        sb.append('{');
-                        sb.append(uri);
-                        sb.append('}');
-                    }
-                    sb.append(localName);
-                }
-                m_StringOfCDATASections = sb.toString();
-            }
-        }
-        initCdataElems(m_StringOfCDATASections);
+      return m_index + 1;
     }
 
     /**
-     * Makes sure that the namespace URI for the given qualified attribute name
-     * is declared.
-     * @param ns the namespace URI
-     * @param localName the local name 
-     * @param rawName the qualified name 
-     * @return returns null if no action is taken, otherwise it returns the
-     * prefix used in declaring the namespace. 
-     * @throws SAXException if attempt to write declaration failed
+     * Clears the stack.
+     *
      */
-    protected String ensureAttributesNamespaceIsDeclared(
-        String ns,
-        String localName,
-        String rawName)
-        throws org.xml.sax.SAXException
+    public final void clear()
     {
-
-        if (ns != null && ns.length() > 0)
-        {
-
-            // extract the prefix in front of the raw name
-            int index = 0;
-            String prefixFromRawName =
-                (index = rawName.indexOf(":")) < 0
-                    ? ""
-                    : rawName.substring(0, index);
-
-            if (index > 0)
-            {
-                // we have a prefix, lets see if it maps to a namespace 
-                String uri = m_prefixMap.lookupNamespace(prefixFromRawName);
-                if (uri != null && uri.equals(ns))
-                {
-                    // the prefix in the raw name is already maps to the given namespace uri
-                    // so we don't need to do anything
-                    return null;
-                }
-                else
-                {
-                    // The uri does not map to the prefix in the raw name,
-                    // so lets make the mapping.
-                    this.startPrefixMapping(prefixFromRawName, ns, false);
-                    this.addAttribute(
-                        "http://www.w3.org/2000/xmlns/",
-                        prefixFromRawName,
-                        "xmlns:" + prefixFromRawName,
-                        "CDATA",
-                        ns, false);
-                    return prefixFromRawName;
-                }
-            }
-            else
-            {
-                // we don't have a prefix in the raw name.
-                // Does the URI map to a prefix already?
-                String prefix = m_prefixMap.lookupPrefix(ns);
-                if (prefix == null)
-                {
-                    // uri is not associated with a prefix,
-                    // so lets generate a new prefix to use
-                    prefix = m_prefixMap.generateNextPrefix();
-                    this.startPrefixMapping(prefix, ns, false);
-                    this.addAttribute(
-                        "http://www.w3.org/2000/xmlns/",
-                        prefix,
-                        "xmlns:" + prefix,
-                        "CDATA",
-                        ns, false);
-                }
-
-                return prefix;
-
-            }
-        }
-        return null;
+      m_index = -1;
     }
 
-    void ensurePrefixIsDeclared(String ns, String rawName)
-        throws org.xml.sax.SAXException
+    /**
+     * Pushes an item onto the top of this stack.
+     *
+     *
+     * @param val the boolean to be pushed onto this stack.
+     * @return  the <code>item</code> argument.
+     */
+    public final boolean push(boolean val)
     {
 
-        if (ns != null && ns.length() > 0)
-        {
-            int index;
-            final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
-            String prefix = (no_prefix) ? "" : rawName.substring(0, index);
-
-            if (null != prefix)
-            {
-                String foundURI = m_prefixMap.lookupNamespace(prefix);
-
-                if ((null == foundURI) || !foundURI.equals(ns))
-                {
-                    this.startPrefixMapping(prefix, ns);
-
-                    // Bugzilla1133: Generate attribute as well as namespace event.
-                    // SAX does expect both.
-
-                    this.addAttributeAlways(
-                        "http://www.w3.org/2000/xmlns/",
-                        no_prefix ? "xmlns" : prefix,  // local name
-                        no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
-                        "CDATA",
-                        ns,
-                        false);
-                }
+      if (m_index == m_allocatedSize - 1)
+        grow();
 
-            }
-        }
+      return (m_values[++m_index] = val);
     }
 
     /**
-     * This method flushes any pending events, which can be startDocument()
-     * closing the opening tag of an element, or closing an open CDATA section.
+     * Removes the object at the top of this stack and returns that
+     * object as the value of this function.
+     *
+     * @return     The object at the top of this stack.
+     * @throws  EmptyStackException  if this stack is empty.
      */
-    public void flushPending() throws SAXException
-    {
-            if (m_needToCallStartDocument)
-            {
-                startDocumentInternal();
-                m_needToCallStartDocument = false;
-            }
-            if (m_elemContext.m_startTagOpen)
-            {
-                closeStartTag();
-                m_elemContext.m_startTagOpen = false;
-            }
-
-            if (m_cdataTagOpen)
-            {
-                closeCDATA();
-                m_cdataTagOpen = false;
-            }
-            if (m_writer != null) {
-                try {
-                    m_writer.flush();
-                }
-                catch(IOException e) {
-                    // what? me worry?
-                }
-            }
-    }
-
-    public void setContentHandler(ContentHandler ch)
+    public final boolean pop()
     {
-        // this method is really only useful in the ToSAXHandler classes but it is
-        // in the interface.  If the method defined here is ever called
-        // we are probably in trouble.
+      return m_values[m_index--];
     }
 
     /**
-     * Adds the given attribute to the set of attributes, even if there is
-     * no currently open element. This is useful if a SAX startPrefixMapping()
-     * should need to add an attribute before the element name is seen.
-     * 
-     * This method is a copy of its super classes method, except that some
-     * tracing of events is done.  This is so the tracing is only done for
-     * stream serializers, not for SAX ones.
+     * Removes the object at the top of this stack and returns the
+     * next object at the top as the value of this function.
      *
-     * @param uri the URI of the attribute
-     * @param localName the local name of the attribute
-     * @param rawName   the qualified name of the attribute
-     * @param type the type of the attribute (probably CDATA)
-     * @param value the value of the attribute
-     * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
-     * @return true if the attribute value was added, 
-     * false if the attribute already existed and the value was
-     * replaced with the new value.
+     *
+     * @return Next object to the top or false if none there
      */
-    public boolean addAttributeAlways(
-        String uri,
-        String localName,
-        String rawName,
-        String type,
-        String value,
-        boolean xslAttribute)
-    {
-        boolean was_added;
-        int index;
-        if (uri == null || localName == null || uri.length() == 0)
-            index = m_attributes.getIndex(rawName);
-        else {
-            index = m_attributes.getIndex(uri, localName);
-        }
-
-        if (index >= 0)
-        {
-            String old_value = null;
-            if (m_tracer != null)
-            {
-                old_value = m_attributes.getValue(index);
-                if (value.equals(old_value))
-                    old_value = null;
-            }
-
-            /* We've seen the attribute before.
-             * We may have a null uri or localName, but all we really
-             * want to re-set is the value anyway.
-             */
-            m_attributes.setValue(index, value);
-            was_added = false;
-            if (old_value != null)
-                firePseudoAttributes();
+    public final boolean popAndTop()
+    {
 
-        }
-        else
-        {
-            // the attribute doesn't exist yet, create it
-            if (xslAttribute)
-            {
-                /*
-                 * This attribute is from an xsl:attribute element so we take some care in
-                 * adding it, e.g.
-                 *   <elem1  foo:attr1="1" xmlns:foo="uri1">
-                 *       <xsl:attribute name="foo:attr2">2</xsl:attribute>
-                 *   </elem1>
-                 * 
-                 * We are adding attr1 and attr2 both as attributes of elem1,
-                 * and this code is adding attr2 (the xsl:attribute ).
-                 * We could have a collision with the prefix like in the example above.
-                 */
-
-                // In the example above, is there a prefix like foo ?
-                final int colonIndex = rawName.indexOf(':');
-                if (colonIndex > 0)
-                {
-                    String prefix = rawName.substring(0,colonIndex);
-                    NamespaceMappings.MappingRecord existing_mapping = m_prefixMap.getMappingFromPrefix(prefix);
-
-                    /* Before adding this attribute (foo:attr2),
-                     * is the prefix for it (foo) already mapped at the current depth?
-                     */
-                    if (existing_mapping != null 
-                    && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth
-                    && !existing_mapping.m_uri.equals(uri))
-                    {
-                        /*
-                         * There is an existing mapping of this prefix,
-                         * it differs from the one we need,
-                         * and unfortunately it is at the current depth so we 
-                         * can not over-ride it.
-                         */
-
-                        /*
-                         * Are we lucky enough that an existing other prefix maps to this URI ?
-                         */
-                        prefix = m_prefixMap.lookupPrefix(uri);
-                        if (prefix == null)
-                        {
-                            /* Unfortunately there is no existing prefix that happens to map to ours,
-                             * so to avoid a prefix collision we must generated a new prefix to use. 
-                             * This is OK because the prefix URI mapping
-                             * defined in the xsl:attribute is short in scope, 
-                             * just the xsl:attribute element itself, 
-                             * and at this point in serialization the body of the
-                             * xsl:attribute, if any, is just a String. Right?
-                             *   . . . I sure hope so - Brian M. 
-                             */
-                            prefix = m_prefixMap.generateNextPrefix();
-                        }
-
-                        rawName = prefix + ':' + localName;
-                    }
-                }
+      m_index--;
 
-                try
-                {
-                    /* This is our last chance to make sure the namespace for this
-                     * attribute is declared, especially if we just generated an alternate
-                     * prefix to avoid a collision (the new prefix/rawName will go out of scope
-                     * soon and be lost ...  last chance here.
-                     */
-                    String prefixUsed =
-                        ensureAttributesNamespaceIsDeclared(
-                            uri,
-                            localName,
-                            rawName);
-                }
-                catch (SAXException e)
-                {
-                    // TODO Auto-generated catch block
-                    e.printStackTrace();
-                }
-            }
-            m_attributes.addAttribute(uri, localName, rawName, type, value);
-            was_added = true;
-            if (m_tracer != null)
-                firePseudoAttributes();
-        }
-        return was_added;
+      return (m_index >= 0) ? m_values[m_index] : false;
     }
 
     /**
-     * To fire off the pseudo characters of attributes, as they currently
-     * exist. This method should be called everytime an attribute is added,
-     * or when an attribute value is changed, or an element is created.
+     * Set the item at the top of this stack  
+     *
+     *
+     * @param b Object to set at the top of this stack
      */
-
-    protected void firePseudoAttributes()
+    public final void setTop(boolean b)
     {
-        if (m_tracer != null)
-        {
-            try
-            {
-                // flush out the "<elemName" if not already flushed
-                m_writer.flush();
-                
-                // make a StringBuffer to write the name="value" pairs to.
-                StringBuffer sb = new StringBuffer();
-                int nAttrs = m_attributes.getLength();
-                if (nAttrs > 0)
-                {
-                    // make a writer that internally appends to the same
-                    // StringBuffer
-                    java.io.Writer writer =
-                        new ToStream.WritertoStringBuffer(sb);
-
-                    processAttributes(writer, nAttrs);
-                    // Don't clear the attributes! 
-                    // We only want to see what would be written out
-                    // at this point, we don't want to loose them.
-                }
-                sb.append('>');  // the potential > after the attributes.
-                // convert the StringBuffer to a char array and
-                // emit the trace event that these characters "might"
-                // be written                
-                char ch[] = sb.toString().toCharArray();
-                m_tracer.fireGenerateEvent(
-                    SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
-                    ch,
-                    0,
-                    ch.length);                
-            }
-            catch (IOException ioe)
-            {
-                // ignore ?
-            }
-            catch (SAXException se)
-            {
-                // ignore ?
-            }
-        }
+      m_values[m_index] = b;
     }
 
     /**
-     * This inner class is used only to collect attribute values
-     * written by the method writeAttrString() into a string buffer.
-     * In this manner trace events, and the real writing of attributes will use
-     * the same code.
+     * Looks at the object at the top of this stack without removing it
+     * from the stack.
+     *
+     * @return     the object at the top of this stack.
+     * @throws  EmptyStackException  if this stack is empty.
      */
-    private static class WritertoStringBuffer extends java.io.Writer
+    public final boolean peek()
     {
-        final private StringBuffer m_stringbuf;
-        /**
-         * @see java.io.Writer#write(char[], int, int)
-         */
-        WritertoStringBuffer(StringBuffer sb)
-        {
-            m_stringbuf = sb;
-        }
-
-        public void write(char[] arg0, int arg1, int arg2) throws IOException
-        {
-            m_stringbuf.append(arg0, arg1, arg2);
-        }
-        /**
-         * @see java.io.Writer#flush()
-         */
-        public void flush() throws IOException
-        {
-        }
-        /**
-         * @see java.io.Writer#close()
-         */
-        public void close() throws IOException
-        {
-        }
-
-        public void write(int i)
-        {
-            m_stringbuf.append((char) i);
-        }
-        
-        public void write(String s)
-        {
-            m_stringbuf.append(s);
-        }
+      return m_values[m_index];
     }
 
     /**
-     * @see SerializationHandler#setTransformer(Transformer)
+     * Looks at the object at the top of this stack without removing it
+     * from the stack.  If the stack is empty, it returns false.
+     *
+     * @return     the object at the top of this stack.
      */
-    public void setTransformer(Transformer transformer) {
-        super.setTransformer(transformer);
-        if (m_tracer != null
-         && !(m_writer instanceof SerializerTraceWriter)  )
-            setWriterInternal(new SerializerTraceWriter(m_writer, m_tracer), false);        
-        
-        
+    public final boolean peekOrFalse()
+    {
+      return (m_index > -1) ? m_values[m_index] : false;
     }
+
     /**
-     * Try's to reset the super class and reset this class for 
-     * re-use, so that you don't need to create a new serializer 
-     * (mostly for performance reasons).
-     * 
-     * @return true if the class was successfuly reset.
+     * Looks at the object at the top of this stack without removing it
+     * from the stack.  If the stack is empty, it returns true.
+     *
+     * @return     the object at the top of this stack.
      */
-    public boolean reset()
+    public final boolean peekOrTrue()
     {
-        boolean wasReset = false;
-        if (super.reset())
-        {
-            resetToStream();
-            wasReset = true;
-        }
-        return wasReset;
+      return (m_index > -1) ? m_values[m_index] : true;
     }
-    
+
     /**
-     * Reset all of the fields owned by ToStream class
+     * Tests if this stack is empty.
      *
+     * @return  <code>true</code> if this stack is empty;
+     *          <code>false</code> otherwise.
      */
-    private void resetToStream()
-    {
-         this.m_cdataStartCalled = false;
-         /* The stream is being reset. It is one of
-          * ToXMLStream, ToHTMLStream ... and this type can't be changed
-          * so neither should m_charInfo which is associated with the
-          * type of Stream. Just leave m_charInfo as-is for the next re-use.
-          * 
-          */
-         // this.m_charInfo = null; // don't set to null 
-         this.m_disableOutputEscapingStates.clear();
-         // this.m_encodingInfo = null; // don't set to null
-         
-         this.m_escaping = true;
-         // Leave m_format alone for now - Brian M.
-         // this.m_format = null;
-         this.m_expandDTDEntities = true; 
-         this.m_inDoctype = false;
-         this.m_ispreserve = false;
-         this.m_isprevtext = false;
-         this.m_isUTF8 = false; //  ?? used anywhere ??
-         this.m_lineSep = s_systemLineSep;
-         this.m_lineSepLen = s_systemLineSep.length;
-         this.m_lineSepUse = true;
-         // this.m_outputStream = null; // Don't reset it may be re-used
-         this.m_preserves.clear();
-         this.m_shouldFlush = true;
-         this.m_spaceBeforeClose = false;
-         this.m_startNewLine = false;
-         this.m_writer_set_by_user = false;
-    }        
-    
-    /**
-      * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
-      * @param encoding the character encoding
-      */
-     public void setEncoding(String encoding)
-     {
-         setOutputProperty(OutputKeys.ENCODING,encoding);
-     }
-     
+    public boolean isEmpty()
+    {
+      return (m_index == -1);
+    }
+
     /**
-     * Simple stack for boolean values.
-     * 
-     * This class is a copy of the one in org.apache.xml.utils. 
-     * It exists to cut the serializers dependancy on that package.
-     * A minor changes from that package are:
-     * doesn't implement Clonable
-     *  
-     * @xsl.usage internal
+     * Grows the size of the stack
+     *
      */
-    static final class BoolStack
+    private void grow()
     {
 
-      /** Array of boolean values          */
-      private boolean m_values[];
-
-      /** Array size allocated           */
-      private int m_allocatedSize;
-
-      /** Index into the array of booleans          */
-      private int m_index;
-
-      /**
-       * Default constructor.  Note that the default
-       * block size is very small, for small lists.
-       */
-      public BoolStack()
-      {
-        this(32);
-      }
-
-      /**
-       * Construct a IntVector, using the given block size.
-       *
-       * @param size array size to allocate
-       */
-      public BoolStack(int size)
-      {
-
-        m_allocatedSize = size;
-        m_values = new boolean[size];
-        m_index = -1;
-      }
-
-      /**
-       * Get the length of the list.
-       *
-       * @return Current length of the list
-       */
-      public final int size()
-      {
-        return m_index + 1;
-      }
-
-      /**
-       * Clears the stack.
-       *
-       */
-      public final void clear()
-      {
-        m_index = -1;
-      }
-
-      /**
-       * Pushes an item onto the top of this stack.
-       *
-       *
-       * @param val the boolean to be pushed onto this stack.
-       * @return  the <code>item</code> argument.
-       */
-      public final boolean push(boolean val)
-      {
-
-        if (m_index == m_allocatedSize - 1)
-          grow();
+      m_allocatedSize *= 2;
 
-        return (m_values[++m_index] = val);
-      }
-
-      /**
-       * Removes the object at the top of this stack and returns that
-       * object as the value of this function.
-       *
-       * @return     The object at the top of this stack.
-       * @throws  EmptyStackException  if this stack is empty.
-       */
-      public final boolean pop()
-      {
-        return m_values[m_index--];
-      }
-
-      /**
-       * Removes the object at the top of this stack and returns the
-       * next object at the top as the value of this function.
-       *
-       *
-       * @return Next object to the top or false if none there
-       */
-      public final boolean popAndTop()
-      {
+      boolean newVector[] = new boolean[m_allocatedSize];
 
-        m_index--;
+      System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
 
-        return (m_index >= 0) ? m_values[m_index] : false;
-      }
+      m_values = newVector;
+    }
+  }
 
-      /**
-       * Set the item at the top of this stack  
-       *
-       *
-       * @param b Object to set at the top of this stack
-       */
-      public final void setTop(boolean b)
-      {
-        m_values[m_index] = b;
-      }
+  // Implement DTDHandler
+  /**
+   * If this method is called, the serializer is used as a
+   * DTDHandler, which changes behavior how the serializer 
+   * handles document entities. 
+   * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
+   */
+  public void notationDecl(String name, String pubID, String sysID) throws SAXException {
+    // TODO Auto-generated method stub
+    try {
+      DTDprolog();
 
-      /**
-       * Looks at the object at the top of this stack without removing it
-       * from the stack.
-       *
-       * @return     the object at the top of this stack.
-       * @throws  EmptyStackException  if this stack is empty.
-       */
-      public final boolean peek()
-      {
-        return m_values[m_index];
-      }
+      m_writer.write("<!NOTATION ");            
+      m_writer.write(name);
+      if (pubID != null) {
+        m_writer.write(" PUBLIC \"");
+        m_writer.write(pubID);
 
-      /**
-       * Looks at the object at the top of this stack without removing it
-       * from the stack.  If the stack is empty, it returns false.
-       *
-       * @return     the object at the top of this stack.
-       */
-      public final boolean peekOrFalse()
-      {
-        return (m_index > -1) ? m_values[m_index] : false;
       }
-
-      /**
-       * Looks at the object at the top of this stack without removing it
-       * from the stack.  If the stack is empty, it returns true.
-       *
-       * @return     the object at the top of this stack.
-       */
-      public final boolean peekOrTrue()
-      {
-        return (m_index > -1) ? m_values[m_index] : true;
+      else {
+        m_writer.write(" SYSTEM \"");
+        m_writer.write(sysID);
       }
+      m_writer.write("\" >");
+      m_writer.write(m_lineSep, 0, m_lineSepLen);
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * If this method is called, the serializer is used as a
+   * DTDHandler, which changes behavior how the serializer 
+   * handles document entities. 
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+   */
+  public void unparsedEntityDecl(String name, String pubID, String sysID, String notationName) throws SAXException {
+    // TODO Auto-generated method stub
+    try {
+      DTDprolog();       
+
+      m_writer.write("<!ENTITY ");            
+      m_writer.write(name);
+      if (pubID != null) {
+        m_writer.write(" PUBLIC \"");
+        m_writer.write(pubID);
 
-      /**
-       * Tests if this stack is empty.
-       *
-       * @return  <code>true</code> if this stack is empty;
-       *          <code>false</code> otherwise.
-       */
-      public boolean isEmpty()
-      {
-        return (m_index == -1);
       }
-
-      /**
-       * Grows the size of the stack
-       *
-       */
-      private void grow()
-      {
-
-        m_allocatedSize *= 2;
-
-        boolean newVector[] = new boolean[m_allocatedSize];
-
-        System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
-
-        m_values = newVector;
+      else {
+        m_writer.write(" SYSTEM \"");
+        m_writer.write(sysID);
       }
-    }
-    
-    // Implement DTDHandler
-    /**
-     * If this method is called, the serializer is used as a
-     * DTDHandler, which changes behavior how the serializer 
-     * handles document entities. 
-     * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
-     */
-    public void notationDecl(String name, String pubID, String sysID) throws SAXException {
-        // TODO Auto-generated method stub
-        try {
-            DTDprolog();
-            
-            m_writer.write("<!NOTATION ");            
-            m_writer.write(name);
-            if (pubID != null) {
-                m_writer.write(" PUBLIC \"");
-                m_writer.write(pubID);
-  
-            }
-            else {
-                m_writer.write(" SYSTEM \"");
-                m_writer.write(sysID);
-            }
-            m_writer.write("\" >");
-            m_writer.write(m_lineSep, 0, m_lineSepLen);
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * If this method is called, the serializer is used as a
-     * DTDHandler, which changes behavior how the serializer 
-     * handles document entities. 
-     * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
-     */
-    public void unparsedEntityDecl(String name, String pubID, String sysID, String notationName) throws SAXException {
-        // TODO Auto-generated method stub
-        try {
-            DTDprolog();       
-            
-            m_writer.write("<!ENTITY ");            
-            m_writer.write(name);
-            if (pubID != null) {
-                m_writer.write(" PUBLIC \"");
-                m_writer.write(pubID);
-  
-            }
-            else {
-                m_writer.write(" SYSTEM \"");
-                m_writer.write(sysID);
-            }
-            m_writer.write("\" NDATA ");
-            m_writer.write(notationName);
-            m_writer.write(" >");
-            m_writer.write(m_lineSep, 0, m_lineSepLen);
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }        
-    }
-    
-    /**
-     * A private helper method to output the DTD
-     * @throws SAXException never (I think)
-     * @throws IOException if an error occurs while writing
-     */
-    private void DTDprolog() throws SAXException, IOException {
-        final java.io.Writer writer = m_writer;
-        if (m_needToOutputDocTypeDecl)
-        {
-            outputDocTypeDecl(m_elemContext.m_elementName, false);
-            m_needToOutputDocTypeDecl = false;
-        }
-        if (m_inDoctype)
-        {
-            writer.write(" [");
-            writer.write(m_lineSep, 0, m_lineSepLen);
-            m_inDoctype = false;
-        }
-    }
-    
-    /**
-     * If set to false the serializer does not expand DTD entities,
-     * but leaves them as is, the default value is true;
-     */
-    public void setDTDEntityExpansion(boolean expand) { 
-        m_expandDTDEntities = expand;     
-    }
-        
-    /**
-     * Sets the end of line characters to be used during serialization
-     * @param eolChars A character array corresponding to the characters to be used.
-     */    
-    public void setNewLine (char[] eolChars) {
-        m_lineSep = eolChars;
-        m_lineSepLen = eolChars.length;
-    }
-
-    /**
-     * Remembers the cdata sections specified in the cdata-section-elements by appending the given
-     * cdata section elements to the list. This method can be called multiple times, but once an
-     * element is put in the list of cdata section elements it can not be removed.
-     * This method should be used by both Xalan and XSLTC.
-     * 
-     * @param URI_and_localNames a whitespace separated list of element names, each element
-     * is a URI in curly braces (optional) and a local name. An example of such a parameter is:
-     * "{http://company.com}price {myURI2}book chapter"
-     */
-    public void addCdataSectionElements(String URI_and_localNames)
-    {
-        if (URI_and_localNames != null)
-            initCdataElems(URI_and_localNames);
-        if (m_StringOfCDATASections == null)
-            m_StringOfCDATASections = URI_and_localNames;
-        else
-            m_StringOfCDATASections += (" " + URI_and_localNames);
-    }
+      m_writer.write("\" NDATA ");
+      m_writer.write(notationName);
+      m_writer.write(" >");
+      m_writer.write(m_lineSep, 0, m_lineSepLen);
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }        
+  }
+
+  /**
+   * A private helper method to output the DTD
+   * @throws SAXException never (I think)
+   * @throws IOException if an error occurs while writing
+   */
+  private void DTDprolog() throws SAXException, IOException {
+    final java.io.Writer writer = m_writer;
+    if (m_needToOutputDocTypeDecl)
+    {
+      outputDocTypeDecl(m_elemContext.m_elementName, false);
+      m_needToOutputDocTypeDecl = false;
+    }
+    if (m_inDoctype)
+    {
+      writer.write(" [");
+      writer.write(m_lineSep, 0, m_lineSepLen);
+      m_inDoctype = false;
+    }
+  }
+
+  /**
+   * If set to false the serializer does not expand DTD entities,
+   * but leaves them as is, the default value is true;
+   */
+  public void setDTDEntityExpansion(boolean expand) { 
+    m_expandDTDEntities = expand;     
+  }
+
+  /**
+   * Sets the end of line characters to be used during serialization
+   * @param eolChars A character array corresponding to the characters to be used.
+   */    
+  public void setNewLine (char[] eolChars) {
+    m_lineSep = eolChars;
+    m_lineSepLen = eolChars.length;
+  }
+
+  /**
+   * Remembers the cdata sections specified in the cdata-section-elements by appending the given
+   * cdata section elements to the list. This method can be called multiple times, but once an
+   * element is put in the list of cdata section elements it can not be removed.
+   * This method should be used by both Xalan and XSLTC.
+   * 
+   * @param URI_and_localNames a whitespace separated list of element names, each element
+   * is a URI in curly braces (optional) and a local name. An example of such a parameter is:
+   * "{http://company.com}price {myURI2}book chapter"
+   */
+  public void addCdataSectionElements(String URI_and_localNames)
+  {
+    if (URI_and_localNames != null)
+      initCdataElems(URI_and_localNames);
+    if (m_StringOfCDATASections == null)
+      m_StringOfCDATASections = URI_and_localNames;
+    else
+      m_StringOfCDATASections += (" " + URI_and_localNames);
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org