0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: ToStream.java,v 1.45 2005/08/04 23:57:06 minchau Exp $
0018: */
0019: package org.apache.xml.serializer;
0020:
0021: import java.io.IOException;
0022: import java.io.OutputStream;
0023: import java.io.UnsupportedEncodingException;
0024: import java.io.Writer;
0025: import java.util.Properties;
0026: import java.util.StringTokenizer;
0027: import java.util.Vector;
0028:
0029: import javax.xml.transform.ErrorListener;
0030: import javax.xml.transform.OutputKeys;
0031: import javax.xml.transform.Transformer;
0032: import javax.xml.transform.TransformerException;
0033:
0034: import org.apache.xml.serializer.utils.MsgKey;
0035: import org.apache.xml.serializer.utils.Utils;
0036: import org.apache.xml.serializer.utils.WrappedRuntimeException;
0037: import org.w3c.dom.Node;
0038: import org.xml.sax.Attributes;
0039: import org.xml.sax.ContentHandler;
0040: import org.xml.sax.SAXException;
0041:
0042: //import com.sun.media.sound.IESecurity;
0043:
0044: /**
0045: * This abstract class is a base class for other stream
0046: * serializers (xml, html, text ...) that write output to a stream.
0047: *
0048: * @xsl.usage internal
0049: */
0050: abstract public class ToStream extends SerializerBase {
0051:
0052: private static final String COMMENT_BEGIN = "<!--";
0053: private static final String COMMENT_END = "-->";
0054:
0055: /** Stack to keep track of disabling output escaping. */
0056: protected BoolStack m_disableOutputEscapingStates = new BoolStack();
0057:
0058: /**
0059: * The encoding information associated with this serializer.
0060: * Although initially there is no encoding,
0061: * there is a dummy EncodingInfo object that will say
0062: * that every character is in the encoding. This is useful
0063: * for a serializer that is in temporary output state and has
0064: * no associated encoding. A serializer in final output state
0065: * will have an encoding, and will worry about whether
0066: * single chars or surrogate pairs of high/low chars form
0067: * characters in the output encoding.
0068: */
0069: EncodingInfo m_encodingInfo = new EncodingInfo(null, null);
0070:
0071: /**
0072: * Stack to keep track of whether or not we need to
0073: * preserve whitespace.
0074: *
0075: * Used to push/pop values used for the field m_ispreserve, but
0076: * m_ispreserve is only relevant if m_doIndent is true.
0077: * If m_doIndent is false this field has no impact.
0078: *
0079: */
0080: protected BoolStack m_preserves = new BoolStack();
0081:
0082: /**
0083: * State flag to tell if preservation of whitespace
0084: * is important.
0085: *
0086: * Used only in shouldIndent() but only if m_doIndent is true.
0087: * If m_doIndent is false this flag has no impact.
0088: *
0089: */
0090: protected boolean m_ispreserve = false;
0091:
0092: /**
0093: * State flag that tells if the previous node processed
0094: * was text, so we can tell if we should preserve whitespace.
0095: *
0096: * Used in endDocument() and shouldIndent() but
0097: * only if m_doIndent is true.
0098: * If m_doIndent is false this flag has no impact.
0099: */
0100: protected boolean m_isprevtext = false;
0101:
0102: /**
0103: * The system line separator for writing out line breaks.
0104: * The default value is from the system property,
0105: * but this value can be set through the xsl:output
0106: * extension attribute xalan:line-separator.
0107: */
0108: protected char[] m_lineSep = System.getProperty("line.separator")
0109: .toCharArray();
0110:
0111: /**
0112: * True if the the system line separator is to be used.
0113: */
0114: protected boolean m_lineSepUse = true;
0115:
0116: /**
0117: * The length of the line seperator, since the write is done
0118: * one character at a time.
0119: */
0120: protected int m_lineSepLen = m_lineSep.length;
0121:
0122: /**
0123: * Map that tells which characters should have special treatment, and it
0124: * provides character to entity name lookup.
0125: */
0126: protected CharInfo m_charInfo;
0127:
0128: /** True if we control the buffer, and we should flush the output on endDocument. */
0129: boolean m_shouldFlush = true;
0130:
0131: /**
0132: * Add space before '/>' for XHTML.
0133: */
0134: protected boolean m_spaceBeforeClose = false;
0135:
0136: /**
0137: * Flag to signal that a newline should be added.
0138: *
0139: * Used only in indent() which is called only if m_doIndent is true.
0140: * If m_doIndent is false this flag has no impact.
0141: */
0142: boolean m_startNewLine;
0143:
0144: /**
0145: * Tells if we're in an internal document type subset.
0146: */
0147: protected boolean m_inDoctype = false;
0148:
0149: /**
0150: * Flag to quickly tell if the encoding is UTF8.
0151: */
0152: boolean m_isUTF8 = false;
0153:
0154: /** The xsl:output properties. */
0155: protected Properties m_format;
0156:
0157: /**
0158: * remembers if we are in between the startCDATA() and endCDATA() callbacks
0159: */
0160: protected boolean m_cdataStartCalled = false;
0161:
0162: /**
0163: * If this flag is true DTD entity references are not left as-is,
0164: * which is exiting older behavior.
0165: */
0166: private boolean m_expandDTDEntities = true;
0167:
0168: /**
0169: * Default constructor
0170: */
0171: public ToStream() {
0172: }
0173:
0174: /**
0175: * This helper method to writes out "]]>" when closing a CDATA section.
0176: *
0177: * @throws org.xml.sax.SAXException
0178: */
0179: protected void closeCDATA() throws org.xml.sax.SAXException {
0180: try {
0181: m_writer.write(CDATA_DELIMITER_CLOSE);
0182: // write out a CDATA section closing "]]>"
0183: m_cdataTagOpen = false; // Remember that we have done so.
0184: } catch (IOException e) {
0185: throw new SAXException(e);
0186: }
0187: }
0188:
0189: /**
0190: * Serializes the DOM node. Throws an exception only if an I/O
0191: * exception occured while serializing.
0192: *
0193: * @param node Node to serialize.
0194: * @throws IOException An I/O exception occured while serializing
0195: */
0196: public void serialize(Node node) throws IOException {
0197:
0198: try {
0199: TreeWalker walker = new TreeWalker(this );
0200:
0201: walker.traverse(node);
0202: } catch (org.xml.sax.SAXException se) {
0203: throw new WrappedRuntimeException(se);
0204: }
0205: }
0206:
0207: /**
0208: * Taken from XSLTC
0209: */
0210: private boolean m_escaping = true;
0211:
0212: /**
0213: * Flush the formatter's result stream.
0214: *
0215: * @throws org.xml.sax.SAXException
0216: */
0217: protected final void flushWriter() throws org.xml.sax.SAXException {
0218: final java.io.Writer writer = m_writer;
0219: if (null != writer) {
0220: try {
0221: if (writer instanceof WriterToUTF8Buffered) {
0222: if (m_shouldFlush)
0223: ((WriterToUTF8Buffered) writer).flush();
0224: else
0225: ((WriterToUTF8Buffered) writer).flushBuffer();
0226: }
0227: if (writer instanceof WriterToASCI) {
0228: if (m_shouldFlush)
0229: writer.flush();
0230: } else {
0231: // Flush always.
0232: // Not a great thing if the writer was created
0233: // by this class, but don't have a choice.
0234: writer.flush();
0235: }
0236: } catch (IOException ioe) {
0237: throw new org.xml.sax.SAXException(ioe);
0238: }
0239: }
0240: }
0241:
0242: /**
0243: * Get the output stream where the events will be serialized to.
0244: *
0245: * @return reference to the result stream, or null of only a writer was
0246: * set.
0247: */
0248: public OutputStream getOutputStream() {
0249:
0250: if (m_writer instanceof WriterToUTF8Buffered)
0251: return ((WriterToUTF8Buffered) m_writer).getOutputStream();
0252: if (m_writer instanceof WriterToASCI)
0253: return ((WriterToASCI) m_writer).getOutputStream();
0254: else
0255: return null;
0256: }
0257:
0258: // Implement DeclHandler
0259:
0260: /**
0261: * Report an element type declaration.
0262: *
0263: * <p>The content model will consist of the string "EMPTY", the
0264: * string "ANY", or a parenthesised group, optionally followed
0265: * by an occurrence indicator. The model will be normalized so
0266: * that all whitespace is removed,and will include the enclosing
0267: * parentheses.</p>
0268: *
0269: * @param name The element type name.
0270: * @param model The content model as a normalized string.
0271: * @exception SAXException The application may raise an exception.
0272: */
0273: public void elementDecl(String name, String model)
0274: throws SAXException {
0275: // Do not inline external DTD
0276: if (m_inExternalDTD)
0277: return;
0278: try {
0279: final java.io.Writer writer = m_writer;
0280: DTDprolog();
0281:
0282: writer.write("<!ELEMENT ");
0283: writer.write(name);
0284: writer.write(' ');
0285: writer.write(model);
0286: writer.write('>');
0287: writer.write(m_lineSep, 0, m_lineSepLen);
0288: } catch (IOException e) {
0289: throw new SAXException(e);
0290: }
0291:
0292: }
0293:
0294: /**
0295: * Report an internal entity declaration.
0296: *
0297: * <p>Only the effective (first) declaration for each entity
0298: * will be reported.</p>
0299: *
0300: * @param name The name of the entity. If it is a parameter
0301: * entity, the name will begin with '%'.
0302: * @param value The replacement text of the entity.
0303: * @exception SAXException The application may raise an exception.
0304: * @see #externalEntityDecl
0305: * @see org.xml.sax.DTDHandler#unparsedEntityDecl
0306: */
0307: public void internalEntityDecl(String name, String value)
0308: throws SAXException {
0309: // Do not inline external DTD
0310: if (m_inExternalDTD)
0311: return;
0312: try {
0313: DTDprolog();
0314: outputEntityDecl(name, value);
0315: } catch (IOException e) {
0316: throw new SAXException(e);
0317: }
0318:
0319: }
0320:
0321: /**
0322: * Output the doc type declaration.
0323: *
0324: * @param name non-null reference to document type name.
0325: * NEEDSDOC @param value
0326: *
0327: * @throws org.xml.sax.SAXException
0328: */
0329: void outputEntityDecl(String name, String value) throws IOException {
0330: final java.io.Writer writer = m_writer;
0331: writer.write("<!ENTITY ");
0332: writer.write(name);
0333: writer.write(" \"");
0334: writer.write(value);
0335: writer.write("\">");
0336: writer.write(m_lineSep, 0, m_lineSepLen);
0337: }
0338:
0339: /**
0340: * Output a system-dependent line break.
0341: *
0342: * @throws org.xml.sax.SAXException
0343: */
0344: protected final void outputLineSep() throws IOException {
0345:
0346: m_writer.write(m_lineSep, 0, m_lineSepLen);
0347: }
0348:
0349: /**
0350: * Specifies an output format for this serializer. It the
0351: * serializer has already been associated with an output format,
0352: * it will switch to the new format. This method should not be
0353: * called while the serializer is in the process of serializing
0354: * a document.
0355: *
0356: * @param format The output format to use
0357: */
0358: public void setOutputFormat(Properties format) {
0359:
0360: boolean shouldFlush = m_shouldFlush;
0361:
0362: init(m_writer, format, false, false);
0363:
0364: m_shouldFlush = shouldFlush;
0365: }
0366:
0367: /**
0368: * Initialize the serializer with the specified writer and output format.
0369: * Must be called before calling any of the serialize methods.
0370: * This method can be called multiple times and the xsl:output properties
0371: * passed in the 'format' parameter are accumulated across calls.
0372: *
0373: * @param writer The writer to use
0374: * @param format The output format
0375: * @param shouldFlush True if the writer should be flushed at EndDocument.
0376: */
0377: private synchronized void init(Writer writer, Properties format,
0378: boolean defaultProperties, boolean shouldFlush) {
0379:
0380: m_shouldFlush = shouldFlush;
0381:
0382: // if we are tracing events we need to trace what
0383: // characters are written to the output writer.
0384: if (m_tracer != null
0385: && !(writer instanceof SerializerTraceWriter))
0386: m_writer = new SerializerTraceWriter(writer, m_tracer);
0387: else
0388: m_writer = writer;
0389:
0390: m_format = format;
0391: // m_cdataSectionNames =
0392: // OutputProperties.getQNameProperties(
0393: // OutputKeys.CDATA_SECTION_ELEMENTS,
0394: // format);
0395: setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS,
0396: format);
0397:
0398: setIndentAmount(OutputPropertyUtils.getIntProperty(
0399: OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, format));
0400: setIndent(OutputPropertyUtils.getBooleanProperty(
0401: OutputKeys.INDENT, format));
0402:
0403: {
0404: String sep = format
0405: .getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR);
0406: if (sep != null) {
0407: m_lineSep = sep.toCharArray();
0408: m_lineSepLen = sep.length();
0409: }
0410: }
0411:
0412: boolean shouldNotWriteXMLHeader = OutputPropertyUtils
0413: .getBooleanProperty(OutputKeys.OMIT_XML_DECLARATION,
0414: format);
0415: setOmitXMLDeclaration(shouldNotWriteXMLHeader);
0416: setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
0417: String doctypePublic = format
0418: .getProperty(OutputKeys.DOCTYPE_PUBLIC);
0419: setDoctypePublic(doctypePublic);
0420:
0421: // if standalone was explicitly specified
0422: if (format.get(OutputKeys.STANDALONE) != null) {
0423: String val = format.getProperty(OutputKeys.STANDALONE);
0424: if (defaultProperties)
0425: setStandaloneInternal(val);
0426: else
0427: setStandalone(val);
0428: }
0429:
0430: setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));
0431:
0432: if (null != doctypePublic) {
0433: if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
0434: m_spaceBeforeClose = true;
0435: }
0436:
0437: /*
0438: * This code is added for XML 1.1 Version output.
0439: */
0440: String version = getVersion();
0441: if (null == version) {
0442: version = format.getProperty(OutputKeys.VERSION);
0443: setVersion(version);
0444: }
0445:
0446: // initCharsMap();
0447: String encoding = getEncoding();
0448: if (null == encoding) {
0449: encoding = Encodings.getMimeEncoding(format
0450: .getProperty(OutputKeys.ENCODING));
0451: setEncoding(encoding);
0452: }
0453:
0454: m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
0455:
0456: // Access this only from the Hashtable level... we don't want to
0457: // get default properties.
0458: String entitiesFileName = (String) format
0459: .get(OutputPropertiesFactory.S_KEY_ENTITIES);
0460:
0461: if (null != entitiesFileName) {
0462:
0463: String method = (String) format.get(OutputKeys.METHOD);
0464:
0465: m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
0466: }
0467:
0468: }
0469:
0470: /**
0471: * Initialize the serializer with the specified writer and output format.
0472: * Must be called before calling any of the serialize methods.
0473: *
0474: * @param writer The writer to use
0475: * @param format The output format
0476: */
0477: private synchronized void init(Writer writer, Properties format) {
0478: init(writer, format, false, false);
0479: }
0480:
0481: /**
0482: * Initialize the serializer with the specified output stream and output
0483: * format. Must be called before calling any of the serialize methods.
0484: *
0485: * @param output The output stream to use
0486: * @param format The output format
0487: * @param defaultProperties true if the properties are the default
0488: * properties
0489: *
0490: * @throws UnsupportedEncodingException The encoding specified in the
0491: * output format is not supported
0492: */
0493: protected synchronized void init(OutputStream output,
0494: Properties format, boolean defaultProperties)
0495: throws UnsupportedEncodingException {
0496:
0497: String encoding = getEncoding();
0498: if (encoding == null) {
0499: // if not already set then get it from the properties
0500: encoding = Encodings.getMimeEncoding(format
0501: .getProperty(OutputKeys.ENCODING));
0502: setEncoding(encoding);
0503: }
0504:
0505: if (encoding.equalsIgnoreCase("UTF-8")) {
0506: m_isUTF8 = true;
0507: // if (output instanceof java.io.BufferedOutputStream)
0508: // {
0509: // init(new WriterToUTF8(output), format, defaultProperties, true);
0510: // }
0511: // else if (output instanceof java.io.FileOutputStream)
0512: // {
0513: // init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
0514: // }
0515: // else
0516: // {
0517: // // Not sure what to do in this case. I'm going to be conservative
0518: // // and not buffer.
0519: // init(new WriterToUTF8(output), format, defaultProperties, true);
0520: // }
0521:
0522: init(new WriterToUTF8Buffered(output), format,
0523: defaultProperties, true);
0524:
0525: } else if (encoding.equals("WINDOWS-1250")
0526: || encoding.equals("US-ASCII")
0527: || encoding.equals("ASCII")) {
0528: init(new WriterToASCI(output), format, defaultProperties,
0529: true);
0530: } else {
0531: Writer osw;
0532:
0533: try {
0534: osw = Encodings.getWriter(output, encoding);
0535: } catch (UnsupportedEncodingException uee) {
0536: System.out.println("Warning: encoding \"" + encoding
0537: + "\" not supported" + ", using "
0538: + Encodings.DEFAULT_MIME_ENCODING);
0539:
0540: encoding = Encodings.DEFAULT_MIME_ENCODING;
0541: setEncoding(encoding);
0542: osw = Encodings.getWriter(output, encoding);
0543: }
0544:
0545: init(osw, format, defaultProperties, true);
0546: }
0547:
0548: }
0549:
0550: /**
0551: * Returns the output format for this serializer.
0552: *
0553: * @return The output format in use
0554: */
0555: public Properties getOutputFormat() {
0556: return m_format;
0557: }
0558:
0559: /**
0560: * Specifies a writer to which the document should be serialized.
0561: * This method should not be called while the serializer is in
0562: * the process of serializing a document.
0563: *
0564: * @param writer The output writer stream
0565: */
0566: public void setWriter(Writer writer) {
0567: // if we are tracing events we need to trace what
0568: // characters are written to the output writer.
0569: if (m_tracer != null
0570: && !(writer instanceof SerializerTraceWriter))
0571: m_writer = new SerializerTraceWriter(writer, m_tracer);
0572: else
0573: m_writer = writer;
0574: }
0575:
0576: /**
0577: * Set if the operating systems end-of-line line separator should
0578: * be used when serializing. If set false NL character
0579: * (decimal 10) is left alone, otherwise the new-line will be replaced on
0580: * output with the systems line separator. For example on UNIX this is
0581: * NL, while on Windows it is two characters, CR NL, where CR is the
0582: * carriage-return (decimal 13).
0583: *
0584: * @param use_sytem_line_break True if an input NL is replaced with the
0585: * operating systems end-of-line separator.
0586: * @return The previously set value of the serializer.
0587: */
0588: public boolean setLineSepUse(boolean use_sytem_line_break) {
0589: boolean oldValue = m_lineSepUse;
0590: m_lineSepUse = use_sytem_line_break;
0591: return oldValue;
0592: }
0593:
0594: /**
0595: * Specifies an output stream to which the document should be
0596: * serialized. This method should not be called while the
0597: * serializer is in the process of serializing a document.
0598: * <p>
0599: * The encoding specified in the output properties is used, or
0600: * if no encoding was specified, the default for the selected
0601: * output method.
0602: *
0603: * @param output The output stream
0604: */
0605: public void setOutputStream(OutputStream output) {
0606:
0607: try {
0608: Properties format;
0609: if (null == m_format)
0610: format = OutputPropertiesFactory
0611: .getDefaultMethodProperties(Method.XML);
0612: else
0613: format = m_format;
0614: init(output, format, true);
0615: } catch (UnsupportedEncodingException uee) {
0616:
0617: // Should have been warned in init, I guess...
0618: }
0619: }
0620:
0621: /**
0622: * @see SerializationHandler#setEscaping(boolean)
0623: */
0624: public boolean setEscaping(boolean escape) {
0625: final boolean temp = m_escaping;
0626: m_escaping = escape;
0627: return temp;
0628:
0629: }
0630:
0631: /**
0632: * Might print a newline character and the indentation amount
0633: * of the given depth.
0634: *
0635: * @param depth the indentation depth (element nesting depth)
0636: *
0637: * @throws org.xml.sax.SAXException if an error occurs during writing.
0638: */
0639: protected void indent(int depth) throws IOException {
0640:
0641: if (m_startNewLine)
0642: outputLineSep();
0643: /* For m_indentAmount > 0 this extra test might be slower
0644: * but Xalan's default value is 0, so this extra test
0645: * will run faster in that situation.
0646: */
0647: if (m_indentAmount > 0)
0648: printSpace(depth * m_indentAmount);
0649:
0650: }
0651:
0652: /**
0653: * Indent at the current element nesting depth.
0654: * @throws IOException
0655: */
0656: protected void indent() throws IOException {
0657: indent(m_elemContext.m_currentElemDepth);
0658: }
0659:
0660: /**
0661: * Prints <var>n</var> spaces.
0662: * @param n Number of spaces to print.
0663: *
0664: * @throws org.xml.sax.SAXException if an error occurs when writing.
0665: */
0666: private void printSpace(int n) throws IOException {
0667: final java.io.Writer writer = m_writer;
0668: for (int i = 0; i < n; i++) {
0669: writer.write(' ');
0670: }
0671:
0672: }
0673:
0674: /**
0675: * Report an attribute type declaration.
0676: *
0677: * <p>Only the effective (first) declaration for an attribute will
0678: * be reported. The type will be one of the strings "CDATA",
0679: * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
0680: * "ENTITIES", or "NOTATION", or a parenthesized token group with
0681: * the separator "|" and all whitespace removed.</p>
0682: *
0683: * @param eName The name of the associated element.
0684: * @param aName The name of the attribute.
0685: * @param type A string representing the attribute type.
0686: * @param valueDefault A string representing the attribute default
0687: * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
0688: * none of these applies.
0689: * @param value A string representing the attribute's default value,
0690: * or null if there is none.
0691: * @exception SAXException The application may raise an exception.
0692: */
0693: public void attributeDecl(String eName, String aName, String type,
0694: String valueDefault, String value) throws SAXException {
0695: // Do not inline external DTD
0696: if (m_inExternalDTD)
0697: return;
0698: try {
0699: final java.io.Writer writer = m_writer;
0700: DTDprolog();
0701:
0702: writer.write("<!ATTLIST ");
0703: writer.write(eName);
0704: writer.write(' ');
0705:
0706: writer.write(aName);
0707: writer.write(' ');
0708: writer.write(type);
0709: if (valueDefault != null) {
0710: writer.write(' ');
0711: writer.write(valueDefault);
0712: }
0713:
0714: //writer.write(" ");
0715: //writer.write(value);
0716: writer.write('>');
0717: writer.write(m_lineSep, 0, m_lineSepLen);
0718: } catch (IOException e) {
0719: throw new SAXException(e);
0720: }
0721: }
0722:
0723: /**
0724: * Get the character stream where the events will be serialized to.
0725: *
0726: * @return Reference to the result Writer, or null.
0727: */
0728: public Writer getWriter() {
0729: return m_writer;
0730: }
0731:
0732: /**
0733: * Report a parsed external entity declaration.
0734: *
0735: * <p>Only the effective (first) declaration for each entity
0736: * will be reported.</p>
0737: *
0738: * @param name The name of the entity. If it is a parameter
0739: * entity, the name will begin with '%'.
0740: * @param publicId The declared public identifier of the entity, or
0741: * null if none was declared.
0742: * @param systemId The declared system identifier of the entity.
0743: * @exception SAXException The application may raise an exception.
0744: * @see #internalEntityDecl
0745: * @see org.xml.sax.DTDHandler#unparsedEntityDecl
0746: */
0747: public void externalEntityDecl(String name, String publicId,
0748: String systemId) throws SAXException {
0749: try {
0750: DTDprolog();
0751:
0752: m_writer.write("<!ENTITY ");
0753: m_writer.write(name);
0754: if (publicId != null) {
0755: m_writer.write(" PUBLIC \"");
0756: m_writer.write(publicId);
0757:
0758: } else {
0759: m_writer.write(" SYSTEM \"");
0760: m_writer.write(systemId);
0761: }
0762: m_writer.write("\" >");
0763: m_writer.write(m_lineSep, 0, m_lineSepLen);
0764: } catch (IOException e) {
0765: // TODO Auto-generated catch block
0766: e.printStackTrace();
0767: }
0768:
0769: }
0770:
0771: /**
0772: * Tell if this character can be written without escaping.
0773: */
0774: protected boolean escapingNotNeeded(char ch) {
0775: final boolean ret;
0776: if (ch < 127) {
0777: // This is the old/fast code here, but is this
0778: // correct for all encodings?
0779: if (ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch))
0780: ret = true;
0781: else
0782: ret = false;
0783: } else {
0784: ret = m_encodingInfo.isInEncoding(ch);
0785: }
0786: return ret;
0787: }
0788:
0789: /**
0790: * Once a surrogate has been detected, write out the pair of
0791: * characters if it is in the encoding, or if there is no
0792: * encoding, otherwise write out an entity reference
0793: * of the value of the unicode code point of the character
0794: * represented by the high/low surrogate pair.
0795: * <p>
0796: * An exception is thrown if there is no low surrogate in the pair,
0797: * because the array ends unexpectely, or if the low char is there
0798: * but its value is such that it is not a low surrogate.
0799: *
0800: * @param c the first (high) part of the surrogate, which
0801: * must be confirmed before calling this method.
0802: * @param ch Character array.
0803: * @param i position Where the surrogate was detected.
0804: * @param end The end index of the significant characters.
0805: * @return 0 if the pair of characters was written out as-is,
0806: * the unicode code point of the character represented by
0807: * the surrogate pair if an entity reference with that value
0808: * was written out.
0809: *
0810: * @throws IOException
0811: * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
0812: */
0813: protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
0814: throws IOException {
0815: int codePoint = 0;
0816: if (i + 1 >= end) {
0817: throw new IOException(Utils.messages.createMessage(
0818: MsgKey.ER_INVALID_UTF16_SURROGATE,
0819: new Object[] { Integer.toHexString((int) c) }));
0820: }
0821:
0822: final char high = c;
0823: final char low = ch[i + 1];
0824: if (!Encodings.isLowUTF16Surrogate(low)) {
0825: throw new IOException(Utils.messages.createMessage(
0826: MsgKey.ER_INVALID_UTF16_SURROGATE,
0827: new Object[] { Integer.toHexString((int) c) + " "
0828: + Integer.toHexString(low) }));
0829: }
0830:
0831: final java.io.Writer writer = m_writer;
0832:
0833: // If we make it to here we have a valid high, low surrogate pair
0834: if (m_encodingInfo.isInEncoding(c, low)) {
0835: // If the character formed by the surrogate pair
0836: // is in the encoding, so just write it out
0837: writer.write(ch, i, 2);
0838: } else {
0839: // Don't know what to do with this char, it is
0840: // not in the encoding and not a high char in
0841: // a surrogate pair, so write out as an entity ref
0842: final String encoding = getEncoding();
0843: if (encoding != null) {
0844: /* The output encoding is known,
0845: * so somthing is wrong.
0846: */
0847: codePoint = Encodings.toCodePoint(high, low);
0848: // not in the encoding, so write out a character reference
0849: writer.write('&');
0850: writer.write('#');
0851: writer.write(Integer.toString(codePoint));
0852: writer.write(';');
0853: } else {
0854: /* The output encoding is not known,
0855: * so just write it out as-is.
0856: */
0857: writer.write(ch, i, 2);
0858: }
0859: }
0860: // non-zero only if character reference was written out.
0861: return codePoint;
0862: }
0863:
0864: /**
0865: * Handle one of the default entities, return false if it
0866: * is not a default entity.
0867: *
0868: * @param ch character to be escaped.
0869: * @param i index into character array.
0870: * @param chars non-null reference to character array.
0871: * @param len length of chars.
0872: * @param fromTextNode true if the characters being processed
0873: * are from a text node, false if they are from an attribute value
0874: * @param escLF true if the linefeed should be escaped.
0875: *
0876: * @return i+1 if the character was written, else i.
0877: *
0878: * @throws java.io.IOException
0879: */
0880: protected int accumDefaultEntity(java.io.Writer writer, char ch,
0881: int i, char[] chars, int len, boolean fromTextNode,
0882: boolean escLF) throws IOException {
0883:
0884: if (!escLF && CharInfo.S_LINEFEED == ch) {
0885: writer.write(m_lineSep, 0, m_lineSepLen);
0886: } else {
0887: // if this is text node character and a special one of those,
0888: // or if this is a character from attribute value and a special one of those
0889: if ((fromTextNode && m_charInfo.isSpecialTextChar(ch))
0890: || (!fromTextNode && m_charInfo
0891: .isSpecialAttrChar(ch))) {
0892: String outputStringForChar = m_charInfo
0893: .getOutputStringForChar(ch);
0894:
0895: if (null != outputStringForChar) {
0896: writer.write(outputStringForChar);
0897: } else
0898: return i;
0899: } else
0900: return i;
0901: }
0902:
0903: return i + 1;
0904:
0905: }
0906:
0907: /**
0908: * Normalize the characters, but don't escape.
0909: *
0910: * @param ch The characters from the XML document.
0911: * @param start The start position in the array.
0912: * @param length The number of characters to read from the array.
0913: * @param isCData true if a CDATA block should be built around the characters.
0914: * @param useSystemLineSeparator true if the operating systems
0915: * end-of-line separator should be output rather than a new-line character.
0916: *
0917: * @throws IOException
0918: * @throws org.xml.sax.SAXException
0919: */
0920: void writeNormalizedChars(char ch[], int start, int length,
0921: boolean isCData, boolean useSystemLineSeparator)
0922: throws IOException, org.xml.sax.SAXException {
0923: final java.io.Writer writer = m_writer;
0924: int end = start + length;
0925:
0926: for (int i = start; i < end; i++) {
0927: char c = ch[i];
0928:
0929: if (CharInfo.S_LINEFEED == c && useSystemLineSeparator) {
0930: writer.write(m_lineSep, 0, m_lineSepLen);
0931: } else if (isCData && (!escapingNotNeeded(c))) {
0932: // if (i != 0)
0933: if (m_cdataTagOpen)
0934: closeCDATA();
0935:
0936: // This needs to go into a function...
0937: if (Encodings.isHighUTF16Surrogate(c)) {
0938: writeUTF16Surrogate(c, ch, i, end);
0939: i++; // process two input characters
0940: } else {
0941: writer.write("&#");
0942:
0943: String intStr = Integer.toString((int) c);
0944:
0945: writer.write(intStr);
0946: writer.write(';');
0947: }
0948:
0949: // if ((i != 0) && (i < (end - 1)))
0950: // if (!m_cdataTagOpen && (i < (end - 1)))
0951: // {
0952: // writer.write(CDATA_DELIMITER_OPEN);
0953: // m_cdataTagOpen = true;
0954: // }
0955: } else if (isCData
0956: && ((i < (end - 2)) && (']' == c)
0957: && (']' == ch[i + 1]) && ('>' == ch[i + 2]))) {
0958: writer.write(CDATA_CONTINUE);
0959:
0960: i += 2;
0961: } else {
0962: if (escapingNotNeeded(c)) {
0963: if (isCData && !m_cdataTagOpen) {
0964: writer.write(CDATA_DELIMITER_OPEN);
0965: m_cdataTagOpen = true;
0966: }
0967: writer.write(c);
0968: }
0969:
0970: // This needs to go into a function...
0971: else if (Encodings.isHighUTF16Surrogate(c)) {
0972: if (m_cdataTagOpen)
0973: closeCDATA();
0974: writeUTF16Surrogate(c, ch, i, end);
0975: i++; // process two input characters
0976: } else {
0977: if (m_cdataTagOpen)
0978: closeCDATA();
0979: writer.write("&#");
0980:
0981: String intStr = Integer.toString((int) c);
0982:
0983: writer.write(intStr);
0984: writer.write(';');
0985: }
0986: }
0987: }
0988:
0989: }
0990:
0991: /**
0992: * Ends an un-escaping section.
0993: *
0994: * @see #startNonEscaping
0995: *
0996: * @throws org.xml.sax.SAXException
0997: */
0998: public void endNonEscaping() throws org.xml.sax.SAXException {
0999: m_disableOutputEscapingStates.pop();
1000: }
1001:
1002: /**
1003: * Starts an un-escaping section. All characters printed within an un-
1004: * escaping section are printed as is, without escaping special characters
1005: * into entity references. Only XML and HTML serializers need to support
1006: * this method.
1007: * <p> The contents of the un-escaping section will be delivered through the
1008: * regular <tt>characters</tt> event.
1009: *
1010: * @throws org.xml.sax.SAXException
1011: */
1012: public void startNonEscaping() throws org.xml.sax.SAXException {
1013: m_disableOutputEscapingStates.push(true);
1014: }
1015:
1016: /**
1017: * Receive notification of cdata.
1018: *
1019: * <p>The Parser will call this method to report each chunk of
1020: * character data. SAX parsers may return all contiguous character
1021: * data in a single chunk, or they may split it into several
1022: * chunks; however, all of the characters in any single event
1023: * must come from the same external entity, so that the Locator
1024: * provides useful information.</p>
1025: *
1026: * <p>The application must not attempt to read from the array
1027: * outside of the specified range.</p>
1028: *
1029: * <p>Note that some parsers will report whitespace using the
1030: * ignorableWhitespace() method rather than this one (validating
1031: * parsers must do so).</p>
1032: *
1033: * @param ch The characters from the XML document.
1034: * @param start The start position in the array.
1035: * @param length The number of characters to read from the array.
1036: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1037: * wrapping another exception.
1038: * @see #ignorableWhitespace
1039: * @see org.xml.sax.Locator
1040: *
1041: * @throws org.xml.sax.SAXException
1042: */
1043: protected void cdata(char ch[], int start, final int length)
1044: throws org.xml.sax.SAXException {
1045:
1046: try {
1047: final int old_start = start;
1048: if (m_elemContext.m_startTagOpen) {
1049: closeStartTag();
1050: m_elemContext.m_startTagOpen = false;
1051: }
1052: m_ispreserve = true;
1053:
1054: if (shouldIndent())
1055: indent();
1056:
1057: boolean writeCDataBrackets = (((length >= 1) && escapingNotNeeded(ch[start])));
1058:
1059: /* Write out the CDATA opening delimiter only if
1060: * we are supposed to, and if we are not already in
1061: * the middle of a CDATA section
1062: */
1063: if (writeCDataBrackets && !m_cdataTagOpen) {
1064: m_writer.write(CDATA_DELIMITER_OPEN);
1065: m_cdataTagOpen = true;
1066: }
1067:
1068: // writer.write(ch, start, length);
1069: if (isEscapingDisabled()) {
1070: charactersRaw(ch, start, length);
1071: } else
1072: writeNormalizedChars(ch, start, length, true,
1073: m_lineSepUse);
1074:
1075: /* used to always write out CDATA closing delimiter here,
1076: * but now we delay, so that we can merge CDATA sections on output.
1077: * need to write closing delimiter later
1078: */
1079: if (writeCDataBrackets) {
1080: /* if the CDATA section ends with ] don't leave it open
1081: * as there is a chance that an adjacent CDATA sections
1082: * starts with ]>.
1083: * We don't want to merge ]] with > , or ] with ]>
1084: */
1085: if (ch[start + length - 1] == ']')
1086: closeCDATA();
1087: }
1088:
1089: // time to fire off CDATA event
1090: if (m_tracer != null)
1091: super .fireCDATAEvent(ch, old_start, length);
1092: } catch (IOException ioe) {
1093: throw new org.xml.sax.SAXException(Utils.messages
1094: .createMessage(MsgKey.ER_OIERROR, null), ioe);
1095: //"IO error", ioe);
1096: }
1097: }
1098:
1099: /**
1100: * Tell if the character escaping should be disabled for the current state.
1101: *
1102: * @return true if the character escaping should be disabled.
1103: */
1104: private boolean isEscapingDisabled() {
1105: return m_disableOutputEscapingStates.peekOrFalse();
1106: }
1107:
1108: /**
1109: * If available, when the disable-output-escaping attribute is used,
1110: * output raw text without escaping.
1111: *
1112: * @param ch The characters from the XML document.
1113: * @param start The start position in the array.
1114: * @param length The number of characters to read from the array.
1115: *
1116: * @throws org.xml.sax.SAXException
1117: */
1118: protected void charactersRaw(char ch[], int start, int length)
1119: throws org.xml.sax.SAXException {
1120:
1121: if (m_inEntityRef)
1122: return;
1123: try {
1124: if (m_elemContext.m_startTagOpen) {
1125: closeStartTag();
1126: m_elemContext.m_startTagOpen = false;
1127: }
1128:
1129: m_ispreserve = true;
1130:
1131: m_writer.write(ch, start, length);
1132: } catch (IOException e) {
1133: throw new SAXException(e);
1134: }
1135:
1136: }
1137:
1138: /**
1139: * Receive notification of character data.
1140: *
1141: * <p>The Parser will call this method to report each chunk of
1142: * character data. SAX parsers may return all contiguous character
1143: * data in a single chunk, or they may split it into several
1144: * chunks; however, all of the characters in any single event
1145: * must come from the same external entity, so that the Locator
1146: * provides useful information.</p>
1147: *
1148: * <p>The application must not attempt to read from the array
1149: * outside of the specified range.</p>
1150: *
1151: * <p>Note that some parsers will report whitespace using the
1152: * ignorableWhitespace() method rather than this one (validating
1153: * parsers must do so).</p>
1154: *
1155: * @param chars The characters from the XML document.
1156: * @param start The start position in the array.
1157: * @param length The number of characters to read from the array.
1158: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1159: * wrapping another exception.
1160: * @see #ignorableWhitespace
1161: * @see org.xml.sax.Locator
1162: *
1163: * @throws org.xml.sax.SAXException
1164: */
1165: public void characters(final char chars[], final int start,
1166: final int length) throws org.xml.sax.SAXException {
1167: // It does not make sense to continue with rest of the method if the number of
1168: // characters to read from array is 0.
1169: // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
1170: // is created if string is empty.
1171: if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
1172: return;
1173: if (m_elemContext.m_startTagOpen) {
1174: closeStartTag();
1175: m_elemContext.m_startTagOpen = false;
1176: } else if (m_needToCallStartDocument) {
1177: startDocumentInternal();
1178: }
1179:
1180: if (m_cdataStartCalled || m_elemContext.m_isCdataSection) {
1181: /* either due to startCDATA() being called or due to
1182: * cdata-section-elements atribute, we need this as cdata
1183: */
1184: cdata(chars, start, length);
1185:
1186: return;
1187: }
1188:
1189: if (m_cdataTagOpen)
1190: closeCDATA();
1191: // the check with _escaping is a bit of a hack for XLSTC
1192:
1193: if (m_disableOutputEscapingStates.peekOrFalse()
1194: || (!m_escaping)) {
1195: charactersRaw(chars, start, length);
1196:
1197: // time to fire off characters generation event
1198: if (m_tracer != null)
1199: super .fireCharEvent(chars, start, length);
1200:
1201: return;
1202: }
1203:
1204: if (m_elemContext.m_startTagOpen) {
1205: closeStartTag();
1206: m_elemContext.m_startTagOpen = false;
1207: }
1208:
1209: try {
1210: int i;
1211: char ch1;
1212: int startClean;
1213:
1214: // skip any leading whitspace
1215: // don't go off the end and use a hand inlined version
1216: // of isWhitespace(ch)
1217: final int end = start + length;
1218: int lastDirty = start - 1; // last character that needed processing
1219: for (i = start; ((i < end) && ((ch1 = chars[i]) == 0x20
1220: || (ch1 == 0xA && m_lineSepUse) || ch1 == 0xD || ch1 == 0x09)); i++) {
1221: /*
1222: * We are processing leading whitespace, but are doing the same
1223: * processing for dirty characters here as for non-whitespace.
1224: *
1225: */
1226: if (!m_charInfo.isTextASCIIClean(ch1)) {
1227: lastDirty = processDirty(chars, end, i, ch1,
1228: lastDirty, true);
1229: i = lastDirty;
1230: }
1231: }
1232: /* If there is some non-whitespace, mark that we may need
1233: * to preserve this. This is only important if we have indentation on.
1234: */
1235: if (i < end)
1236: m_ispreserve = true;
1237:
1238: // int lengthClean; // number of clean characters in a row
1239: // final boolean[] isAsciiClean = m_charInfo.getASCIIClean();
1240:
1241: final boolean isXML10 = XMLVERSION10.equals(getVersion());
1242: // we've skipped the leading whitespace, now deal with the rest
1243: for (; i < end; i++) {
1244: {
1245: // A tight loop to skip over common clean chars
1246: // This tight loop makes it easier for the JIT
1247: // to optimize.
1248: char ch2;
1249: while (i < end && ((ch2 = chars[i]) < 127)
1250: && m_charInfo.isTextASCIIClean(ch2))
1251: i++;
1252: if (i == end)
1253: break;
1254: }
1255:
1256: final char ch = chars[i];
1257: /* The check for isCharacterInC0orC1Ranger and
1258: * isNELorLSEPCharacter has been added
1259: * to support Control Characters in XML 1.1
1260: */
1261: if (!isCharacterInC0orC1Range(ch)
1262: && (isXML10 || !isNELorLSEPCharacter(ch))
1263: && (escapingNotNeeded(ch) && (!m_charInfo
1264: .isSpecialTextChar(ch))) || ('"' == ch)) {
1265: ; // a character needing no special processing
1266: } else {
1267: lastDirty = processDirty(chars, end, i, ch,
1268: lastDirty, true);
1269: i = lastDirty;
1270: }
1271: }
1272:
1273: // we've reached the end. Any clean characters at the
1274: // end of the array than need to be written out?
1275: startClean = lastDirty + 1;
1276: if (i > startClean) {
1277: int lengthClean = i - startClean;
1278: m_writer.write(chars, startClean, lengthClean);
1279: }
1280:
1281: // For indentation purposes, mark that we've just writen text out
1282: m_isprevtext = true;
1283: } catch (IOException e) {
1284: throw new SAXException(e);
1285: }
1286:
1287: // time to fire off characters generation event
1288: if (m_tracer != null)
1289: super .fireCharEvent(chars, start, length);
1290: }
1291:
1292: /**
1293: * This method checks if a given character is between C0 or C1 range
1294: * of Control characters.
1295: * This method is added to support Control Characters for XML 1.1
1296: * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
1297: * return false. Since they are whitespace characters, no special processing is needed.
1298: *
1299: * @param ch
1300: * @return boolean
1301: */
1302: private static boolean isCharacterInC0orC1Range(char ch) {
1303: if (ch == 0x09 || ch == 0x0A || ch == 0x0D)
1304: return false;
1305: else
1306: return (ch >= 0x7F && ch <= 0x9F)
1307: || (ch >= 0x01 && ch <= 0x1F);
1308: }
1309:
1310: /**
1311: * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
1312: * These are new end of line charcters added in XML 1.1. These characters must be
1313: * written as Numeric Character References (NCR) in XML 1.1 output document.
1314: *
1315: * @param ch
1316: * @return boolean
1317: */
1318: private static boolean isNELorLSEPCharacter(char ch) {
1319: return (ch == 0x85 || ch == 0x2028);
1320: }
1321:
1322: /**
1323: * Process a dirty character and any preeceding clean characters
1324: * that were not yet processed.
1325: * @param chars array of characters being processed
1326: * @param end one (1) beyond the last character
1327: * in chars to be processed
1328: * @param i the index of the dirty character
1329: * @param ch the character in chars[i]
1330: * @param lastDirty the last dirty character previous to i
1331: * @param fromTextNode true if the characters being processed are
1332: * from a text node, false if they are from an attribute value.
1333: * @return the index of the last character processed
1334: */
1335: private int processDirty(char[] chars, int end, int i, char ch,
1336: int lastDirty, boolean fromTextNode) throws IOException {
1337: int startClean = lastDirty + 1;
1338: // if we have some clean characters accumulated
1339: // process them before the dirty one.
1340: if (i > startClean) {
1341: int lengthClean = i - startClean;
1342: m_writer.write(chars, startClean, lengthClean);
1343: }
1344:
1345: // process the "dirty" character
1346: if (CharInfo.S_LINEFEED == ch && fromTextNode) {
1347: m_writer.write(m_lineSep, 0, m_lineSepLen);
1348: } else {
1349: startClean = accumDefaultEscape(m_writer, (char) ch, i,
1350: chars, end, fromTextNode, false);
1351: i = startClean - 1;
1352: }
1353: // Return the index of the last character that we just processed
1354: // which is a dirty character.
1355: return i;
1356: }
1357:
1358: /**
1359: * Receive notification of character data.
1360: *
1361: * @param s The string of characters to process.
1362: *
1363: * @throws org.xml.sax.SAXException
1364: */
1365: public void characters(String s) throws org.xml.sax.SAXException {
1366: if (m_inEntityRef && !m_expandDTDEntities)
1367: return;
1368: final int length = s.length();
1369: if (length > m_charsBuff.length) {
1370: m_charsBuff = new char[length * 2 + 1];
1371: }
1372: s.getChars(0, length, m_charsBuff, 0);
1373: characters(m_charsBuff, 0, length);
1374: }
1375:
1376: /**
1377: * Escape and writer.write a character.
1378: *
1379: * @param ch character to be escaped.
1380: * @param i index into character array.
1381: * @param chars non-null reference to character array.
1382: * @param len length of chars.
1383: * @param fromTextNode true if the characters being processed are
1384: * from a text node, false if the characters being processed are from
1385: * an attribute value.
1386: * @param escLF true if the linefeed should be escaped.
1387: *
1388: * @return i+1 if a character was written, i+2 if two characters
1389: * were written out, else return i.
1390: *
1391: * @throws org.xml.sax.SAXException
1392: */
1393: protected int accumDefaultEscape(Writer writer, char ch, int i,
1394: char[] chars, int len, boolean fromTextNode, boolean escLF)
1395: throws IOException {
1396:
1397: int pos = accumDefaultEntity(writer, ch, i, chars, len,
1398: fromTextNode, escLF);
1399:
1400: if (i == pos) {
1401: if (Encodings.isHighUTF16Surrogate(ch)) {
1402:
1403: // Should be the UTF-16 low surrogate of the hig/low pair.
1404: char next;
1405: // Unicode code point formed from the high/low pair.
1406: int codePoint = 0;
1407:
1408: if (i + 1 >= len) {
1409: throw new IOException(Utils.messages.createMessage(
1410: MsgKey.ER_INVALID_UTF16_SURROGATE,
1411: new Object[] { Integer.toHexString(ch) }));
1412: //"Invalid UTF-16 surrogate detected: "
1413:
1414: //+Integer.toHexString(ch)+ " ?");
1415: } else {
1416: next = chars[++i];
1417:
1418: if (!(Encodings.isLowUTF16Surrogate(next)))
1419: throw new IOException(
1420: Utils.messages
1421: .createMessage(
1422: MsgKey.ER_INVALID_UTF16_SURROGATE,
1423: new Object[] { Integer
1424: .toHexString(ch)
1425: + " "
1426: + Integer
1427: .toHexString(next) }));
1428: //"Invalid UTF-16 surrogate detected: "
1429:
1430: //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
1431: codePoint = Encodings.toCodePoint(ch, next);
1432: }
1433:
1434: writer.write("&#");
1435: writer.write(Integer.toString(codePoint));
1436: writer.write(';');
1437: pos += 2; // count the two characters that went into writing out this entity
1438: } else {
1439: /* This if check is added to support control characters in XML 1.1.
1440: * If a character is a Control Character within C0 and C1 range, it is desirable
1441: * to write it out as Numeric Character Reference(NCR) regardless of XML Version
1442: * being used for output document.
1443: */
1444: if (isCharacterInC0orC1Range(ch)
1445: || (XMLVERSION11.equals(getVersion()) && isNELorLSEPCharacter(ch))) {
1446: writer.write("&#");
1447: writer.write(Integer.toString(ch));
1448: writer.write(';');
1449: } else if ((!escapingNotNeeded(ch) || ((fromTextNode && m_charInfo
1450: .isSpecialTextChar(ch)) || (!fromTextNode && m_charInfo
1451: .isSpecialAttrChar(ch))))
1452: && m_elemContext.m_currentElemDepth > 0) {
1453: writer.write("&#");
1454: writer.write(Integer.toString(ch));
1455: writer.write(';');
1456: } else {
1457: writer.write(ch);
1458: }
1459: pos++; // count the single character that was processed
1460: }
1461:
1462: }
1463: return pos;
1464: }
1465:
1466: /**
1467: * Receive notification of the beginning of an element, although this is a
1468: * SAX method additional namespace or attribute information can occur before
1469: * or after this call, that is associated with this element.
1470: *
1471: *
1472: * @param namespaceURI The Namespace URI, or the empty string if the
1473: * element has no Namespace URI or if Namespace
1474: * processing is not being performed.
1475: * @param localName The local name (without prefix), or the
1476: * empty string if Namespace processing is not being
1477: * performed.
1478: * @param name The element type name.
1479: * @param atts The attributes attached to the element, if any.
1480: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1481: * wrapping another exception.
1482: * @see org.xml.sax.ContentHandler#startElement
1483: * @see org.xml.sax.ContentHandler#endElement
1484: * @see org.xml.sax.AttributeList
1485: *
1486: * @throws org.xml.sax.SAXException
1487: */
1488: public void startElement(String namespaceURI, String localName,
1489: String name, Attributes atts)
1490: throws org.xml.sax.SAXException {
1491: if (m_inEntityRef)
1492: return;
1493:
1494: if (m_needToCallStartDocument) {
1495: startDocumentInternal();
1496: m_needToCallStartDocument = false;
1497: } else if (m_cdataTagOpen)
1498: closeCDATA();
1499: try {
1500: if ((true == m_needToOutputDocTypeDecl)
1501: && (null != getDoctypeSystem())) {
1502: outputDocTypeDecl(name, true);
1503: }
1504:
1505: m_needToOutputDocTypeDecl = false;
1506:
1507: /* before we over-write the current elementLocalName etc.
1508: * lets close out the old one (if we still need to)
1509: */
1510: if (m_elemContext.m_startTagOpen) {
1511: closeStartTag();
1512: m_elemContext.m_startTagOpen = false;
1513: }
1514:
1515: if (namespaceURI != null)
1516: ensurePrefixIsDeclared(namespaceURI, name);
1517:
1518: m_ispreserve = false;
1519:
1520: if (shouldIndent() && m_startNewLine) {
1521: indent();
1522: }
1523:
1524: m_startNewLine = true;
1525:
1526: final java.io.Writer writer = m_writer;
1527: writer.write('<');
1528: writer.write(name);
1529: } catch (IOException e) {
1530: throw new SAXException(e);
1531: }
1532:
1533: // process the attributes now, because after this SAX call they might be gone
1534: if (atts != null)
1535: addAttributes(atts);
1536:
1537: m_elemContext = m_elemContext.push(namespaceURI, localName,
1538: name);
1539: m_isprevtext = false;
1540:
1541: if (m_tracer != null)
1542: firePseudoAttributes();
1543: }
1544:
1545: /**
1546: * Receive notification of the beginning of an element, additional
1547: * namespace or attribute information can occur before or after this call,
1548: * that is associated with this element.
1549: *
1550: *
1551: * @param elementNamespaceURI The Namespace URI, or the empty string if the
1552: * element has no Namespace URI or if Namespace
1553: * processing is not being performed.
1554: * @param elementLocalName The local name (without prefix), or the
1555: * empty string if Namespace processing is not being
1556: * performed.
1557: * @param elementName The element type name.
1558: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1559: * wrapping another exception.
1560: * @see org.xml.sax.ContentHandler#startElement
1561: * @see org.xml.sax.ContentHandler#endElement
1562: * @see org.xml.sax.AttributeList
1563: *
1564: * @throws org.xml.sax.SAXException
1565: */
1566: public void startElement(String elementNamespaceURI,
1567: String elementLocalName, String elementName)
1568: throws SAXException {
1569: startElement(elementNamespaceURI, elementLocalName,
1570: elementName, null);
1571: }
1572:
1573: public void startElement(String elementName) throws SAXException {
1574: startElement(null, null, elementName, null);
1575: }
1576:
1577: /**
1578: * Output the doc type declaration.
1579: *
1580: * @param name non-null reference to document type name.
1581: * NEEDSDOC @param closeDecl
1582: *
1583: * @throws java.io.IOException
1584: */
1585: void outputDocTypeDecl(String name, boolean closeDecl)
1586: throws SAXException {
1587: if (m_cdataTagOpen)
1588: closeCDATA();
1589: try {
1590: final java.io.Writer writer = m_writer;
1591: writer.write("<!DOCTYPE ");
1592: writer.write(name);
1593:
1594: String doctypePublic = getDoctypePublic();
1595: if (null != doctypePublic) {
1596: writer.write(" PUBLIC \"");
1597: writer.write(doctypePublic);
1598: writer.write('\"');
1599: }
1600:
1601: String doctypeSystem = getDoctypeSystem();
1602: if (null != doctypeSystem) {
1603: if (null == doctypePublic)
1604: writer.write(" SYSTEM \"");
1605: else
1606: writer.write(" \"");
1607:
1608: writer.write(doctypeSystem);
1609:
1610: if (closeDecl) {
1611: writer.write("\">");
1612: writer.write(m_lineSep, 0, m_lineSepLen);
1613: closeDecl = false; // done closing
1614: } else
1615: writer.write('\"');
1616: }
1617: boolean dothis = false;
1618: if (dothis ) {
1619: // at one point this code seemed right,
1620: // but not anymore - Brian M.
1621: if (closeDecl) {
1622: writer.write('>');
1623: writer.write(m_lineSep, 0, m_lineSepLen);
1624: }
1625: }
1626: } catch (IOException e) {
1627: throw new SAXException(e);
1628: }
1629: }
1630:
1631: /**
1632: * Process the attributes, which means to write out the currently
1633: * collected attributes to the writer. The attributes are not
1634: * cleared by this method
1635: *
1636: * @param writer the writer to write processed attributes to.
1637: * @param nAttrs the number of attributes in m_attributes
1638: * to be processed
1639: *
1640: * @throws java.io.IOException
1641: * @throws org.xml.sax.SAXException
1642: */
1643: public void processAttributes(java.io.Writer writer, int nAttrs)
1644: throws IOException, SAXException {
1645: /* real SAX attributes are not passed in, so process the
1646: * attributes that were collected after the startElement call.
1647: * _attribVector is a "cheap" list for Stream serializer output
1648: * accumulated over a series of calls to attribute(name,value)
1649: */
1650:
1651: String encoding = getEncoding();
1652: for (int i = 0; i < nAttrs; i++) {
1653: // elementAt is JDK 1.1.8
1654: final String name = m_attributes.getQName(i);
1655: final String value = m_attributes.getValue(i);
1656: writer.write(' ');
1657: writer.write(name);
1658: writer.write("=\"");
1659: writeAttrString(writer, value, encoding);
1660: writer.write('\"');
1661: }
1662: }
1663:
1664: /**
1665: * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
1666: * and UTF-16 surrogates for chracter references <CODE>&#xnn</CODE>.
1667: *
1668: * @param string String to convert to XML format.
1669: * @param encoding CURRENTLY NOT IMPLEMENTED.
1670: *
1671: * @throws java.io.IOException
1672: */
1673: public void writeAttrString(Writer writer, String string,
1674: String encoding) throws IOException {
1675: final int len = string.length();
1676: if (len > m_attrBuff.length) {
1677: m_attrBuff = new char[len * 2 + 1];
1678: }
1679: string.getChars(0, len, m_attrBuff, 0);
1680: final char[] stringChars = m_attrBuff;
1681:
1682: for (int i = 0; i < len; i++) {
1683: char ch = stringChars[i];
1684: if (escapingNotNeeded(ch)
1685: && (!m_charInfo.isSpecialAttrChar(ch))) {
1686: writer.write(ch);
1687: } else { // I guess the parser doesn't normalize cr/lf in attributes. -sb
1688: // if ((CharInfo.S_CARRIAGERETURN == ch)
1689: // && ((i + 1) < len)
1690: // && (CharInfo.S_LINEFEED == stringChars[i + 1]))
1691: // {
1692: // i++;
1693: // ch = CharInfo.S_LINEFEED;
1694: // }
1695:
1696: accumDefaultEscape(writer, ch, i, stringChars, len,
1697: false, true);
1698: }
1699: }
1700:
1701: }
1702:
1703: /**
1704: * Receive notification of the end of an element.
1705: *
1706: *
1707: * @param namespaceURI The Namespace URI, or the empty string if the
1708: * element has no Namespace URI or if Namespace
1709: * processing is not being performed.
1710: * @param localName The local name (without prefix), or the
1711: * empty string if Namespace processing is not being
1712: * performed.
1713: * @param name The element type name
1714: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1715: * wrapping another exception.
1716: *
1717: * @throws org.xml.sax.SAXException
1718: */
1719: public void endElement(String namespaceURI, String localName,
1720: String name) throws org.xml.sax.SAXException {
1721: if (m_inEntityRef)
1722: return;
1723:
1724: // namespaces declared at the current depth are no longer valid
1725: // so get rid of them
1726: m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth,
1727: null);
1728:
1729: try {
1730: final java.io.Writer writer = m_writer;
1731: if (m_elemContext.m_startTagOpen) {
1732: if (m_tracer != null)
1733: super .fireStartElem(m_elemContext.m_elementName);
1734: int nAttrs = m_attributes.getLength();
1735: if (nAttrs > 0) {
1736: processAttributes(m_writer, nAttrs);
1737: // clear attributes object for re-use with next element
1738: m_attributes.clear();
1739: }
1740: if (m_spaceBeforeClose)
1741: writer.write(" />");
1742: else
1743: writer.write("/>");
1744: /* don't need to pop cdataSectionState because
1745: * this element ended so quickly that we didn't get
1746: * to push the state.
1747: */
1748:
1749: } else {
1750: if (m_cdataTagOpen)
1751: closeCDATA();
1752:
1753: if (shouldIndent())
1754: indent(m_elemContext.m_currentElemDepth - 1);
1755: writer.write('<');
1756: writer.write('/');
1757: writer.write(name);
1758: writer.write('>');
1759: }
1760: } catch (IOException e) {
1761: throw new SAXException(e);
1762: }
1763:
1764: if (!m_elemContext.m_startTagOpen && m_doIndent) {
1765: m_ispreserve = m_preserves.isEmpty() ? false : m_preserves
1766: .pop();
1767: }
1768:
1769: m_isprevtext = false;
1770:
1771: // fire off the end element event
1772: if (m_tracer != null)
1773: super .fireEndElem(name);
1774: m_elemContext = m_elemContext.m_prev;
1775: }
1776:
1777: /**
1778: * Receive notification of the end of an element.
1779: * @param name The element type name
1780: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1781: * wrapping another exception.
1782: */
1783: public void endElement(String name) throws org.xml.sax.SAXException {
1784: endElement(null, null, name);
1785: }
1786:
1787: /**
1788: * Begin the scope of a prefix-URI Namespace mapping
1789: * just before another element is about to start.
1790: * This call will close any open tags so that the prefix mapping
1791: * will not apply to the current element, but the up comming child.
1792: *
1793: * @see org.xml.sax.ContentHandler#startPrefixMapping
1794: *
1795: * @param prefix The Namespace prefix being declared.
1796: * @param uri The Namespace URI the prefix is mapped to.
1797: *
1798: * @throws org.xml.sax.SAXException The client may throw
1799: * an exception during processing.
1800: *
1801: */
1802: public void startPrefixMapping(String prefix, String uri)
1803: throws org.xml.sax.SAXException {
1804: // the "true" causes the flush of any open tags
1805: startPrefixMapping(prefix, uri, true);
1806: }
1807:
1808: /**
1809: * Handle a prefix/uri mapping, which is associated with a startElement()
1810: * that is soon to follow. Need to close any open start tag to make
1811: * sure than any name space attributes due to this event are associated wih
1812: * the up comming element, not the current one.
1813: * @see ExtendedContentHandler#startPrefixMapping
1814: *
1815: * @param prefix The Namespace prefix being declared.
1816: * @param uri The Namespace URI the prefix is mapped to.
1817: * @param shouldFlush true if any open tags need to be closed first, this
1818: * will impact which element the mapping applies to (open parent, or its up
1819: * comming child)
1820: * @return returns true if the call made a change to the current
1821: * namespace information, false if it did not change anything, e.g. if the
1822: * prefix/namespace mapping was already in scope from before.
1823: *
1824: * @throws org.xml.sax.SAXException The client may throw
1825: * an exception during processing.
1826: *
1827: *
1828: */
1829: public boolean startPrefixMapping(String prefix, String uri,
1830: boolean shouldFlush) throws org.xml.sax.SAXException {
1831:
1832: /* Remember the mapping, and at what depth it was declared
1833: * This is one greater than the current depth because these
1834: * mappings will apply to the next depth. This is in
1835: * consideration that startElement() will soon be called
1836: */
1837:
1838: boolean pushed;
1839: int pushDepth;
1840: if (shouldFlush) {
1841: flushPending();
1842: // the prefix mapping applies to the child element (one deeper)
1843: pushDepth = m_elemContext.m_currentElemDepth + 1;
1844: } else {
1845: // the prefix mapping applies to the current element
1846: pushDepth = m_elemContext.m_currentElemDepth;
1847: }
1848: pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
1849:
1850: if (pushed) {
1851: /* Brian M.: don't know if we really needto do this. The
1852: * callers of this object should have injected both
1853: * startPrefixMapping and the attributes. We are
1854: * just covering our butt here.
1855: */
1856: String name;
1857: if (EMPTYSTRING.equals(prefix)) {
1858: name = "xmlns";
1859: addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri,
1860: false);
1861: } else {
1862: if (!EMPTYSTRING.equals(uri))
1863: // hack for XSLTC attribset16 test
1864: { // that maps ns1 prefix to "" URI
1865: name = "xmlns:" + prefix;
1866:
1867: /* for something like xmlns:abc="w3.pretend.org"
1868: * the uri is the value, that is why we pass it in the
1869: * value, or 5th slot of addAttributeAlways()
1870: */
1871: addAttributeAlways(XMLNS_URI, prefix, name,
1872: "CDATA", uri, false);
1873: }
1874: }
1875: }
1876: return pushed;
1877: }
1878:
1879: /**
1880: * Receive notification of an XML comment anywhere in the document. This
1881: * callback will be used for comments inside or outside the document
1882: * element, including comments in the external DTD subset (if read).
1883: * @param ch An array holding the characters in the comment.
1884: * @param start The starting position in the array.
1885: * @param length The number of characters to use from the array.
1886: * @throws org.xml.sax.SAXException The application may raise an exception.
1887: */
1888: public void comment(char ch[], int start, int length)
1889: throws org.xml.sax.SAXException {
1890:
1891: int start_old = start;
1892: if (m_inEntityRef)
1893: return;
1894: if (m_elemContext.m_startTagOpen) {
1895: closeStartTag();
1896: m_elemContext.m_startTagOpen = false;
1897: } else if (m_needToCallStartDocument) {
1898: startDocumentInternal();
1899: m_needToCallStartDocument = false;
1900: }
1901:
1902: try {
1903: if (shouldIndent())
1904: indent();
1905:
1906: final int limit = start + length;
1907: boolean wasDash = false;
1908: if (m_cdataTagOpen)
1909: closeCDATA();
1910: final java.io.Writer writer = m_writer;
1911: writer.write(COMMENT_BEGIN);
1912: // Detect occurrences of two consecutive dashes, handle as necessary.
1913: for (int i = start; i < limit; i++) {
1914: if (wasDash && ch[i] == '-') {
1915: writer.write(ch, start, i - start);
1916: writer.write(" -");
1917: start = i + 1;
1918: }
1919: wasDash = (ch[i] == '-');
1920: }
1921:
1922: // if we have some chars in the comment
1923: if (length > 0) {
1924: // Output the remaining characters (if any)
1925: final int remainingChars = (limit - start);
1926: if (remainingChars > 0)
1927: writer.write(ch, start, remainingChars);
1928: // Protect comment end from a single trailing dash
1929: if (ch[limit - 1] == '-')
1930: writer.write(' ');
1931: }
1932: writer.write(COMMENT_END);
1933: } catch (IOException e) {
1934: throw new SAXException(e);
1935: }
1936:
1937: m_startNewLine = true;
1938: // time to generate comment event
1939: if (m_tracer != null)
1940: super .fireCommentEvent(ch, start_old, length);
1941: }
1942:
1943: /**
1944: * Report the end of a CDATA section.
1945: * @throws org.xml.sax.SAXException The application may raise an exception.
1946: *
1947: * @see #startCDATA
1948: */
1949: public void endCDATA() throws org.xml.sax.SAXException {
1950: if (m_cdataTagOpen)
1951: closeCDATA();
1952: m_cdataStartCalled = false;
1953: }
1954:
1955: /**
1956: * Report the end of DTD declarations.
1957: * @throws org.xml.sax.SAXException The application may raise an exception.
1958: * @see #startDTD
1959: */
1960: public void endDTD() throws org.xml.sax.SAXException {
1961: try {
1962: if (m_needToOutputDocTypeDecl) {
1963: outputDocTypeDecl(m_elemContext.m_elementName, false);
1964: m_needToOutputDocTypeDecl = false;
1965: }
1966: final java.io.Writer writer = m_writer;
1967: if (!m_inDoctype)
1968: writer.write("]>");
1969: else {
1970: writer.write('>');
1971: }
1972:
1973: writer.write(m_lineSep, 0, m_lineSepLen);
1974: } catch (IOException e) {
1975: throw new SAXException(e);
1976: }
1977:
1978: }
1979:
1980: /**
1981: * End the scope of a prefix-URI Namespace mapping.
1982: * @see org.xml.sax.ContentHandler#endPrefixMapping
1983: *
1984: * @param prefix The prefix that was being mapping.
1985: * @throws org.xml.sax.SAXException The client may throw
1986: * an exception during processing.
1987: */
1988: public void endPrefixMapping(String prefix)
1989: throws org.xml.sax.SAXException { // do nothing
1990: }
1991:
1992: /**
1993: * Receive notification of ignorable whitespace in element content.
1994: *
1995: * Not sure how to get this invoked quite yet.
1996: *
1997: * @param ch The characters from the XML document.
1998: * @param start The start position in the array.
1999: * @param length The number of characters to read from the array.
2000: * @throws org.xml.sax.SAXException Any SAX exception, possibly
2001: * wrapping another exception.
2002: * @see #characters
2003: *
2004: * @throws org.xml.sax.SAXException
2005: */
2006: public void ignorableWhitespace(char ch[], int start, int length)
2007: throws org.xml.sax.SAXException {
2008:
2009: if (0 == length)
2010: return;
2011: characters(ch, start, length);
2012: }
2013:
2014: /**
2015: * Receive notification of a skipped entity.
2016: * @see org.xml.sax.ContentHandler#skippedEntity
2017: *
2018: * @param name The name of the skipped entity. If it is a
2019: * parameter entity, the name will begin with '%',
2020: * and if it is the external DTD subset, it will be the string
2021: * "[dtd]".
2022: * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2023: * another exception.
2024: */
2025: public void skippedEntity(String name)
2026: throws org.xml.sax.SAXException { // TODO: Should handle
2027: }
2028:
2029: /**
2030: * Report the start of a CDATA section.
2031: *
2032: * @throws org.xml.sax.SAXException The application may raise an exception.
2033: * @see #endCDATA
2034: */
2035: public void startCDATA() throws org.xml.sax.SAXException {
2036: m_cdataStartCalled = true;
2037: }
2038:
2039: /**
2040: * Report the beginning of an entity.
2041: *
2042: * The start and end of the document entity are not reported.
2043: * The start and end of the external DTD subset are reported
2044: * using the pseudo-name "[dtd]". All other events must be
2045: * properly nested within start/end entity events.
2046: *
2047: * @param name The name of the entity. If it is a parameter
2048: * entity, the name will begin with '%'.
2049: * @throws org.xml.sax.SAXException The application may raise an exception.
2050: * @see #endEntity
2051: * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2052: * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2053: */
2054: public void startEntity(String name)
2055: throws org.xml.sax.SAXException {
2056: if (name.equals("[dtd]"))
2057: m_inExternalDTD = true;
2058:
2059: if (!m_expandDTDEntities && !m_inExternalDTD) {
2060: /* Only leave the entity as-is if
2061: * we've been told not to expand them
2062: * and this is not the magic [dtd] name.
2063: */
2064: startNonEscaping();
2065: characters("&" + name + ';');
2066: endNonEscaping();
2067: }
2068:
2069: m_inEntityRef = true;
2070: }
2071:
2072: /**
2073: * For the enclosing elements starting tag write out
2074: * out any attributes followed by ">"
2075: *
2076: * @throws org.xml.sax.SAXException
2077: */
2078: protected void closeStartTag() throws SAXException {
2079:
2080: if (m_elemContext.m_startTagOpen) {
2081:
2082: try {
2083: if (m_tracer != null)
2084: super .fireStartElem(m_elemContext.m_elementName);
2085: int nAttrs = m_attributes.getLength();
2086: if (nAttrs > 0) {
2087: processAttributes(m_writer, nAttrs);
2088: // clear attributes object for re-use with next element
2089: m_attributes.clear();
2090: }
2091: m_writer.write('>');
2092: } catch (IOException e) {
2093: throw new SAXException(e);
2094: }
2095:
2096: /* whether Xalan or XSLTC, we have the prefix mappings now, so
2097: * lets determine if the current element is specified in the cdata-
2098: * section-elements list.
2099: */
2100: if (m_cdataSectionElements != null)
2101: m_elemContext.m_isCdataSection = isCdataSection();
2102:
2103: if (m_doIndent) {
2104: m_isprevtext = false;
2105: m_preserves.push(m_ispreserve);
2106: }
2107: }
2108:
2109: }
2110:
2111: /**
2112: * Report the start of DTD declarations, if any.
2113: *
2114: * Any declarations are assumed to be in the internal subset unless
2115: * otherwise indicated.
2116: *
2117: * @param name The document type name.
2118: * @param publicId The declared public identifier for the
2119: * external DTD subset, or null if none was declared.
2120: * @param systemId The declared system identifier for the
2121: * external DTD subset, or null if none was declared.
2122: * @throws org.xml.sax.SAXException The application may raise an
2123: * exception.
2124: * @see #endDTD
2125: * @see #startEntity
2126: */
2127: public void startDTD(String name, String publicId, String systemId)
2128: throws org.xml.sax.SAXException {
2129: setDoctypeSystem(systemId);
2130: setDoctypePublic(publicId);
2131:
2132: m_elemContext.m_elementName = name;
2133: m_inDoctype = true;
2134: }
2135:
2136: /**
2137: * Returns the m_indentAmount.
2138: * @return int
2139: */
2140: public int getIndentAmount() {
2141: return m_indentAmount;
2142: }
2143:
2144: /**
2145: * Sets the m_indentAmount.
2146: *
2147: * @param m_indentAmount The m_indentAmount to set
2148: */
2149: public void setIndentAmount(int m_indentAmount) {
2150: this .m_indentAmount = m_indentAmount;
2151: }
2152:
2153: /**
2154: * Tell if, based on space preservation constraints and the doIndent property,
2155: * if an indent should occur.
2156: *
2157: * @return True if an indent should occur.
2158: */
2159: protected boolean shouldIndent() {
2160: return m_doIndent && (!m_ispreserve && !m_isprevtext);
2161: }
2162:
2163: /**
2164: * Searches for the list of qname properties with the specified key in the
2165: * property list. If the key is not found in this property list, the default
2166: * property list, and its defaults, recursively, are then checked. The
2167: * method returns <code>null</code> if the property is not found.
2168: *
2169: * @param key the property key.
2170: * @param props the list of properties to search in.
2171: *
2172: * Sets the vector of local-name/URI pairs of the cdata section elements
2173: * specified in the cdata-section-elements property.
2174: *
2175: * This method is essentially a copy of getQNameProperties() from
2176: * OutputProperties. Eventually this method should go away and a call
2177: * to setCdataSectionElements(Vector v) should be made directly.
2178: */
2179: private void setCdataSectionElements(String key, Properties props) {
2180:
2181: String s = props.getProperty(key);
2182:
2183: if (null != s) {
2184: // Vector of URI/LocalName pairs
2185: Vector v = new Vector();
2186: int l = s.length();
2187: boolean inCurly = false;
2188: StringBuffer buf = new StringBuffer();
2189:
2190: // parse through string, breaking on whitespaces. I do this instead
2191: // of a tokenizer so I can track whitespace inside of curly brackets,
2192: // which theoretically shouldn't happen if they contain legal URLs.
2193: for (int i = 0; i < l; i++) {
2194: char c = s.charAt(i);
2195:
2196: if (Character.isWhitespace(c)) {
2197: if (!inCurly) {
2198: if (buf.length() > 0) {
2199: addCdataSectionElement(buf.toString(), v);
2200: buf.setLength(0);
2201: }
2202: continue;
2203: }
2204: } else if ('{' == c)
2205: inCurly = true;
2206: else if ('}' == c)
2207: inCurly = false;
2208:
2209: buf.append(c);
2210: }
2211:
2212: if (buf.length() > 0) {
2213: addCdataSectionElement(buf.toString(), v);
2214: buf.setLength(0);
2215: }
2216: // call the official, public method to set the collected names
2217: setCdataSectionElements(v);
2218: }
2219:
2220: }
2221:
2222: /**
2223: * Adds a URI/LocalName pair of strings to the list.
2224: *
2225: * @param URI_and_localName String of the form "{uri}local" or "local"
2226: *
2227: * @return a QName object
2228: */
2229: private void addCdataSectionElement(String URI_and_localName,
2230: Vector v) {
2231:
2232: StringTokenizer tokenizer = new StringTokenizer(
2233: URI_and_localName, "{}", false);
2234: String s1 = tokenizer.nextToken();
2235: String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken()
2236: : null;
2237:
2238: if (null == s2) {
2239: // add null URI and the local name
2240: v.addElement(null);
2241: v.addElement(s1);
2242: } else {
2243: // add URI, then local name
2244: v.addElement(s1);
2245: v.addElement(s2);
2246: }
2247: }
2248:
2249: /**
2250: * Remembers the cdata sections specified in the cdata-section-elements.
2251: * The "official way to set URI and localName pairs.
2252: * This method should be used by both Xalan and XSLTC.
2253: *
2254: * @param URI_and_localNames a vector of pairs of Strings (URI/local)
2255: */
2256: public void setCdataSectionElements(Vector URI_and_localNames) {
2257: m_cdataSectionElements = URI_and_localNames;
2258: }
2259:
2260: /**
2261: * Makes sure that the namespace URI for the given qualified attribute name
2262: * is declared.
2263: * @param ns the namespace URI
2264: * @param rawName the qualified name
2265: * @return returns null if no action is taken, otherwise it returns the
2266: * prefix used in declaring the namespace.
2267: * @throws SAXException
2268: */
2269: protected String ensureAttributesNamespaceIsDeclared(String ns,
2270: String localName, String rawName)
2271: throws org.xml.sax.SAXException {
2272:
2273: if (ns != null && ns.length() > 0) {
2274:
2275: // extract the prefix in front of the raw name
2276: int index = 0;
2277: String prefixFromRawName = (index = rawName.indexOf(":")) < 0 ? ""
2278: : rawName.substring(0, index);
2279:
2280: if (index > 0) {
2281: // we have a prefix, lets see if it maps to a namespace
2282: String uri = m_prefixMap
2283: .lookupNamespace(prefixFromRawName);
2284: if (uri != null && uri.equals(ns)) {
2285: // the prefix in the raw name is already maps to the given namespace uri
2286: // so we don't need to do anything
2287: return null;
2288: } else {
2289: // The uri does not map to the prefix in the raw name,
2290: // so lets make the mapping.
2291: this .startPrefixMapping(prefixFromRawName, ns,
2292: false);
2293: this .addAttribute("http://www.w3.org/2000/xmlns/",
2294: prefixFromRawName, "xmlns:"
2295: + prefixFromRawName, "CDATA", ns,
2296: false);
2297: return prefixFromRawName;
2298: }
2299: } else {
2300: // we don't have a prefix in the raw name.
2301: // Does the URI map to a prefix already?
2302: String prefix = m_prefixMap.lookupPrefix(ns);
2303: if (prefix == null) {
2304: // uri is not associated with a prefix,
2305: // so lets generate a new prefix to use
2306: prefix = m_prefixMap.generateNextPrefix();
2307: this .startPrefixMapping(prefix, ns, false);
2308: this .addAttribute("http://www.w3.org/2000/xmlns/",
2309: prefix, "xmlns:" + prefix, "CDATA", ns,
2310: false);
2311: }
2312:
2313: return prefix;
2314:
2315: }
2316: }
2317: return null;
2318: }
2319:
2320: void ensurePrefixIsDeclared(String ns, String rawName)
2321: throws org.xml.sax.SAXException {
2322:
2323: if (ns != null && ns.length() > 0) {
2324: int index;
2325: final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
2326: String prefix = (no_prefix) ? "" : rawName.substring(0,
2327: index);
2328:
2329: if (null != prefix) {
2330: String foundURI = m_prefixMap.lookupNamespace(prefix);
2331:
2332: if ((null == foundURI) || !foundURI.equals(ns)) {
2333: this .startPrefixMapping(prefix, ns);
2334:
2335: // Bugzilla1133: Generate attribute as well as namespace event.
2336: // SAX does expect both.
2337:
2338: this .addAttributeAlways(
2339: "http://www.w3.org/2000/xmlns/",
2340: no_prefix ? "xmlns" : prefix, // local name
2341: no_prefix ? "xmlns" : ("xmlns:" + prefix), // qname
2342: "CDATA", ns, false);
2343: }
2344:
2345: }
2346: }
2347: }
2348:
2349: /**
2350: * This method flushes any pending events, which can be startDocument()
2351: * closing the opening tag of an element, or closing an open CDATA section.
2352: */
2353: public void flushPending() throws SAXException {
2354: if (m_needToCallStartDocument) {
2355: startDocumentInternal();
2356: m_needToCallStartDocument = false;
2357: }
2358: if (m_elemContext.m_startTagOpen) {
2359: closeStartTag();
2360: m_elemContext.m_startTagOpen = false;
2361: }
2362:
2363: if (m_cdataTagOpen) {
2364: closeCDATA();
2365: m_cdataTagOpen = false;
2366: }
2367: }
2368:
2369: public void setContentHandler(ContentHandler ch) {
2370: // this method is really only useful in the ToSAXHandler classes but it is
2371: // in the interface. If the method defined here is ever called
2372: // we are probably in trouble.
2373: }
2374:
2375: /**
2376: * Adds the given attribute to the set of attributes, even if there is
2377: * no currently open element. This is useful if a SAX startPrefixMapping()
2378: * should need to add an attribute before the element name is seen.
2379: *
2380: * This method is a copy of its super classes method, except that some
2381: * tracing of events is done. This is so the tracing is only done for
2382: * stream serializers, not for SAX ones.
2383: *
2384: * @param uri the URI of the attribute
2385: * @param localName the local name of the attribute
2386: * @param rawName the qualified name of the attribute
2387: * @param type the type of the attribute (probably CDATA)
2388: * @param value the value of the attribute
2389: * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
2390: * @return true if the attribute value was added,
2391: * false if the attribute already existed and the value was
2392: * replaced with the new value.
2393: */
2394: public boolean addAttributeAlways(String uri, String localName,
2395: String rawName, String type, String value,
2396: boolean xslAttribute) {
2397: boolean was_added;
2398: int index;
2399: if (uri == null || localName == null || uri.length() == 0)
2400: index = m_attributes.getIndex(rawName);
2401: else {
2402: index = m_attributes.getIndex(uri, localName);
2403: }
2404:
2405: if (index >= 0) {
2406: String old_value = null;
2407: if (m_tracer != null) {
2408: old_value = m_attributes.getValue(index);
2409: if (value.equals(old_value))
2410: old_value = null;
2411: }
2412:
2413: /* We've seen the attribute before.
2414: * We may have a null uri or localName, but all we really
2415: * want to re-set is the value anyway.
2416: */
2417: m_attributes.setValue(index, value);
2418: was_added = false;
2419: if (old_value != null)
2420: firePseudoAttributes();
2421:
2422: } else {
2423: // the attribute doesn't exist yet, create it
2424: if (xslAttribute) {
2425: /*
2426: * This attribute is from an xsl:attribute element so we take some care in
2427: * adding it, e.g.
2428: * <elem1 foo:attr1="1" xmlns:foo="uri1">
2429: * <xsl:attribute name="foo:attr2">2</xsl:attribute>
2430: * </elem1>
2431: *
2432: * We are adding attr1 and attr2 both as attributes of elem1,
2433: * and this code is adding attr2 (the xsl:attribute ).
2434: * We could have a collision with the prefix like in the example above.
2435: */
2436:
2437: // In the example above, is there a prefix like foo ?
2438: final int colonIndex = rawName.indexOf(':');
2439: if (colonIndex > 0) {
2440: String prefix = rawName.substring(0, colonIndex);
2441: NamespaceMappings.MappingRecord existing_mapping = m_prefixMap
2442: .getMappingFromPrefix(prefix);
2443:
2444: /* Before adding this attribute (foo:attr2),
2445: * is the prefix for it (foo) already mapped at the current depth?
2446: */
2447: if (existing_mapping != null
2448: && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth
2449: && !existing_mapping.m_uri.equals(uri)) {
2450: /*
2451: * There is an existing mapping of this prefix,
2452: * it differs from the one we need,
2453: * and unfortunately it is at the current depth so we
2454: * can not over-ride it.
2455: */
2456:
2457: /*
2458: * Are we lucky enough that an existing other prefix maps to this URI ?
2459: */
2460: prefix = m_prefixMap.lookupPrefix(uri);
2461: if (prefix == null) {
2462: /* Unfortunately there is no existing prefix that happens to map to ours,
2463: * so to avoid a prefix collision we must generated a new prefix to use.
2464: * This is OK because the prefix URI mapping
2465: * defined in the xsl:attribute is short in scope,
2466: * just the xsl:attribute element itself,
2467: * and at this point in serialization the body of the
2468: * xsl:attribute, if any, is just a String. Right?
2469: * . . . I sure hope so - Brian M.
2470: */
2471: prefix = m_prefixMap.generateNextPrefix();
2472: }
2473:
2474: rawName = prefix + ':' + localName;
2475: }
2476: }
2477:
2478: try {
2479: /* This is our last chance to make sure the namespace for this
2480: * attribute is declared, especially if we just generated an alternate
2481: * prefix to avoid a collision (the new prefix/rawName will go out of scope
2482: * soon and be lost ... last chance here.
2483: */
2484: String prefixUsed = ensureAttributesNamespaceIsDeclared(
2485: uri, localName, rawName);
2486: } catch (SAXException e) {
2487: // TODO Auto-generated catch block
2488: e.printStackTrace();
2489: }
2490: }
2491: m_attributes.addAttribute(uri, localName, rawName, type,
2492: value);
2493: was_added = true;
2494: if (m_tracer != null)
2495: firePseudoAttributes();
2496: }
2497: return was_added;
2498: }
2499:
2500: /**
2501: * To fire off the pseudo characters of attributes, as they currently
2502: * exist. This method should be called everytime an attribute is added,
2503: * or when an attribute value is changed, or an element is created.
2504: */
2505:
2506: protected void firePseudoAttributes() {
2507: if (m_tracer != null) {
2508: try {
2509: // flush out the "<elemName" if not already flushed
2510: m_writer.flush();
2511:
2512: // make a StringBuffer to write the name="value" pairs to.
2513: StringBuffer sb = new StringBuffer();
2514: int nAttrs = m_attributes.getLength();
2515: if (nAttrs > 0) {
2516: // make a writer that internally appends to the same
2517: // StringBuffer
2518: java.io.Writer writer = new ToStream.WritertoStringBuffer(
2519: sb);
2520:
2521: processAttributes(writer, nAttrs);
2522: // Don't clear the attributes!
2523: // We only want to see what would be written out
2524: // at this point, we don't want to loose them.
2525: }
2526: sb.append('>'); // the potential > after the attributes.
2527: // convert the StringBuffer to a char array and
2528: // emit the trace event that these characters "might"
2529: // be written
2530: char ch[] = sb.toString().toCharArray();
2531: m_tracer
2532: .fireGenerateEvent(
2533: SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
2534: ch, 0, ch.length);
2535: } catch (IOException ioe) {
2536: // ignore ?
2537: } catch (SAXException se) {
2538: // ignore ?
2539: }
2540: }
2541: }
2542:
2543: /**
2544: * This inner class is used only to collect attribute values
2545: * written by the method writeAttrString() into a string buffer.
2546: * In this manner trace events, and the real writing of attributes will use
2547: * the same code.
2548: */
2549: private class WritertoStringBuffer extends java.io.Writer {
2550: final private StringBuffer m_stringbuf;
2551:
2552: /**
2553: * @see java.io.Writer#write(char[], int, int)
2554: */
2555: WritertoStringBuffer(StringBuffer sb) {
2556: m_stringbuf = sb;
2557: }
2558:
2559: public void write(char[] arg0, int arg1, int arg2)
2560: throws IOException {
2561: m_stringbuf.append(arg0, arg1, arg2);
2562: }
2563:
2564: /**
2565: * @see java.io.Writer#flush()
2566: */
2567: public void flush() throws IOException {
2568: }
2569:
2570: /**
2571: * @see java.io.Writer#close()
2572: */
2573: public void close() throws IOException {
2574: }
2575:
2576: public void write(int i) {
2577: m_stringbuf.append((char) i);
2578: }
2579:
2580: public void write(String s) {
2581: m_stringbuf.append(s);
2582: }
2583: }
2584:
2585: /**
2586: * @see SerializationHandler#setTransformer(Transformer)
2587: */
2588: public void setTransformer(Transformer transformer) {
2589: super .setTransformer(transformer);
2590: if (m_tracer != null
2591: && !(m_writer instanceof SerializerTraceWriter))
2592: m_writer = new SerializerTraceWriter(m_writer, m_tracer);
2593:
2594: }
2595:
2596: /**
2597: * Try's to reset the super class and reset this class for
2598: * re-use, so that you don't need to create a new serializer
2599: * (mostly for performance reasons).
2600: *
2601: * @return true if the class was successfuly reset.
2602: */
2603: public boolean reset() {
2604: boolean wasReset = false;
2605: if (super .reset()) {
2606: resetToStream();
2607: wasReset = true;
2608: }
2609: return wasReset;
2610: }
2611:
2612: /**
2613: * Reset all of the fields owned by ToStream class
2614: *
2615: */
2616: private void resetToStream() {
2617: this .m_cdataStartCalled = false;
2618: /* The stream is being reset. It is one of
2619: * ToXMLStream, ToHTMLStream ... and this type can't be changed
2620: * so neither should m_charInfo which is associated with the
2621: * type of Stream. Just leave m_charInfo as-is for the next re-use.
2622: *
2623: */
2624: // this.m_charInfo = null; // don't set to null
2625: this .m_disableOutputEscapingStates.clear();
2626:
2627: this .m_escaping = true;
2628: // Leave m_format alone for now - Brian M.
2629: // this.m_format = null;
2630: this .m_inDoctype = false;
2631: this .m_ispreserve = false;
2632: this .m_ispreserve = false;
2633: this .m_isprevtext = false;
2634: this .m_isUTF8 = false; // ?? used anywhere ??
2635: this .m_preserves.clear();
2636: this .m_shouldFlush = true;
2637: this .m_spaceBeforeClose = false;
2638: this .m_startNewLine = false;
2639: this .m_lineSepUse = true;
2640: // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
2641: // this.m_writer = null;
2642: this .m_expandDTDEntities = true;
2643:
2644: }
2645:
2646: /**
2647: * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
2648: * @param encoding the character encoding
2649: */
2650: public void setEncoding(String encoding) {
2651: String old = getEncoding();
2652: super .setEncoding(encoding);
2653: if (old == null || !old.equals(encoding)) {
2654: // If we have changed the setting of the
2655: m_encodingInfo = Encodings.getEncodingInfo(encoding);
2656:
2657: if (encoding != null && m_encodingInfo.name == null) {
2658: // We tried to get an EncodingInfo for Object for the given
2659: // encoding, but it came back with an internall null name
2660: // so the encoding is not supported by the JDK, issue a message.
2661: String msg = Utils.messages.createMessage(
2662: MsgKey.ER_ENCODING_NOT_SUPPORTED,
2663: new Object[] { encoding });
2664: try {
2665: // Prepare to issue the warning message
2666: Transformer tran = super .getTransformer();
2667: if (tran != null) {
2668: ErrorListener errHandler = tran
2669: .getErrorListener();
2670: // Issue the warning message
2671: if (null != errHandler
2672: && m_sourceLocator != null)
2673: errHandler
2674: .warning(new TransformerException(
2675: msg, m_sourceLocator));
2676: else
2677: System.out.println(msg);
2678: } else
2679: System.out.println(msg);
2680: } catch (Exception e) {
2681: }
2682: }
2683: }
2684: return;
2685: }
2686:
2687: /**
2688: * Simple stack for boolean values.
2689: *
2690: * This class is a copy of the one in org.apache.xml.utils.
2691: * It exists to cut the serializers dependancy on that package.
2692: * A minor changes from that package are:
2693: * doesn't implement Clonable
2694: *
2695: * @xsl.usage internal
2696: */
2697: static final class BoolStack {
2698:
2699: /** Array of boolean values */
2700: private boolean m_values[];
2701:
2702: /** Array size allocated */
2703: private int m_allocatedSize;
2704:
2705: /** Index into the array of booleans */
2706: private int m_index;
2707:
2708: /**
2709: * Default constructor. Note that the default
2710: * block size is very small, for small lists.
2711: */
2712: public BoolStack() {
2713: this (32);
2714: }
2715:
2716: /**
2717: * Construct a IntVector, using the given block size.
2718: *
2719: * @param size array size to allocate
2720: */
2721: public BoolStack(int size) {
2722:
2723: m_allocatedSize = size;
2724: m_values = new boolean[size];
2725: m_index = -1;
2726: }
2727:
2728: /**
2729: * Get the length of the list.
2730: *
2731: * @return Current length of the list
2732: */
2733: public final int size() {
2734: return m_index + 1;
2735: }
2736:
2737: /**
2738: * Clears the stack.
2739: *
2740: */
2741: public final void clear() {
2742: m_index = -1;
2743: }
2744:
2745: /**
2746: * Pushes an item onto the top of this stack.
2747: *
2748: *
2749: * @param val the boolean to be pushed onto this stack.
2750: * @return the <code>item</code> argument.
2751: */
2752: public final boolean push(boolean val) {
2753:
2754: if (m_index == m_allocatedSize - 1)
2755: grow();
2756:
2757: return (m_values[++m_index] = val);
2758: }
2759:
2760: /**
2761: * Removes the object at the top of this stack and returns that
2762: * object as the value of this function.
2763: *
2764: * @return The object at the top of this stack.
2765: * @throws EmptyStackException if this stack is empty.
2766: */
2767: public final boolean pop() {
2768: return m_values[m_index--];
2769: }
2770:
2771: /**
2772: * Removes the object at the top of this stack and returns the
2773: * next object at the top as the value of this function.
2774: *
2775: *
2776: * @return Next object to the top or false if none there
2777: */
2778: public final boolean popAndTop() {
2779:
2780: m_index--;
2781:
2782: return (m_index >= 0) ? m_values[m_index] : false;
2783: }
2784:
2785: /**
2786: * Set the item at the top of this stack
2787: *
2788: *
2789: * @param b Object to set at the top of this stack
2790: */
2791: public final void setTop(boolean b) {
2792: m_values[m_index] = b;
2793: }
2794:
2795: /**
2796: * Looks at the object at the top of this stack without removing it
2797: * from the stack.
2798: *
2799: * @return the object at the top of this stack.
2800: * @throws EmptyStackException if this stack is empty.
2801: */
2802: public final boolean peek() {
2803: return m_values[m_index];
2804: }
2805:
2806: /**
2807: * Looks at the object at the top of this stack without removing it
2808: * from the stack. If the stack is empty, it returns false.
2809: *
2810: * @return the object at the top of this stack.
2811: */
2812: public final boolean peekOrFalse() {
2813: return (m_index > -1) ? m_values[m_index] : false;
2814: }
2815:
2816: /**
2817: * Looks at the object at the top of this stack without removing it
2818: * from the stack. If the stack is empty, it returns true.
2819: *
2820: * @return the object at the top of this stack.
2821: */
2822: public final boolean peekOrTrue() {
2823: return (m_index > -1) ? m_values[m_index] : true;
2824: }
2825:
2826: /**
2827: * Tests if this stack is empty.
2828: *
2829: * @return <code>true</code> if this stack is empty;
2830: * <code>false</code> otherwise.
2831: */
2832: public boolean isEmpty() {
2833: return (m_index == -1);
2834: }
2835:
2836: /**
2837: * Grows the size of the stack
2838: *
2839: */
2840: private void grow() {
2841:
2842: m_allocatedSize *= 2;
2843:
2844: boolean newVector[] = new boolean[m_allocatedSize];
2845:
2846: System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
2847:
2848: m_values = newVector;
2849: }
2850: }
2851:
2852: // Implement DTDHandler
2853: /**
2854: * If this method is called, the serializer is used as a
2855: * DTDHandler, which changes behavior how the serializer
2856: * handles document entities.
2857: * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
2858: */
2859: public void notationDecl(String name, String pubID, String sysID)
2860: throws SAXException {
2861: // TODO Auto-generated method stub
2862: try {
2863: DTDprolog();
2864:
2865: m_writer.write("<!NOTATION ");
2866: m_writer.write(name);
2867: if (pubID != null) {
2868: m_writer.write(" PUBLIC \"");
2869: m_writer.write(pubID);
2870:
2871: } else {
2872: m_writer.write(" SYSTEM \"");
2873: m_writer.write(sysID);
2874: }
2875: m_writer.write("\" >");
2876: m_writer.write(m_lineSep, 0, m_lineSepLen);
2877: } catch (IOException e) {
2878: // TODO Auto-generated catch block
2879: e.printStackTrace();
2880: }
2881: }
2882:
2883: /**
2884: * If this method is called, the serializer is used as a
2885: * DTDHandler, which changes behavior how the serializer
2886: * handles document entities.
2887: * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
2888: */
2889: public void unparsedEntityDecl(String name, String pubID,
2890: String sysID, String notationName) throws SAXException {
2891: // TODO Auto-generated method stub
2892: try {
2893: DTDprolog();
2894:
2895: m_writer.write("<!ENTITY ");
2896: m_writer.write(name);
2897: if (pubID != null) {
2898: m_writer.write(" PUBLIC \"");
2899: m_writer.write(pubID);
2900:
2901: } else {
2902: m_writer.write(" SYSTEM \"");
2903: m_writer.write(sysID);
2904: }
2905: m_writer.write("\" NDATA ");
2906: m_writer.write(notationName);
2907: m_writer.write(" >");
2908: m_writer.write(m_lineSep, 0, m_lineSepLen);
2909: } catch (IOException e) {
2910: // TODO Auto-generated catch block
2911: e.printStackTrace();
2912: }
2913: }
2914:
2915: /**
2916: * A private helper method to output the
2917: * @throws SAXException
2918: * @throws IOException
2919: */
2920: private void DTDprolog() throws SAXException, IOException {
2921: final java.io.Writer writer = m_writer;
2922: if (m_needToOutputDocTypeDecl) {
2923: outputDocTypeDecl(m_elemContext.m_elementName, false);
2924: m_needToOutputDocTypeDecl = false;
2925: }
2926: if (m_inDoctype) {
2927: writer.write(" [");
2928: writer.write(m_lineSep, 0, m_lineSepLen);
2929: m_inDoctype = false;
2930: }
2931: }
2932:
2933: /**
2934: * If set to false the serializer does not expand DTD entities,
2935: * but leaves them as is, the default value is true;
2936: */
2937: public void setDTDEntityExpansion(boolean expand) {
2938: m_expandDTDEntities = expand;
2939: }
2940: }
|