| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id: ToTextStream.java 468654 2006-10-28 07:09:23Z minchau $ |
| */ |
| package org.apache.xml.serializer; |
| |
| import java.io.IOException; |
| |
| import org.apache.xml.serializer.utils.MsgKey; |
| import org.apache.xml.serializer.utils.Utils; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * This class is not a public API. |
| * It is only public because it is used in other packages. |
| * This class converts SAX or SAX-like calls to a |
| * serialized document for xsl:output method of "text". |
| * @xsl.usage internal |
| */ |
| public class ToTextStream extends ToStream |
| { |
| |
| |
| /** |
| * Default constructor. |
| */ |
| public ToTextStream() |
| { |
| super(); |
| } |
| |
| |
| |
| /** |
| * Receive notification of the beginning of a document. |
| * |
| * <p>The SAX parser will invoke this method only once, before any |
| * other methods in this interface or in DTDHandler (except for |
| * setDocumentLocator).</p> |
| * |
| * @throws org.xml.sax.SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| protected void startDocumentInternal() throws org.xml.sax.SAXException |
| { |
| super.startDocumentInternal(); |
| |
| m_needToCallStartDocument = false; |
| |
| // No action for the moment. |
| } |
| |
| /** |
| * Receive notification of the end of a document. |
| * |
| * <p>The SAX parser will invoke this method only once, and it will |
| * be the last method invoked during the parse. The parser shall |
| * not invoke this method until it has either abandoned parsing |
| * (because of an unrecoverable error) or reached the end of |
| * input.</p> |
| * |
| * @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 |
| { |
| flushPending(); |
| flushWriter(); |
| if (m_tracer != null) |
| super.fireEndDoc(); |
| } |
| |
| /** |
| * Receive notification of the beginning of an element. |
| * |
| * <p>The Parser will invoke this method at the beginning of every |
| * element in the XML document; there will be a corresponding |
| * endElement() event for every startElement() event (even when the |
| * element is empty). All of the element's content will be |
| * reported, in order, before the corresponding endElement() |
| * event.</p> |
| * |
| * <p>If the element name has a namespace prefix, the prefix will |
| * still be attached. Note that the attribute list provided will |
| * contain only attributes with explicit values (specified or |
| * defaulted): #IMPLIED attributes will be omitted.</p> |
| * |
| * |
| * @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 qualified name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @param atts The attributes attached to the element, if any. |
| * @throws org.xml.sax.SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see #endElement |
| * @see org.xml.sax.AttributeList |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void startElement( |
| String namespaceURI, String localName, String name, Attributes atts) |
| throws org.xml.sax.SAXException |
| { |
| // time to fire off startElement event |
| if (m_tracer != null) { |
| super.fireStartElem(name); |
| this.firePseudoAttributes(); |
| } |
| return; |
| } |
| |
| /** |
| * Receive notification of the end of an element. |
| * |
| * <p>The SAX parser will invoke this method at the end of every |
| * element in the XML document; there will be a corresponding |
| * startElement() event for every endElement() event (even when the |
| * element is empty).</p> |
| * |
| * <p>If the element name has a namespace prefix, the prefix will |
| * still be attached to the name.</p> |
| * |
| * |
| * @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 qualified name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @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_tracer != null) |
| super.fireEndElem(name); |
| } |
| |
| /** |
| * 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 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 |
| */ |
| public void characters(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| |
| flushPending(); |
| |
| try |
| { |
| if (inTemporaryOutputState()) { |
| /* leave characters un-processed as we are |
| * creating temporary output, the output generated by |
| * this serializer will be input to a final serializer |
| * later on and it will do the processing in final |
| * output state (not temporary output state). |
| * |
| * A "temporary" ToTextStream serializer is used to |
| * evaluate attribute value templates (for example), |
| * and the result of evaluating such a thing |
| * is fed into a final serializer later on. |
| */ |
| m_writer.write(ch, start, length); |
| } |
| else { |
| // In final output state we do process the characters! |
| writeNormalizedChars(ch, start, length, m_lineSepUse); |
| } |
| |
| if (m_tracer != null) |
| super.fireCharEvent(ch, start, length); |
| } |
| catch(IOException ioe) |
| { |
| throw new SAXException(ioe); |
| } |
| } |
| |
| /** |
| * 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 Any SAX exception, possibly |
| * wrapping another exception. |
| */ |
| public void charactersRaw(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| |
| try |
| { |
| writeNormalizedChars(ch, start, length, m_lineSepUse); |
| } |
| catch(IOException ioe) |
| { |
| throw new SAXException(ioe); |
| } |
| } |
| |
| /** |
| * Normalize the characters, but don't escape. Different from |
| * SerializerToXML#writeNormalizedChars because it does not attempt to do |
| * XML escaping at all. |
| * |
| * @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 useLineSep true if the operating systems |
| * end-of-line separator should be output rather than a new-line character. |
| * |
| * @throws IOException |
| * @throws org.xml.sax.SAXException |
| */ |
| void writeNormalizedChars( |
| final char ch[], |
| final int start, |
| final int length, |
| final boolean useLineSep) |
| throws IOException, org.xml.sax.SAXException |
| { |
| final String encoding = getEncoding(); |
| final java.io.Writer writer = m_writer; |
| final int end = start + length; |
| |
| /* copy a few "constants" before the loop for performance */ |
| final char S_LINEFEED = CharInfo.S_LINEFEED; |
| |
| // This for() loop always increments i by one at the end |
| // of the loop. Additional increments of i adjust for when |
| // two input characters (a high/low UTF16 surrogate pair) |
| // are processed. |
| for (int i = start; i < end; i++) { |
| final char c = ch[i]; |
| |
| if (S_LINEFEED == c && useLineSep) { |
| writer.write(m_lineSep, 0, m_lineSepLen); |
| // one input char processed |
| } else if (m_encodingInfo.isInEncoding(c)) { |
| writer.write(c); |
| // one input char processed |
| } else if (Encodings.isHighUTF16Surrogate(c)) { |
| final int codePoint = writeUTF16Surrogate(c, ch, i, end); |
| if (codePoint != 0) { |
| // I think we can just emit the message, |
| // not crash and burn. |
| final String integralValue = Integer.toString(codePoint); |
| final String msg = Utils.messages.createMessage( |
| MsgKey.ER_ILLEGAL_CHARACTER, |
| new Object[] { integralValue, encoding }); |
| |
| //Older behavior was to throw the message, |
| //but newer gentler behavior is to write a message to System.err |
| //throw new SAXException(msg); |
| System.err.println(msg); |
| |
| } |
| i++; // two input chars processed |
| } 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 |
| if (encoding != null) { |
| /* The output encoding is known, |
| * so somthing is wrong. |
| */ |
| |
| // not in the encoding, so write out a character reference |
| writer.write('&'); |
| writer.write('#'); |
| writer.write(Integer.toString(c)); |
| writer.write(';'); |
| |
| // I think we can just emit the message, |
| // not crash and burn. |
| final String integralValue = Integer.toString(c); |
| final String msg = Utils.messages.createMessage( |
| MsgKey.ER_ILLEGAL_CHARACTER, |
| new Object[] { integralValue, encoding }); |
| |
| //Older behavior was to throw the message, |
| //but newer gentler behavior is to write a message to System.err |
| //throw new SAXException(msg); |
| System.err.println(msg); |
| } else { |
| /* The output encoding is not known, |
| * so just write it out as-is. |
| */ |
| writer.write(c); |
| } |
| |
| // one input char was processed |
| } |
| } |
| } |
| |
| /** |
| * 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 |
| */ |
| public void cdata(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| try |
| { |
| writeNormalizedChars(ch, start, length, m_lineSepUse); |
| if (m_tracer != null) |
| super.fireCDATAEvent(ch, start, length); |
| } |
| catch(IOException ioe) |
| { |
| throw new SAXException(ioe); |
| } |
| } |
| |
| /** |
| * Receive notification of ignorable whitespace in element content. |
| * |
| * <p>Validating Parsers must use this method to report each chunk |
| * of ignorable whitespace (see the W3C XML 1.0 recommendation, |
| * section 2.10): non-validating parsers may also use this method |
| * if they are capable of parsing and using content models.</p> |
| * |
| * <p>SAX parsers may return all contiguous whitespace 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> |
| * |
| * @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 |
| { |
| |
| try |
| { |
| writeNormalizedChars(ch, start, length, m_lineSepUse); |
| } |
| catch(IOException ioe) |
| { |
| throw new SAXException(ioe); |
| } |
| } |
| |
| /** |
| * Receive notification of a processing instruction. |
| * |
| * <p>The Parser will invoke this method once for each processing |
| * instruction found: note that processing instructions may occur |
| * before or after the main document element.</p> |
| * |
| * <p>A SAX parser should never report an XML declaration (XML 1.0, |
| * section 2.8) or a text declaration (XML 1.0, section 4.3.1) |
| * using this method.</p> |
| * |
| * @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 |
| { |
| // flush anything pending first |
| flushPending(); |
| |
| if (m_tracer != null) |
| super.fireEscapingEvent(target, data); |
| } |
| |
| /** |
| * Called when a Comment is to be constructed. |
| * Note that Xalan will normally invoke the other version of this method. |
| * %REVIEW% In fact, is this one ever needed, or was it a mistake? |
| * |
| * @param data The comment data. |
| * @throws org.xml.sax.SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| */ |
| public void comment(String data) throws org.xml.sax.SAXException |
| { |
| final int length = data.length(); |
| if (length > m_charsBuff.length) |
| { |
| m_charsBuff = new char[length*2 + 1]; |
| } |
| data.getChars(0, length, m_charsBuff, 0); |
| comment(m_charsBuff, 0, length); |
| } |
| |
| /** |
| * 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 |
| { |
| |
| flushPending(); |
| if (m_tracer != null) |
| super.fireCommentEvent(ch, start, length); |
| } |
| |
| /** |
| * Receive notivication of a entityReference. |
| * |
| * @param name non-null reference to the name of the entity. |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void entityReference(String name) throws org.xml.sax.SAXException |
| { |
| if (m_tracer != null) |
| super.fireEntityReference(name); |
| } |
| |
| /** |
| * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) |
| */ |
| public void addAttribute( |
| String uri, |
| String localName, |
| String rawName, |
| String type, |
| String value, |
| boolean XSLAttribute) |
| { |
| // do nothing, just forget all about the attribute |
| } |
| |
| /** |
| * @see org.xml.sax.ext.LexicalHandler#endCDATA() |
| */ |
| public void endCDATA() throws SAXException |
| { |
| // do nothing |
| } |
| |
| /** |
| * @see ExtendedContentHandler#endElement(String) |
| */ |
| public void endElement(String elemName) throws SAXException |
| { |
| if (m_tracer != null) |
| super.fireEndElem(elemName); |
| } |
| |
| /** |
| * From XSLTC |
| */ |
| public void startElement( |
| String elementNamespaceURI, |
| String elementLocalName, |
| String elementName) |
| throws SAXException |
| { |
| if (m_needToCallStartDocument) |
| startDocumentInternal(); |
| // time to fire off startlement event. |
| if (m_tracer != null) { |
| super.fireStartElem(elementName); |
| this.firePseudoAttributes(); |
| } |
| |
| return; |
| } |
| |
| |
| /** |
| * From XSLTC |
| */ |
| public void characters(String characters) |
| throws SAXException |
| { |
| final int length = characters.length(); |
| if (length > m_charsBuff.length) |
| { |
| m_charsBuff = new char[length*2 + 1]; |
| } |
| characters.getChars(0, length, m_charsBuff, 0); |
| characters(m_charsBuff, 0, length); |
| } |
| |
| |
| /** |
| * From XSLTC |
| */ |
| public void addAttribute(String name, String value) |
| { |
| // do nothing, forget about the attribute |
| } |
| |
| /** |
| * Add a unique attribute |
| */ |
| public void addUniqueAttribute(String qName, String value, int flags) |
| throws SAXException |
| { |
| // do nothing, forget about the attribute |
| } |
| |
| public boolean startPrefixMapping( |
| String prefix, |
| String uri, |
| boolean shouldFlush) |
| throws SAXException |
| { |
| // no namespace support for HTML |
| return false; |
| } |
| |
| |
| public void startPrefixMapping(String prefix, String uri) |
| throws org.xml.sax.SAXException |
| { |
| // no namespace support for HTML |
| } |
| |
| |
| public void namespaceAfterStartElement( |
| final String prefix, |
| final String uri) |
| throws SAXException |
| { |
| // no namespace support for HTML |
| } |
| |
| public void flushPending() throws org.xml.sax.SAXException |
| { |
| if (m_needToCallStartDocument) |
| { |
| startDocumentInternal(); |
| m_needToCallStartDocument = false; |
| } |
| } |
| } |