/* * The Apache Software License, Version 1.1 * * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Xalan" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation and was * originally based on software copyright (c) 1999, Lotus * Development Corporation., http://www.lotus.com. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.xalan.serialize; import java.io.Writer; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.util.Enumeration; import java.util.Stack; import java.util.Vector; import java.util.Hashtable; import java.util.Properties; import java.util.BitSet; import org.xml.sax.*; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.ext.DeclHandler; import org.w3c.dom.Node; import org.apache.xalan.serialize.Serializer; import org.apache.xalan.serialize.DOMSerializer; import org.apache.xml.utils.QName; import org.apache.xalan.templates.OutputProperties; import org.apache.xml.utils.BoolStack; import org.apache.xml.utils.TreeWalker; import org.apache.xml.utils.WrappedRuntimeException; import org.apache.xml.utils.SystemIDResolver; import org.apache.xalan.res.XSLTErrorResources; import org.apache.xalan.res.XSLMessages; import org.apache.xpath.res.XPATHErrorResources; import javax.xml.transform.Result; import javax.xml.transform.OutputKeys; /** * <meta name="usage" content="general"/> * SerializerToXML formats SAX-style events into XML. */ public class SerializerToXML implements ContentHandler, LexicalHandler, DeclHandler, Serializer, DOMSerializer { /** * The writer where the XML will be written. */ protected Writer m_writer = null; /** True if we control the buffer, and we should flush the output on endDocument. */ boolean m_shouldFlush = true; // /** The output stream where the result stream is written. */ // protected OutputStream m_outputStream = System.out; /** * The character encoding. Must match the encoding used for the printWriter. */ protected String m_encoding = null; /** * Assume java encoding names are the same as the ISO encoding names if this is true. */ static boolean javaEncodingIsISO = false; /** * Tells if we should write the XML declaration. */ public boolean m_shouldNotWriteXMLHeader = false; /** * Tells the XML version, for writing out to the XML decl. */ public String m_version = null; /** * A stack of Boolean objects that tell if the given element * has children. */ protected BoolStack m_elemStack = new BoolStack(); /** Stack to keep track of disabling output escaping. */ protected BoolStack m_disableOutputEscapingStates = new BoolStack(); /** True will be pushed, if characters should be in CDATA section blocks. */ protected BoolStack m_cdataSectionStates = new BoolStack(); /** List of QNames obtained from the xsl:output properties. */ protected Vector m_cdataSectionNames = null; /** True if the current characters should be in CDATA blocks. */ protected boolean m_inCData = false; /** * Tell if the character escaping should be disabled for the current state. * * @return true if the character escaping should be disabled. */ protected boolean isEscapingDisabled() { return m_disableOutputEscapingStates.peekOrFalse(); } /** * Tell if the characters in the current state should be put in * cdata section blocks. * * @return true if the characters in the current state should be put in * cdata section blocks. */ protected boolean isCDataSection() { return m_inCData || m_cdataSectionStates.peekOrFalse(); } /** * Use the system line seperator to write line breaks. */ protected final char[] m_lineSep = System.getProperty("line.separator").toCharArray(); /** * The length of the line seperator, since the write is done * one character at a time. */ protected final int m_lineSepLen = m_lineSep.length; /** * Output a system-dependent line break. * * @throws org.xml.sax.SAXException */ protected final void outputLineSep() throws org.xml.sax.SAXException { try { m_writer.write(m_lineSep, 0, m_lineSepLen); } catch (IOException ioe) { throw new SAXException(ioe); } } /** * State flag to tell if preservation of whitespace * is important. */ protected boolean m_ispreserve = false; /** * Stack to keep track of whether or not we need to * preserve whitespace. */ protected BoolStack m_preserves = new BoolStack(); /** * State flag that tells if the previous node processed * was text, so we can tell if we should preserve whitespace. */ protected boolean m_isprevtext = false; /** * Flag to tell if indenting (pretty-printing) is on. */ protected boolean m_doIndent = false; /** * Flag to keep track of the indent amount. */ protected int m_currentIndent = 0; /** * Amount to indent. */ public int m_indentAmount = 0; /** * Current level of indent. */ protected int level = 0; /** * Flag to signal that a newline should be added. */ boolean m_startNewLine; /** * Flag to tell that we need to add the doctype decl, * which we can't do until the first element is * encountered. */ boolean m_needToOutputDocTypeDecl = true; /** * The System ID for the doc type. */ String m_doctypeSystem; /** * The public ID for the doc type. */ String m_doctypePublic; /** * The standalone value for the doctype. */ boolean m_standalone = false; /** * True if standalone was specified. */ boolean m_standaloneWasSpecified = false; /** * The mediatype. Not used right now. */ String m_mediatype; /** * Tells if we're in an EntityRef event. */ protected boolean m_inEntityRef = false; /** * Tells if we're in an internal document type subset. */ private boolean m_inDoctype = false; /** * Map that tells which XML characters should have special treatment, and it * provides character to entity name lookup. */ protected static CharInfo m_xmlcharInfo = new CharInfo(CharInfo.XML_ENTITIES_RESOURCE); /** * Map that tells which characters should have special treatment, and it * provides character to entity name lookup. */ protected CharInfo m_charInfo; /** Table of user-specified char infos. */ private static Hashtable m_charInfos = null; /** * Flag to quickly tell if the encoding is UTF8. */ boolean m_isUTF8; /** * The maximum character size before we have to resort * to escaping. */ int m_maxCharacter = Encodings.getLastPrintable(); /** * Add space before '/>' for XHTML. */ public boolean m_spaceBeforeClose = false; /** The xsl:output properties. */ protected Properties m_format; /** Indicate whether running in Debug mode */ private static final boolean DEBUG = false; /** * Default constructor. */ public SerializerToXML() { m_charInfo = m_xmlcharInfo; } /** * Copy properties from another SerializerToXML. * * @param xmlListener non-null reference to a SerializerToXML object. */ public void CopyFrom(SerializerToXML xmlListener) { m_writer = xmlListener.m_writer; // m_outputStream = xmlListener.m_outputStream; m_encoding = xmlListener.m_encoding; javaEncodingIsISO = xmlListener.javaEncodingIsISO; m_shouldNotWriteXMLHeader = xmlListener.m_shouldNotWriteXMLHeader; // m_shouldNotWriteXMLHeader = xmlListener.m_shouldNotWriteXMLHeader; m_elemStack = xmlListener.m_elemStack; // m_lineSep = xmlListener.m_lineSep; // m_lineSepLen = xmlListener.m_lineSepLen; m_ispreserve = xmlListener.m_ispreserve; m_preserves = xmlListener.m_preserves; m_isprevtext = xmlListener.m_isprevtext; m_doIndent = xmlListener.m_doIndent; m_currentIndent = xmlListener.m_currentIndent; m_indentAmount = xmlListener.m_indentAmount; level = xmlListener.level; m_startNewLine = xmlListener.m_startNewLine; m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl; m_doctypeSystem = xmlListener.m_doctypeSystem; m_doctypePublic = xmlListener.m_doctypePublic; m_standalone = xmlListener.m_standalone; m_mediatype = xmlListener.m_mediatype; m_maxCharacter = xmlListener.m_maxCharacter; m_spaceBeforeClose = xmlListener.m_spaceBeforeClose; m_inCData = xmlListener.m_inCData; // m_pos = xmlListener.m_pos; m_pos = 0; } /** * Initialize the serializer with the specified writer and output format. * Must be called before calling any of the serialize methods. * * @param writer The writer to use * @param format The output format */ public synchronized void init(Writer writer, Properties format) { init(writer, format, false); } /** * Initialize the serializer with the specified writer and output format. * Must be called before calling any of the serialize methods. * * @param writer The writer to use * @param format The output format * @param shouldFlush True if the writer should be flushed at EndDocument. */ private synchronized void init(Writer writer, Properties format, boolean shouldFlush) { m_shouldFlush = shouldFlush; m_writer = writer; m_format = format; m_cdataSectionNames = OutputProperties.getQNameProperties(OutputKeys.CDATA_SECTION_ELEMENTS, format); m_indentAmount = OutputProperties.getIntProperty(OutputProperties.S_KEY_INDENT_AMOUNT, format); m_doIndent = OutputProperties.getBooleanProperty(OutputKeys.INDENT, format); m_shouldNotWriteXMLHeader = OutputProperties.getBooleanProperty(OutputKeys.OMIT_XML_DECLARATION, format); m_doctypeSystem = format.getProperty(OutputKeys.DOCTYPE_SYSTEM); m_doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); m_standaloneWasSpecified = (null != format.get(OutputKeys.STANDALONE)); m_standalone = OutputProperties.getBooleanProperty(OutputKeys.STANDALONE, format); m_mediatype = format.getProperty(OutputKeys.MEDIA_TYPE); if (null != m_doctypePublic) { if (m_doctypePublic.startsWith("-//W3C//DTD XHTML")) m_spaceBeforeClose = true; } // initCharsMap(); if (null == m_encoding) m_encoding = Encodings.getMimeEncoding(format.getProperty(OutputKeys.ENCODING)); m_isUTF8 = m_encoding.equals(Encodings.DEFAULT_MIME_ENCODING); m_maxCharacter = Encodings.getLastPrintable(m_encoding); // Access this only from the Hashtable level... we don't want to // get default properties. String entitiesFileName = (String) format.get(OutputProperties.S_KEY_ENTITIES); if (null != entitiesFileName) { try { m_charInfo = null; if (null == m_charInfos) { synchronized (m_xmlcharInfo) { if (null == m_charInfos) // secondary check m_charInfos = new Hashtable(); } } else { m_charInfo = (CharInfo) m_charInfos.get(entitiesFileName); } if (null == m_charInfo) { String absoluteEntitiesFileName; if (entitiesFileName.indexOf(':') < 0) { absoluteEntitiesFileName = SystemIDResolver.getAbsoluteURIFromRelative(entitiesFileName); } else { absoluteEntitiesFileName = SystemIDResolver.getAbsoluteURI(entitiesFileName, null); } m_charInfo = new CharInfo(absoluteEntitiesFileName); m_charInfos.put(entitiesFileName, m_charInfo); } } catch (javax.xml.transform.TransformerException te) { throw new org.apache.xml.utils.WrappedRuntimeException(te); } } } /** * Initialize the serializer with the specified output stream and output format. * Must be called before calling any of the serialize methods. * * @param output The output stream to use * @param format The output format * @throws UnsupportedEncodingException The encoding specified * in the output format is not supported */ public synchronized void init(OutputStream output, Properties format) throws UnsupportedEncodingException { if (null == format) { OutputProperties op = new OutputProperties(Method.XML); format = op.getProperties(); } m_encoding = Encodings.getMimeEncoding(format.getProperty(OutputKeys.ENCODING)); if (m_encoding.equalsIgnoreCase("UTF-8")) { if(output instanceof java.io.BufferedOutputStream) { init(new WriterToUTF8(output), format, true); } else if(output instanceof java.io.FileOutputStream) { init(new WriterToUTF8Buffered(output), format, true); } else { // Not sure what to do in this case. I'm going to be conservative // and not buffer. init(new WriterToUTF8(output), format, true); } } else if (m_encoding.equals("WINDOWS-1250") || m_encoding.equals("US-ASCII") || m_encoding.equals("ASCII")) { init(new WriterToASCI(output), format, true); } else { Writer osw; try { osw = Encodings.getWriter(output, m_encoding); } catch (UnsupportedEncodingException uee) { System.out.println("Warning: encoding \"" + m_encoding + "\" not supported" + ", using " + Encodings.DEFAULT_MIME_ENCODING); m_encoding = Encodings.DEFAULT_MIME_ENCODING; osw = Encodings.getWriter(output, m_encoding); } m_maxCharacter = Encodings.getLastPrintable(m_encoding); init(osw, format, true); } } /** * Receive an object for locating the origin of SAX document events. * * @param locator An object that can return the location of * any SAX document event. * @see org.xml.sax.Locator */ public void setDocumentLocator(Locator locator) { // I don't do anything with this yet. } /** * Output the doc type declaration. * * @param name non-null reference to document type name. * NEEDSDOC @param closeDecl * * @throws org.xml.sax.SAXException */ void outputDocTypeDecl(String name, boolean closeDecl) throws org.xml.sax.SAXException { try { final Writer writer = m_writer; writer.write("<!DOCTYPE "); writer.write(name); if (null != m_doctypePublic) { writer.write(" PUBLIC \""); writer.write(m_doctypePublic); writer.write('\"'); } if (null != m_doctypeSystem) { if (null == m_doctypePublic) writer.write(" SYSTEM \""); else writer.write(" \""); writer.write(m_doctypeSystem); if (closeDecl) { writer.write("\">"); writer.write(m_lineSep, 0, m_lineSepLen);; } else writer.write('\"'); } } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Output the doc type declaration. * * @param name non-null reference to document type name. * NEEDSDOC @param value * * @throws org.xml.sax.SAXException */ void outputEntityDecl(String name, String value) throws org.xml.sax.SAXException { try { final Writer writer = m_writer; writer.write("<!ENTITY "); writer.write(name); writer.write(" \""); writer.write(value); writer.write("\">"); writer.write(m_lineSep, 0, m_lineSepLen); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Receive notification of the beginning of a document. * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * * @throws org.xml.sax.SAXException */ public void startDocument() throws org.xml.sax.SAXException { if (m_inEntityRef) return; m_needToOutputDocTypeDecl = true; m_startNewLine = false; if (m_shouldNotWriteXMLHeader == false) { String encoding = Encodings.getMimeEncoding(m_encoding); String version = (null == m_version) ? "1.0" : m_version; String standalone; if (m_standaloneWasSpecified) { standalone = " standalone=\"" + (m_standalone ? "yes" : "no") + "\""; } else { standalone = ""; } try { final Writer writer = m_writer; writer.write("<?xml version=\""); writer.write(version); writer.write("\" encoding=\""); writer.write(encoding); writer.write('\"'); writer.write(standalone); writer.write("?>"); writer.write(m_lineSep, 0, m_lineSepLen); } catch(IOException ioe) { throw new SAXException(ioe); } } } /** * Receive notification of the end of a document. * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * * @throws org.xml.sax.SAXException */ public void endDocument() throws org.xml.sax.SAXException { if (m_doIndent &&!m_isprevtext) { outputLineSep(); } flushWriter(); } /** * 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 { m_doctypeSystem = systemId; m_doctypePublic = publicId; if ((true == m_needToOutputDocTypeDecl)) // && (null != m_doctypeSystem)) { outputDocTypeDecl(name, false); } m_needToOutputDocTypeDecl = false; m_inDoctype = true; } /** * 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_inDoctype) m_writer.write("]>"); else { m_writer.write('>'); } m_writer.write(m_lineSep, 0, m_lineSepLen); } catch(IOException ioe) { throw new SAXException(ioe); } // Do nothing for now. } /** * Begin the scope of a prefix-URI Namespace mapping. * @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{} /** * 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{} /** * Tell if two strings are equal, without worry if the first string is null. * * @param p String reference, which may be null. * @param t String reference, which may be null. * * @return true if strings are equal. */ protected static final boolean subPartMatch(String p, String t) { return (p == t) || ((null != p) && (p.equals(t))); } /** * Push a boolean state based on if the name of the element * is found in the list of qnames. A state is always pushed, * one way or the other. * * @param namespaceURI Should be a non-null reference to the namespace URL * of the element that owns the state, or empty string. * @param localName Should be a non-null reference to the local name * of the element that owns the state. * @param qnames Vector of qualified names of elements, or null. * @param state The stack where the state should be pushed. */ protected void pushState(String namespaceURI, String localName, Vector qnames, BoolStack state) { boolean b; if (null != qnames) { b = false; if ((null != namespaceURI) && namespaceURI.length() == 0) namespaceURI = null; int nElems = qnames.size(); for (int i = 0; i < nElems; i++) { QName q = (QName) qnames.elementAt(i); if (q.getLocalName().equals(localName) && subPartMatch(namespaceURI, q.getNamespaceURI())) { b = true; break; } } } else { b = state.peekOrFalse(); } state.push(b); } /** * Receive notification of the beginning 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. * @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 * * @throws org.xml.sax.SAXException */ public void startElement( String namespaceURI, String localName, String name, Attributes atts) throws org.xml.sax.SAXException { if (DEBUG) { System.out.println("SerializerToXML - startElement: " + namespaceURI + ", " + localName); int n = atts.getLength(); for (int i = 0; i < n; i++) { System.out.println("atts[" + i + "]: " + atts.getQName(i) + " = " + atts.getValue(i)); } if (null == namespaceURI) { (new RuntimeException(localName + " has a null namespace!")).printStackTrace(); } } if (m_inEntityRef) return; if ((true == m_needToOutputDocTypeDecl) && (null != m_doctypeSystem)) { outputDocTypeDecl(name, true); } m_needToOutputDocTypeDecl = false; writeParentTagEnd(); pushState(namespaceURI, localName, m_cdataSectionNames, m_cdataSectionStates); // pushState(namespaceURI, localName, m_format.getNonEscapingElements(), // m_disableOutputEscapingStates); m_ispreserve = false; // System.out.println(name+": m_doIndent = "+m_doIndent+", m_ispreserve = "+m_ispreserve+", m_isprevtext = "+m_isprevtext); if (shouldIndent() && m_startNewLine) { indent(m_currentIndent); } m_startNewLine = true; try { m_writer.write('<'); m_writer.write(name); } catch(IOException ioe) { throw new SAXException(ioe); } int nAttrs = atts.getLength(); for (int i = 0; i < nAttrs; i++) { processAttribute(atts.getQName(i), atts.getValue(i)); } // Flag the current element as not yet having any children. openElementForChildren(); m_currentIndent += m_indentAmount; m_isprevtext = false; } /** * Check to see if a parent's ">" has been written, and, if * it has not, write it. * * @throws org.xml.sax.SAXException */ protected void writeParentTagEnd() throws org.xml.sax.SAXException { // See if the parent element has already been flagged as having children. if (!m_elemStack.peekOrTrue()) { try { m_writer.write('>'); } catch(IOException ioe) { throw new SAXException(ioe); } m_isprevtext = false; m_elemStack.setTop(true); m_preserves.push(m_ispreserve); } } /** * Flag the current element as not yet having any * children. */ protected void openElementForChildren() { // Flag the current element as not yet having any children. m_elemStack.push(false); } /** * Tell if child nodes have been added to the current * element. Must be called in balance with openElementForChildren(). * * @return true if child nodes were added. */ protected boolean childNodesWereAdded() { return m_elemStack.isEmpty() ? false : m_elemStack.pop(); } /** * 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. * * @throws org.xml.sax.SAXException */ public void endElement(String namespaceURI, String localName, String name) throws org.xml.sax.SAXException { if (m_inEntityRef) return; m_currentIndent -= m_indentAmount; boolean hasChildNodes = childNodesWereAdded(); try { final Writer writer = m_writer; if (hasChildNodes) { if (shouldIndent()) indent(m_currentIndent); writer.write('<'); writer.write('/'); writer.write(name); writer.write('>'); } else { if (m_spaceBeforeClose) writer.write(" />"); else writer.write("/>"); } } catch(IOException ioe) { throw new SAXException(ioe); } if (hasChildNodes) { m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop(); } m_isprevtext = false; // m_disableOutputEscapingStates.pop(); m_cdataSectionStates.pop(); } /** * Process an attribute. * @param name The name of the attribute. * @param value The value of the attribute. * * @throws org.xml.sax.SAXException */ protected void processAttribute(String name, String value) throws org.xml.sax.SAXException { try { final Writer writer = m_writer; writer.write(' '); writer.write(name); writer.write("=\""); writeAttrString(value, m_encoding); writer.write('\"'); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * 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 */ public void startNonEscaping() throws org.xml.sax.SAXException { m_disableOutputEscapingStates.push(true); } /** * Ends an un-escaping section. * * @see #startNonEscaping * * @throws org.xml.sax.SAXException */ public void endNonEscaping() throws org.xml.sax.SAXException { m_disableOutputEscapingStates.pop(); } /** * Starts a whitespace preserving section. All characters printed * within a preserving section are printed without indentation and * without consolidating multiple spaces. This is equivalent to * the <tt>xml:space="preserve"</tt> attribute. Only XML * and HTML serializers need to support this method. * <p> * The contents of the whitespace preserving section will be delivered * through the regular <tt>characters</tt> event. * * @throws org.xml.sax.SAXException */ public void startPreserving() throws org.xml.sax.SAXException { // Not sure this is really what we want. -sb m_preserves.push(true); m_ispreserve = true; } /** * Ends a whitespace preserving section. * * @see #startPreserving * * @throws org.xml.sax.SAXException */ public void endPreserving() throws org.xml.sax.SAXException { // Not sure this is really what we want. -sb m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop(); } /** * Receive notification of a processing instruction. * * @param target The processing instruction target. * @param data The processing instruction data, or null if * none was supplied. * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * * @throws org.xml.sax.SAXException */ public void processingInstruction(String target, String data) throws org.xml.sax.SAXException { if (m_inEntityRef) return; if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING)) { startNonEscaping(); } else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING)) { endNonEscaping(); } else { try { final Writer writer = m_writer; writeParentTagEnd(); if (shouldIndent()) indent(m_currentIndent); writer.write('<'); writer.write('?'); writer.write(target); if (data.length() > 0 &&!Character.isSpaceChar(data.charAt(0))) writer.write(' '); int indexOfQLT = data.indexOf("?>"); if (indexOfQLT >= 0) { // See XSLT spec on error recovery of "?>" in PIs. if (indexOfQLT > 0) { writer.write(data.substring(0, indexOfQLT)); } writer.write("? >"); // add space between. if ((indexOfQLT + 2) < data.length()) { writer.write(data.substring(indexOfQLT + 2)); } } else { writer.write(data); } writer.write('?'); writer.write('>'); // Always output a newline char if not inside of an // element. The whitespace is not significant in that // case. if (m_elemStack.isEmpty()) writer.write(m_lineSep, 0, m_lineSepLen); m_startNewLine = true; } catch(IOException ioe) { throw new SAXException(ioe); } } } /** * Report 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 { if (m_inEntityRef) return; writeParentTagEnd(); if (shouldIndent()) indent(m_currentIndent); try { final Writer writer = m_writer; writer.write("<!--"); writer.write(ch, start, length); writer.write("-->"); } catch(IOException ioe) { throw new SAXException(ioe); } m_startNewLine = true; } /** * 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_inCData = true; } /** * 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 { m_inCData = false; } /** * Receive notification of cdata. * * <p>The Parser will call this method to report each chunk of * character data. SAX parsers may return all contiguous character * data in a single chunk, or they may split it into several * chunks; however, all of the characters in any single event * must come from the same external entity, so that the Locator * provides useful information.</p> * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Note that some parsers will report whitespace using the * ignorableWhitespace() method rather than this one (validating * parsers must do so).</p> * * @param ch The characters from the XML document. * @param start The start position in the array. * @param length The number of characters to read from the array. * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see #ignorableWhitespace * @see org.xml.sax.Locator * * @throws org.xml.sax.SAXException */ public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException { try { writeParentTagEnd(); m_ispreserve = true; if (shouldIndent()) indent(m_currentIndent); boolean writeCDataBrackets = (((length >= 1) && (ch[start] <= m_maxCharacter))); if (writeCDataBrackets) { m_writer.write("<![CDATA["); } // m_writer.write(ch, start, length); if (isEscapingDisabled()) { charactersRaw(ch, start, length); } else writeNormalizedChars(ch, start, length, true); if (writeCDataBrackets) { m_writer.write("]]>"); } } catch (IOException ioe) { throw new org.xml.sax.SAXException( XSLMessages.createXPATHMessage(XPATHErrorResources.ER_OIERROR, null), ioe); //"IO error", ioe); } } /** The current position in the m_charBuf or m_byteBuf. */ protected int m_pos = 0; /** * Append a character to the buffer. * * @param b byte to be written to result stream. * * @throws org.xml.sax.SAXException */ protected final void accum(char b) throws org.xml.sax.SAXException { try { m_writer.write(b); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Append a character to the buffer. * * @param chars non-null reference to character array. * @param start Start of characters to be written. * @param length Number of characters to be written. * * @throws org.xml.sax.SAXException */ protected final void accum(char chars[], int start, int length) throws org.xml.sax.SAXException { try { m_writer.write(chars, start, length); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Append a character to the buffer. * * @param s non-null reference to string to be written to the character buffer. * * @throws org.xml.sax.SAXException */ protected final void accum(String s) throws org.xml.sax.SAXException { try { m_writer.write(s); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Flush the formatter's result stream. * * @throws org.xml.sax.SAXException */ public final void flushWriter() throws org.xml.sax.SAXException { if (null != m_writer) { try { if (m_writer instanceof WriterToUTF8Buffered) { if(m_shouldFlush) ((WriterToUTF8Buffered) m_writer).flush(); else ((WriterToUTF8Buffered) m_writer).flushBuffer(); } if (m_writer instanceof WriterToUTF8) { if(m_shouldFlush) m_writer.flush(); } else if (m_writer instanceof WriterToASCI) { if(m_shouldFlush) m_writer.flush(); } else { // Flush always. // Not a great thing if the writer was created // by this class, but don't have a choice. m_writer.flush(); } } catch (IOException ioe) { throw new org.xml.sax.SAXException(ioe); } } } /** * Receive notification of character data. * * <p>The Parser will call this method to report each chunk of * character data. SAX parsers may return all contiguous character * data in a single chunk, or they may split it into several * chunks; however, all of the characters in any single event * must come from the same external entity, so that the Locator * provides useful information.</p> * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Note that some parsers will report whitespace using the * ignorableWhitespace() method rather than this one (validating * parsers must do so).</p> * * @param chars The characters from the XML document. * @param start The start position in the array. * @param length The number of characters to read from the array. * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see #ignorableWhitespace * @see org.xml.sax.Locator * * @throws org.xml.sax.SAXException */ public void characters(char chars[], int start, int length) throws org.xml.sax.SAXException { if(0 == length) return; // if (m_inEntityRef) // return; // else if (m_inCData || m_cdataSectionStates.peekOrFalse()) { cdata(chars, start, length); return; } try { if (m_disableOutputEscapingStates.peekOrFalse()) { charactersRaw(chars, start, length); return; } final Writer writer = m_writer; if (!m_elemStack.peekOrTrue()) { writer.write('>'); m_isprevtext = false; m_elemStack.setTop(true); m_preserves.push(m_ispreserve); } int startClean = start; int lengthClean = 0; // int pos = 0; int end = start + length; boolean checkWhite = true; final int maxCharacter = m_maxCharacter; final BitSet specialsMap = m_charInfo.m_specialsMap; for (int i = start; i < end; i++) { char ch = chars[i]; if (checkWhite && ((ch > 0x20) ||!((ch == 0x20) || (ch == 0x09) || (ch == 0xD) || (ch == 0xA)))) { m_ispreserve = true; checkWhite = false; } if (((ch < maxCharacter) && (!specialsMap.get(ch))) || ('"' == ch)) { lengthClean++; } else { if (lengthClean > 0) { writer.write(chars, startClean, lengthClean); lengthClean = 0; } if (CharInfo.S_LINEFEED == ch) { writer.write(m_lineSep, 0, m_lineSepLen); startClean = i + 1; } else { startClean = accumDefaultEscape(ch, i, chars, end, false); i = startClean - 1; } } } if (lengthClean > 0) { writer.write(chars, startClean, lengthClean); } } catch(IOException ioe) { throw new SAXException(ioe); } m_isprevtext = true; } /** * 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 */ public void charactersRaw(char ch[], int start, int length) throws org.xml.sax.SAXException { try { if (m_inEntityRef) return; writeParentTagEnd(); m_ispreserve = true; m_writer.write(ch, start, length); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Return true if the character is the high member of a surrogate pair. * * NEEDSDOC @param c * * NEEDSDOC ($objectName$) @return */ static final boolean isUTF16Surrogate(char c) { return (c & 0xFC00) == 0xD800; } /** * Once a surrogate has been detected, get the pair as a single * integer value. * * @param c the first part of the surrogate. * @param ch Character array. * @param i position Where the surrogate was detected. * @param end The end index of the significant characters. * @return i+1. * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected. */ int getURF16SurrogateValue(char c, char ch[], int i, int end) throws org.xml.sax.SAXException { int next; if (i + 1 >= end) { throw new org.xml.sax.SAXException( XSLMessages.createXPATHMessage( XPATHErrorResources.ER_INVALID_UTF16_SURROGATE, new Object[]{ Integer.toHexString((int) c) })); //"Invalid UTF-16 surrogate detected: " //+Integer.toHexString((int)c)+ " ?"); } else { next = ch[++i]; if (!(0xdc00 <= next && next < 0xe000)) throw new org.xml.sax.SAXException( XSLMessages.createXPATHMessage( XPATHErrorResources.ER_INVALID_UTF16_SURROGATE, new Object[]{ Integer.toHexString((int) c) + " " + Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: " //+Integer.toHexString((int)c)+" "+Integer.toHexString(next)); next = ((c - 0xd800) << 10) + next - 0xdc00 + 0x00010000; } return next; } /** * Once a surrogate has been detected, write the pair as a single * character reference. * * @param c the first part of the surrogate. * @param ch Character array. * @param i position Where the surrogate was detected. * @param end The end index of the significant characters. * @return i+1. * @throws IOException * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected. */ protected int writeUTF16Surrogate(char c, char ch[], int i, int end) throws IOException, org.xml.sax.SAXException { // UTF-16 surrogate int surrogateValue = getURF16SurrogateValue(c, ch, i, end); i++; m_writer.write('&'); m_writer.write('#'); // m_writer.write('x'); m_writer.write(Integer.toString(surrogateValue)); m_writer.write(';'); return i; } /** * 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. * * @throws IOException * @throws org.xml.sax.SAXException */ void writeNormalizedChars(char ch[], int start, int length, boolean isCData) throws IOException, org.xml.sax.SAXException { int end = start + length; for (int i = start; i < end; i++) { char c = ch[i]; if (CharInfo.S_LINEFEED == c) { m_writer.write(m_lineSep, 0, m_lineSepLen); } else if (isCData && (c > m_maxCharacter)) { if (i != 0) m_writer.write("]]>"); // This needs to go into a function... if (isUTF16Surrogate(c)) { i = writeUTF16Surrogate(c, ch, i, end); } else { m_writer.write("&#"); String intStr = Integer.toString((int) c); m_writer.write(intStr); m_writer.write(';'); } if ((i != 0) && (i < (end - 1))) m_writer.write("<![CDATA["); } else if (isCData && ((i < (end - 2)) && (']' == c) && (']' == ch[i + 1]) && ('>' == ch[i + 2]))) { m_writer.write("]]]]><![CDATA[>"); i += 2; } else { if (c <= m_maxCharacter) { m_writer.write(c); } // This needs to go into a function... else if (isUTF16Surrogate(c)) { i = writeUTF16Surrogate(c, ch, i, end); } else { m_writer.write("&#"); String intStr = Integer.toString((int) c); m_writer.write(intStr); m_writer.write(';'); } } } } /** * 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 * * @throws org.xml.sax.SAXException */ 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 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 { m_inEntityRef = true; } /** * Report the end of an entity. * * @param name The name of the entity that is ending. * @throws org.xml.sax.SAXException The application may raise an exception. * @see #startEntity */ public void endEntity(String name) throws org.xml.sax.SAXException { m_inEntityRef = false; } /** * Receive notivication of a entityReference. * * @param name The name of the entity. * * @throws org.xml.sax.SAXException */ public void entityReference(String name) throws org.xml.sax.SAXException { writeParentTagEnd(); if (shouldIndent()) indent(m_currentIndent); try { final Writer writer = m_writer; writer.write("&"); writer.write(name); writer.write(";"); } catch(IOException ioe) { throw new SAXException(ioe); } } // 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 { try { final Writer writer = m_writer; if (m_inDoctype) { writer.write(" ["); writer.write(m_lineSep, 0, m_lineSepLen); m_inDoctype = false; } writer.write("<!ELEMENT "); writer.write(name); writer.write(' '); writer.write(model); writer.write('>'); writer.write(m_lineSep, 0, m_lineSepLen); } catch(IOException ioe) { throw new SAXException(ioe); } } /** NEEDSDOC Field m_elemName */ private String m_elemName = ""; /** * 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 { try { final Writer writer = m_writer; if (m_inDoctype) { writer.write(" ["); writer.write(m_lineSep, 0, m_lineSepLen); m_inDoctype = false; } if (!eName.equals(m_elemName)) { writer.write("<!ATTLIST "); writer.write(eName); writer.write(" "); m_elemName = eName; } else { m_pos -= 3; writer.write(m_lineSep, 0, m_lineSepLen); } writer.write(aName); writer.write(" "); writer.write(type); writer.write(" "); writer.write(valueDefault); //m_writer.write(" "); //m_writer.write(value); writer.write(">"); writer.write(m_lineSep, 0, m_lineSepLen); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * 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 { try { if (m_inDoctype) { m_writer.write(" ["); m_writer.write(m_lineSep, 0, m_lineSepLen); m_inDoctype = false; } outputEntityDecl(name, value); } catch(IOException ioe) { throw new SAXException(ioe); } } /** * 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{} /** * 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 escLF true if the linefeed should be escaped. * * @return i+1 if the character was written, else i. * * @throws org.xml.sax.SAXException */ final int accumDefaultEntity( char ch, int i, char[] chars, int len, boolean escLF) throws org.xml.sax.SAXException { try { if (!escLF && CharInfo.S_LINEFEED == ch) { m_writer.write(m_lineSep, 0, m_lineSepLen); } else { if (m_charInfo.isSpecial(ch)) { String entityRef = m_charInfo.getEntityNameForChar(ch); if (null != entityRef) { final Writer writer = m_writer; writer.write('&'); writer.write(entityRef); writer.write(';'); } else return i; } else return i; } return i + 1; } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Escape and m_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 escLF true if the linefeed should be escaped. * * @return i+1 if the character was written, else i. * * @throws org.xml.sax.SAXException */ final int accumDefaultEscape( char ch, int i, char[] chars, int len, boolean escLF) throws org.xml.sax.SAXException { int pos = accumDefaultEntity(ch, i, chars, len, escLF); if (i == pos) { pos++; try { if (0xd800 <= ch && ch < 0xdc00) { // UTF-16 surrogate int next; if (i + 1 >= len) { throw new org.xml.sax.SAXException( XSLMessages.createXPATHMessage( XPATHErrorResources.ER_INVALID_UTF16_SURROGATE, new Object[]{ Integer.toHexString(ch) })); //"Invalid UTF-16 surrogate detected: " //+Integer.toHexString(ch)+ " ?"); } else { next = chars[++i]; if (!(0xdc00 <= next && next < 0xe000)) throw new org.xml.sax.SAXException( XSLMessages.createXPATHMessage( XPATHErrorResources.ER_INVALID_UTF16_SURROGATE, new Object[]{ Integer.toHexString(ch) + " " + Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: " //+Integer.toHexString(ch)+" "+Integer.toHexString(next)); next = ((ch - 0xd800) << 10) + next - 0xdc00 + 0x00010000; } m_writer.write("&#"); m_writer.write(Integer.toString(next)); m_writer.write(";"); /*} else if (null != ctbc && !ctbc.canConvert(ch)) { sb.append("&#x"); sb.append(Integer.toString((int)ch, 16)); sb.append(";");*/ } else { if (ch > m_maxCharacter || (m_charInfo.isSpecial(ch))) { m_writer.write("&#"); m_writer.write(Integer.toString(ch)); m_writer.write(";"); } else { m_writer.write(ch); } } } catch(IOException ioe) { throw new SAXException(ioe); } } return pos; } /** * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>, * and UTF-16 surrogates for chracter references <CODE>&#xnn</CODE>. * * @param string String to convert to XML format. * @param encoding CURRENTLY NOT IMPLEMENTED. * * @throws org.xml.sax.SAXException */ public void writeAttrString(String string, String encoding) throws org.xml.sax.SAXException { try { final char[] stringChars = string.toCharArray(); final int len = stringChars.length; final Writer writer = m_writer; for (int i = 0; i < len; i++) { char ch = stringChars[i]; if ((ch < m_maxCharacter) && (!m_charInfo.isSpecial(ch))) { writer.write(ch); } else { // I guess the parser doesn't normalize cr/lf in attributes. -sb if ((CharInfo.S_CARRIAGERETURN == ch) && ((i + 1) < len) && (CharInfo.S_LINEFEED == stringChars[i + 1])) { i++; ch = CharInfo.S_LINEFEED; } accumDefaultEscape(ch, i, stringChars, len, true); } } } catch(IOException ioe) { throw new SAXException(ioe); } } /** * 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); } /** * Prints <var>n</var> spaces. * @param pw The character output stream to use. * @param n Number of spaces to print. * * @throws org.xml.sax.SAXException if an error occurs when writing. */ public void printSpace(int n) throws org.xml.sax.SAXException { try { for (int i = 0; i < n; i++) { m_writer.write(' '); } } catch(IOException ioe) { throw new SAXException(ioe); } } /** * Prints a newline character and <var>n</var> spaces. * @param pw The character output stream to use. * @param n Number of spaces to print. * * @throws org.xml.sax.SAXException if an error occurs during writing. */ public void indent(int n) throws org.xml.sax.SAXException { if (m_startNewLine) outputLineSep(); if (m_doIndent) { printSpace(n); } } /** * Specifies an output stream to which the document should be * serialized. This method should not be called while the * serializer is in the process of serializing a document. * <p> * The encoding specified in the output properties is used, or * if no encoding was specified, the default for the selected * output method. * * @param output The output stream */ public void setOutputStream(OutputStream output) { try { init(output, m_format); } catch (UnsupportedEncodingException uee) { // Should have been warned in init, I guess... } } /** * 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() { if (m_writer instanceof WriterToUTF8Buffered) return ((WriterToUTF8Buffered) m_writer).getOutputStream(); if (m_writer instanceof WriterToUTF8) return ((WriterToUTF8) m_writer).getOutputStream(); else if (m_writer instanceof WriterToASCI) return ((WriterToASCI) m_writer).getOutputStream(); else return null; } /** * 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) { m_writer = writer; } /** * 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; } /** * 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; init(m_writer, format, false); m_shouldFlush = shouldFlush; } /** * Returns the output format for this serializer. * * @return The output format in use */ public Properties getOutputFormat() { return m_format; } /** * Return a {@link ContentHandler} interface into this serializer. * If the serializer does not support the {@link ContentHandler} * interface, it should return null. * * @return A {@link ContentHandler} interface into this serializer, * or null if the serializer is not SAX 2 capable * @throws IOException An I/O exception occured */ public ContentHandler asContentHandler() throws IOException { return this; } /** * Return a {@link DOMSerializer} interface into this serializer. * If the serializer does not support the {@link DOMSerializer} * interface, it should return null. * * @return A {@link DOMSerializer} interface into this serializer, * or null if the serializer is not DOM capable * @throws IOException An I/O exception occured */ public DOMSerializer asDOMSerializer() throws IOException { return this; // for now } /** * Resets the serializer. If this method returns true, the * serializer may be used for subsequent serialization of new * documents. It is possible to change the output format and * output stream prior to serializing, or to use the existing * output format and output stream. * * @return True if serializer has been reset and can be reused */ public boolean reset() { return false; } /** * Serializes the DOM node. Throws an exception only if an I/O * exception occured while serializing. * * @param elem The element to serialize * * @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, new org.apache.xpath.DOM2Helper()); walker.traverse(node); } catch (org.xml.sax.SAXException se) { throw new WrappedRuntimeException(se); } } } //ToXMLStringVisitor