PageRenderTime 64ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/java-1.7.0-openjdk/openjdk/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToStream.java

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