1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.serialize;
  58. import java.io.Writer;
  59. import java.io.OutputStream;
  60. import java.io.OutputStreamWriter;
  61. import java.io.UnsupportedEncodingException;
  62. import java.io.IOException;
  63. import java.util.Enumeration;
  64. import java.util.Stack;
  65. import java.util.Vector;
  66. import java.util.Hashtable;
  67. import java.util.Properties;
  68. import java.util.BitSet;
  69. import org.xml.sax.*;
  70. import org.xml.sax.ext.LexicalHandler;
  71. import org.xml.sax.ext.DeclHandler;
  72. import org.w3c.dom.Node;
  73. import org.apache.xalan.serialize.Serializer;
  74. import org.apache.xalan.serialize.DOMSerializer;
  75. import org.apache.xml.utils.QName;
  76. import org.apache.xalan.templates.OutputProperties;
  77. import org.apache.xml.utils.BoolStack;
  78. import org.apache.xml.utils.TreeWalker;
  79. import org.apache.xml.utils.WrappedRuntimeException;
  80. import org.apache.xml.utils.SystemIDResolver;
  81. import org.apache.xalan.res.XSLTErrorResources;
  82. import org.apache.xalan.res.XSLMessages;
  83. import org.apache.xpath.res.XPATHErrorResources;
  84. import javax.xml.transform.Result;
  85. import javax.xml.transform.OutputKeys;
  86. /**
  87. * <meta name="usage" content="general"/>
  88. * SerializerToXML formats SAX-style events into XML.
  89. */
  90. public class SerializerToXML
  91. implements ContentHandler, LexicalHandler, DeclHandler, Serializer,
  92. DOMSerializer
  93. {
  94. /**
  95. * The writer where the XML will be written.
  96. */
  97. protected Writer m_writer = null;
  98. /** True if we control the buffer, and we should flush the output on endDocument. */
  99. boolean m_shouldFlush = true;
  100. // /** The output stream where the result stream is written. */
  101. // protected OutputStream m_outputStream = System.out;
  102. /**
  103. * The character encoding. Must match the encoding used for the printWriter.
  104. */
  105. protected String m_encoding = null;
  106. /**
  107. * Assume java encoding names are the same as the ISO encoding names if this is true.
  108. */
  109. static boolean javaEncodingIsISO = false;
  110. /**
  111. * Tells if we should write the XML declaration.
  112. */
  113. public boolean m_shouldNotWriteXMLHeader = false;
  114. /**
  115. * Tells the XML version, for writing out to the XML decl.
  116. */
  117. public String m_version = null;
  118. /**
  119. * A stack of Boolean objects that tell if the given element
  120. * has children.
  121. */
  122. protected BoolStack m_elemStack = new BoolStack();
  123. /** Stack to keep track of disabling output escaping. */
  124. protected BoolStack m_disableOutputEscapingStates = new BoolStack();
  125. /** True will be pushed, if characters should be in CDATA section blocks. */
  126. protected BoolStack m_cdataSectionStates = new BoolStack();
  127. /** List of QNames obtained from the xsl:output properties. */
  128. protected Vector m_cdataSectionNames = null;
  129. /** True if the current characters should be in CDATA blocks. */
  130. protected boolean m_inCData = false;
  131. /**
  132. * Tell if the character escaping should be disabled for the current state.
  133. *
  134. * @return true if the character escaping should be disabled.
  135. */
  136. protected boolean isEscapingDisabled()
  137. {
  138. return m_disableOutputEscapingStates.peekOrFalse();
  139. }
  140. /**
  141. * Tell if the characters in the current state should be put in
  142. * cdata section blocks.
  143. *
  144. * @return true if the characters in the current state should be put in
  145. * cdata section blocks.
  146. */
  147. protected boolean isCDataSection()
  148. {
  149. return m_inCData || m_cdataSectionStates.peekOrFalse();
  150. }
  151. /**
  152. * Use the system line seperator to write line breaks.
  153. */
  154. protected final char[] m_lineSep =
  155. System.getProperty("line.separator").toCharArray();
  156. /**
  157. * The length of the line seperator, since the write is done
  158. * one character at a time.
  159. */
  160. protected final int m_lineSepLen = m_lineSep.length;
  161. /**
  162. * Output a system-dependent line break.
  163. *
  164. * @throws org.xml.sax.SAXException
  165. */
  166. protected final void outputLineSep() throws org.xml.sax.SAXException
  167. {
  168. try
  169. {
  170. m_writer.write(m_lineSep, 0, m_lineSepLen);
  171. }
  172. catch (IOException ioe)
  173. {
  174. throw new SAXException(ioe);
  175. }
  176. }
  177. /**
  178. * State flag to tell if preservation of whitespace
  179. * is important.
  180. */
  181. protected boolean m_ispreserve = false;
  182. /**
  183. * Stack to keep track of whether or not we need to
  184. * preserve whitespace.
  185. */
  186. protected BoolStack m_preserves = new BoolStack();
  187. /**
  188. * State flag that tells if the previous node processed
  189. * was text, so we can tell if we should preserve whitespace.
  190. */
  191. protected boolean m_isprevtext = false;
  192. /**
  193. * Flag to tell if indenting (pretty-printing) is on.
  194. */
  195. protected boolean m_doIndent = false;
  196. /**
  197. * Flag to keep track of the indent amount.
  198. */
  199. protected int m_currentIndent = 0;
  200. /**
  201. * Amount to indent.
  202. */
  203. public int m_indentAmount = 0;
  204. /**
  205. * Current level of indent.
  206. */
  207. protected int level = 0;
  208. /**
  209. * Flag to signal that a newline should be added.
  210. */
  211. boolean m_startNewLine;
  212. /**
  213. * Flag to tell that we need to add the doctype decl,
  214. * which we can't do until the first element is
  215. * encountered.
  216. */
  217. boolean m_needToOutputDocTypeDecl = true;
  218. /**
  219. * The System ID for the doc type.
  220. */
  221. String m_doctypeSystem;
  222. /**
  223. * The public ID for the doc type.
  224. */
  225. String m_doctypePublic;
  226. /**
  227. * The standalone value for the doctype.
  228. */
  229. boolean m_standalone = false;
  230. /**
  231. * True if standalone was specified.
  232. */
  233. boolean m_standaloneWasSpecified = false;
  234. /**
  235. * The mediatype. Not used right now.
  236. */
  237. String m_mediatype;
  238. /**
  239. * Tells if we're in an EntityRef event.
  240. */
  241. protected boolean m_inEntityRef = false;
  242. /**
  243. * Tells if we're in an internal document type subset.
  244. */
  245. private boolean m_inDoctype = false;
  246. /**
  247. * Map that tells which XML characters should have special treatment, and it
  248. * provides character to entity name lookup.
  249. */
  250. protected static CharInfo m_xmlcharInfo =
  251. new CharInfo(CharInfo.XML_ENTITIES_RESOURCE);
  252. /**
  253. * Map that tells which characters should have special treatment, and it
  254. * provides character to entity name lookup.
  255. */
  256. protected CharInfo m_charInfo;
  257. /** Table of user-specified char infos. */
  258. private static Hashtable m_charInfos = null;
  259. /**
  260. * Flag to quickly tell if the encoding is UTF8.
  261. */
  262. boolean m_isUTF8 = false;
  263. /**
  264. * The maximum character size before we have to resort
  265. * to escaping.
  266. */
  267. protected int m_maxCharacter = Encodings.getLastPrintable();
  268. /**
  269. * Add space before '/>' for XHTML.
  270. */
  271. public boolean m_spaceBeforeClose = false;
  272. /** The xsl:output properties. */
  273. protected Properties m_format;
  274. /** Indicate whether running in Debug mode */
  275. private static final boolean DEBUG = false;
  276. /** This flag is set while receiving events from the external DTD */
  277. private boolean m_inExternalDTD = false;
  278. /**
  279. * Default constructor.
  280. */
  281. public SerializerToXML()
  282. {
  283. m_charInfo = m_xmlcharInfo;
  284. }
  285. /**
  286. * Copy properties from another SerializerToXML.
  287. *
  288. * @param xmlListener non-null reference to a SerializerToXML object.
  289. */
  290. public void CopyFrom(SerializerToXML xmlListener)
  291. {
  292. m_writer = xmlListener.m_writer;
  293. // m_outputStream = xmlListener.m_outputStream;
  294. m_encoding = xmlListener.m_encoding;
  295. javaEncodingIsISO = xmlListener.javaEncodingIsISO;
  296. m_shouldNotWriteXMLHeader = xmlListener.m_shouldNotWriteXMLHeader;
  297. // m_shouldNotWriteXMLHeader = xmlListener.m_shouldNotWriteXMLHeader;
  298. m_elemStack = xmlListener.m_elemStack;
  299. // m_lineSep = xmlListener.m_lineSep;
  300. // m_lineSepLen = xmlListener.m_lineSepLen;
  301. m_ispreserve = xmlListener.m_ispreserve;
  302. m_preserves = xmlListener.m_preserves;
  303. m_isprevtext = xmlListener.m_isprevtext;
  304. m_doIndent = xmlListener.m_doIndent;
  305. m_currentIndent = xmlListener.m_currentIndent;
  306. m_indentAmount = xmlListener.m_indentAmount;
  307. level = xmlListener.level;
  308. m_startNewLine = xmlListener.m_startNewLine;
  309. m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl;
  310. m_doctypeSystem = xmlListener.m_doctypeSystem;
  311. m_doctypePublic = xmlListener.m_doctypePublic;
  312. m_standalone = xmlListener.m_standalone;
  313. m_mediatype = xmlListener.m_mediatype;
  314. m_maxCharacter = xmlListener.m_maxCharacter;
  315. m_spaceBeforeClose = xmlListener.m_spaceBeforeClose;
  316. m_inCData = xmlListener.m_inCData;
  317. // m_pos = xmlListener.m_pos;
  318. m_pos = 0;
  319. }
  320. /**
  321. * Initialize the serializer with the specified writer and output format.
  322. * Must be called before calling any of the serialize methods.
  323. *
  324. * @param writer The writer to use
  325. * @param format The output format
  326. */
  327. public synchronized void init(Writer writer, Properties format)
  328. {
  329. init(writer, format, false);
  330. }
  331. /**
  332. * Initialize the serializer with the specified writer and output format.
  333. * Must be called before calling any of the serialize methods.
  334. *
  335. * @param writer The writer to use
  336. * @param format The output format
  337. * @param shouldFlush True if the writer should be flushed at EndDocument.
  338. */
  339. private synchronized void init(Writer writer, Properties format,
  340. boolean shouldFlush)
  341. {
  342. m_shouldFlush = shouldFlush;
  343. m_writer = writer;
  344. m_format = format;
  345. m_cdataSectionNames =
  346. OutputProperties.getQNameProperties(OutputKeys.CDATA_SECTION_ELEMENTS,
  347. format);
  348. m_indentAmount =
  349. OutputProperties.getIntProperty(OutputProperties.S_KEY_INDENT_AMOUNT,
  350. format);
  351. m_doIndent = OutputProperties.getBooleanProperty(OutputKeys.INDENT,
  352. format);
  353. m_shouldNotWriteXMLHeader =
  354. OutputProperties.getBooleanProperty(OutputKeys.OMIT_XML_DECLARATION,
  355. format);
  356. m_doctypeSystem = format.getProperty(OutputKeys.DOCTYPE_SYSTEM);
  357. m_doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
  358. m_standaloneWasSpecified = (null != format.get(OutputKeys.STANDALONE));
  359. m_standalone = OutputProperties.getBooleanProperty(OutputKeys.STANDALONE,
  360. format);
  361. m_mediatype = format.getProperty(OutputKeys.MEDIA_TYPE);
  362. if (null != m_doctypePublic)
  363. {
  364. if (m_doctypePublic.startsWith("-//W3C//DTD XHTML"))
  365. m_spaceBeforeClose = true;
  366. }
  367. // initCharsMap();
  368. if (null == m_encoding)
  369. m_encoding =
  370. Encodings.getMimeEncoding(format.getProperty(OutputKeys.ENCODING));
  371. m_isUTF8 = m_encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
  372. m_maxCharacter = Encodings.getLastPrintable(m_encoding);
  373. // Access this only from the Hashtable level... we don't want to
  374. // get default properties.
  375. String entitiesFileName =
  376. (String) format.get(OutputProperties.S_KEY_ENTITIES);
  377. if (null != entitiesFileName)
  378. {
  379. try
  380. {
  381. m_charInfo = null;
  382. if (null == m_charInfos)
  383. {
  384. synchronized (m_xmlcharInfo)
  385. {
  386. if (null == m_charInfos) // secondary check
  387. m_charInfos = new Hashtable();
  388. }
  389. }
  390. else
  391. {
  392. m_charInfo = (CharInfo) m_charInfos.get(entitiesFileName);
  393. }
  394. if (null == m_charInfo)
  395. {
  396. try
  397. {
  398. m_charInfo = new CharInfo(entitiesFileName);
  399. }
  400. catch(Exception e)
  401. {
  402. m_charInfo = null;
  403. }
  404. }
  405. if (null == m_charInfo)
  406. {
  407. String absoluteEntitiesFileName;
  408. if (entitiesFileName.indexOf(':') < 0)
  409. {
  410. absoluteEntitiesFileName =
  411. SystemIDResolver.getAbsoluteURIFromRelative(entitiesFileName);
  412. }
  413. else
  414. {
  415. absoluteEntitiesFileName =
  416. SystemIDResolver.getAbsoluteURI(entitiesFileName, null);
  417. }
  418. m_charInfo = new CharInfo(absoluteEntitiesFileName);
  419. m_charInfos.put(entitiesFileName, m_charInfo);
  420. }
  421. }
  422. catch (javax.xml.transform.TransformerException te)
  423. {
  424. throw new org.apache.xml.utils.WrappedRuntimeException(te);
  425. }
  426. }
  427. }
  428. /**
  429. * Initialize the serializer with the specified output stream and output format.
  430. * Must be called before calling any of the serialize methods.
  431. *
  432. * @param output The output stream to use
  433. * @param format The output format
  434. * @throws UnsupportedEncodingException The encoding specified
  435. * in the output format is not supported
  436. */
  437. public synchronized void init(OutputStream output, Properties format)
  438. throws UnsupportedEncodingException
  439. {
  440. if (null == format)
  441. {
  442. OutputProperties op = new OutputProperties(Method.XML);
  443. format = op.getProperties();
  444. }
  445. m_encoding =
  446. Encodings.getMimeEncoding(format.getProperty(OutputKeys.ENCODING));
  447. if (m_encoding.equalsIgnoreCase("UTF-8"))
  448. {
  449. m_isUTF8 = true;
  450. if(output instanceof java.io.BufferedOutputStream)
  451. {
  452. init(new WriterToUTF8(output), format, true);
  453. }
  454. else if(output instanceof java.io.FileOutputStream)
  455. {
  456. init(new WriterToUTF8Buffered(output), format, true);
  457. }
  458. else
  459. {
  460. // Not sure what to do in this case. I'm going to be conservative
  461. // and not buffer.
  462. init(new WriterToUTF8(output), format, true);
  463. }
  464. }
  465. else if (m_encoding.equals("WINDOWS-1250")
  466. || m_encoding.equals("US-ASCII") || m_encoding.equals("ASCII"))
  467. {
  468. init(new WriterToASCI(output), format, true);
  469. }
  470. else
  471. {
  472. Writer osw;
  473. try
  474. {
  475. osw = Encodings.getWriter(output, m_encoding);
  476. }
  477. catch (UnsupportedEncodingException uee)
  478. {
  479. System.out.println("Warning: encoding \"" + m_encoding
  480. + "\" not supported" + ", using "
  481. + Encodings.DEFAULT_MIME_ENCODING);
  482. m_encoding = Encodings.DEFAULT_MIME_ENCODING;
  483. osw = Encodings.getWriter(output, m_encoding);
  484. }
  485. m_maxCharacter = Encodings.getLastPrintable(m_encoding);
  486. init(osw, format, true);
  487. }
  488. }
  489. /**
  490. * Receive an object for locating the origin of SAX document events.
  491. *
  492. * @param locator An object that can return the location of
  493. * any SAX document event.
  494. * @see org.xml.sax.Locator
  495. */
  496. public void setDocumentLocator(Locator locator)
  497. {
  498. // I don't do anything with this yet.
  499. }
  500. /**
  501. * Output the doc type declaration.
  502. *
  503. * @param name non-null reference to document type name.
  504. * NEEDSDOC @param closeDecl
  505. *
  506. * @throws org.xml.sax.SAXException
  507. */
  508. void outputDocTypeDecl(String name, boolean closeDecl)
  509. throws org.xml.sax.SAXException
  510. {
  511. try
  512. {
  513. final Writer writer = m_writer;
  514. writer.write("<!DOCTYPE ");
  515. writer.write(name);
  516. if (null != m_doctypePublic)
  517. {
  518. writer.write(" PUBLIC \"");
  519. writer.write(m_doctypePublic);
  520. writer.write('\"');
  521. }
  522. if (null != m_doctypeSystem)
  523. {
  524. if (null == m_doctypePublic)
  525. writer.write(" SYSTEM \"");
  526. else
  527. writer.write(" \"");
  528. writer.write(m_doctypeSystem);
  529. if (closeDecl)
  530. {
  531. writer.write("\">");
  532. writer.write(m_lineSep, 0, m_lineSepLen);;
  533. }
  534. else
  535. writer.write('\"');
  536. }
  537. }
  538. catch(IOException ioe)
  539. {
  540. throw new SAXException(ioe);
  541. }
  542. }
  543. /**
  544. * Output the doc type declaration.
  545. *
  546. * @param name non-null reference to document type name.
  547. * NEEDSDOC @param value
  548. *
  549. * @throws org.xml.sax.SAXException
  550. */
  551. void outputEntityDecl(String name, String value)
  552. throws org.xml.sax.SAXException
  553. {
  554. try
  555. {
  556. final Writer writer = m_writer;
  557. writer.write("<!ENTITY ");
  558. writer.write(name);
  559. writer.write(" \"");
  560. writer.write(value);
  561. writer.write("\">");
  562. writer.write(m_lineSep, 0, m_lineSepLen);
  563. }
  564. catch(IOException ioe)
  565. {
  566. throw new SAXException(ioe);
  567. }
  568. }
  569. /**
  570. * Receive notification of the beginning of a document.
  571. *
  572. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  573. * wrapping another exception.
  574. *
  575. * @throws org.xml.sax.SAXException
  576. */
  577. public void startDocument() throws org.xml.sax.SAXException
  578. {
  579. if (m_inEntityRef)
  580. return;
  581. m_needToOutputDocTypeDecl = true;
  582. m_startNewLine = false;
  583. if (m_shouldNotWriteXMLHeader == false)
  584. {
  585. String encoding = Encodings.getMimeEncoding(m_encoding);
  586. String version = (null == m_version) ? "1.0" : m_version;
  587. String standalone;
  588. if (m_standaloneWasSpecified)
  589. {
  590. standalone = " standalone=\"" + (m_standalone ? "yes" : "no") + "\"";
  591. }
  592. else
  593. {
  594. standalone = "";
  595. }
  596. try
  597. {
  598. final Writer writer = m_writer;
  599. writer.write("<?xml version=\"");
  600. writer.write(version);
  601. writer.write("\" encoding=\"");
  602. writer.write(encoding);
  603. writer.write('\"');
  604. writer.write(standalone);
  605. writer.write("?>");
  606. writer.write(m_lineSep, 0, m_lineSepLen);
  607. }
  608. catch(IOException ioe)
  609. {
  610. throw new SAXException(ioe);
  611. }
  612. }
  613. }
  614. /**
  615. * Receive notification of the end of a document.
  616. *
  617. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  618. * wrapping another exception.
  619. *
  620. * @throws org.xml.sax.SAXException
  621. */
  622. public void endDocument() throws org.xml.sax.SAXException
  623. {
  624. if (m_doIndent &&!m_isprevtext)
  625. {
  626. outputLineSep();
  627. }
  628. flushWriter();
  629. }
  630. /**
  631. * Report the start of DTD declarations, if any.
  632. *
  633. * Any declarations are assumed to be in the internal subset
  634. * unless otherwise indicated.
  635. *
  636. * @param name The document type name.
  637. * @param publicId The declared public identifier for the
  638. * external DTD subset, or null if none was declared.
  639. * @param systemId The declared system identifier for the
  640. * external DTD subset, or null if none was declared.
  641. * @throws org.xml.sax.SAXException The application may raise an
  642. * exception.
  643. * @see #endDTD
  644. * @see #startEntity
  645. */
  646. public void startDTD(String name, String publicId, String systemId)
  647. throws org.xml.sax.SAXException
  648. {
  649. m_doctypeSystem = systemId;
  650. m_doctypePublic = publicId;
  651. if ((true == m_needToOutputDocTypeDecl)) // && (null != m_doctypeSystem))
  652. {
  653. outputDocTypeDecl(name, false);
  654. }
  655. m_needToOutputDocTypeDecl = false;
  656. m_inDoctype = true;
  657. }
  658. /**
  659. * Report the end of DTD declarations.
  660. *
  661. * @throws org.xml.sax.SAXException The application may raise an exception.
  662. * @see #startDTD
  663. */
  664. public void endDTD() throws org.xml.sax.SAXException
  665. {
  666. try
  667. {
  668. if (!m_inDoctype)
  669. m_writer.write("]>");
  670. else
  671. {
  672. m_writer.write('>');
  673. }
  674. m_writer.write(m_lineSep, 0, m_lineSepLen);
  675. }
  676. catch(IOException ioe)
  677. {
  678. throw new SAXException(ioe);
  679. }
  680. // Do nothing for now.
  681. }
  682. /**
  683. * Begin the scope of a prefix-URI Namespace mapping.
  684. * @see org.xml.sax.ContentHandler#startPrefixMapping
  685. *
  686. * @param prefix The Namespace prefix being declared.
  687. * @param uri The Namespace URI the prefix is mapped to.
  688. * @throws org.xml.sax.SAXException The client may throw
  689. * an exception during processing.
  690. */
  691. public void startPrefixMapping(String prefix, String uri)
  692. throws org.xml.sax.SAXException{}
  693. /**
  694. * End the scope of a prefix-URI Namespace mapping.
  695. * @see org.xml.sax.ContentHandler#endPrefixMapping
  696. *
  697. * @param prefix The prefix that was being mapping.
  698. * @throws org.xml.sax.SAXException The client may throw
  699. * an exception during processing.
  700. */
  701. public void endPrefixMapping(String prefix)
  702. throws org.xml.sax.SAXException{}
  703. /**
  704. * Tell if two strings are equal, without worry if the first string is null.
  705. *
  706. * @param p String reference, which may be null.
  707. * @param t String reference, which may be null.
  708. *
  709. * @return true if strings are equal.
  710. */
  711. protected static final boolean subPartMatch(String p, String t)
  712. {
  713. return (p == t) || ((null != p) && (p.equals(t)));
  714. }
  715. /**
  716. * Push a boolean state based on if the name of the element
  717. * is found in the list of qnames. A state is always pushed,
  718. * one way or the other.
  719. *
  720. * @param namespaceURI Should be a non-null reference to the namespace URL
  721. * of the element that owns the state, or empty string.
  722. * @param localName Should be a non-null reference to the local name
  723. * of the element that owns the state.
  724. * @param qnames Vector of qualified names of elements, or null.
  725. * @param state The stack where the state should be pushed.
  726. */
  727. protected void pushState(String namespaceURI, String localName,
  728. Vector qnames, BoolStack state)
  729. {
  730. boolean b;
  731. if (null != qnames)
  732. {
  733. b = false;
  734. if ((null != namespaceURI) && namespaceURI.length() == 0)
  735. namespaceURI = null;
  736. int nElems = qnames.size();
  737. for (int i = 0; i < nElems; i++)
  738. {
  739. QName q = (QName) qnames.elementAt(i);
  740. if (q.getLocalName().equals(localName)
  741. && subPartMatch(namespaceURI, q.getNamespaceURI()))
  742. {
  743. b = true;
  744. break;
  745. }
  746. }
  747. }
  748. else
  749. {
  750. b = state.peekOrFalse();
  751. }
  752. state.push(b);
  753. }
  754. /**
  755. * Receive notification of the beginning of an element.
  756. *
  757. *
  758. * @param namespaceURI The Namespace URI, or the empty string if the
  759. * element has no Namespace URI or if Namespace
  760. * processing is not being performed.
  761. * @param localName The local name (without prefix), or the
  762. * empty string if Namespace processing is not being
  763. * performed.
  764. * @param name The element type name.
  765. * @param atts The attributes attached to the element, if any.
  766. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  767. * wrapping another exception.
  768. * @see org.xml.sax.ContentHandler#startElement
  769. * @see org.xml.sax.ContentHandler#endElement
  770. * @see org.xml.sax.AttributeList
  771. *
  772. * @throws org.xml.sax.SAXException
  773. */
  774. public void startElement(
  775. String namespaceURI, String localName, String name, Attributes atts)
  776. throws org.xml.sax.SAXException
  777. {
  778. if (DEBUG)
  779. {
  780. System.out.println("SerializerToXML - startElement: " + namespaceURI
  781. + ", " + localName);
  782. int n = atts.getLength();
  783. for (int i = 0; i < n; i++)
  784. {
  785. System.out.println("atts[" + i + "]: " + atts.getQName(i) + " = "
  786. + atts.getValue(i));
  787. }
  788. if (null == namespaceURI)
  789. {
  790. (new RuntimeException(localName
  791. + " has a null namespace!")).printStackTrace();
  792. }
  793. }
  794. if (m_inEntityRef)
  795. return;
  796. if ((true == m_needToOutputDocTypeDecl) && (null != m_doctypeSystem))
  797. {
  798. outputDocTypeDecl(name, true);
  799. }
  800. m_needToOutputDocTypeDecl = false;
  801. writeParentTagEnd();
  802. pushState(namespaceURI, localName, m_cdataSectionNames,
  803. m_cdataSectionStates);
  804. // pushState(namespaceURI, localName, m_format.getNonEscapingElements(),
  805. // m_disableOutputEscapingStates);
  806. m_ispreserve = false;
  807. // System.out.println(name+": m_doIndent = "+m_doIndent+", m_ispreserve = "+m_ispreserve+", m_isprevtext = "+m_isprevtext);
  808. if (shouldIndent() && m_startNewLine)
  809. {
  810. indent(m_currentIndent);
  811. }
  812. m_startNewLine = true;
  813. try
  814. {
  815. m_writer.write('<');
  816. m_writer.write(name);
  817. }
  818. catch(IOException ioe)
  819. {
  820. throw new SAXException(ioe);
  821. }
  822. int nAttrs = atts.getLength();
  823. for (int i = 0; i < nAttrs; i++)
  824. {
  825. processAttribute(atts.getQName(i), atts.getValue(i));
  826. }
  827. // Flag the current element as not yet having any children.
  828. openElementForChildren();
  829. m_currentIndent += m_indentAmount;
  830. m_isprevtext = false;
  831. }
  832. /**
  833. * Check to see if a parent's ">" has been written, and, if
  834. * it has not, write it.
  835. *
  836. * @throws org.xml.sax.SAXException
  837. */
  838. protected void writeParentTagEnd() throws org.xml.sax.SAXException
  839. {
  840. // See if the parent element has already been flagged as having children.
  841. if (!m_elemStack.peekOrTrue())
  842. {
  843. try
  844. {
  845. m_writer.write('>');
  846. }
  847. catch(IOException ioe)
  848. {
  849. throw new SAXException(ioe);
  850. }
  851. m_isprevtext = false;
  852. m_elemStack.setTop(true);
  853. m_preserves.push(m_ispreserve);
  854. }
  855. }
  856. /**
  857. * Flag the current element as not yet having any
  858. * children.
  859. */
  860. protected void openElementForChildren()
  861. {
  862. // Flag the current element as not yet having any children.
  863. m_elemStack.push(false);
  864. }
  865. /**
  866. * Tell if child nodes have been added to the current
  867. * element. Must be called in balance with openElementForChildren().
  868. *
  869. * @return true if child nodes were added.
  870. */
  871. protected boolean childNodesWereAdded()
  872. {
  873. return m_elemStack.isEmpty() ? false : m_elemStack.pop();
  874. }
  875. /**
  876. * Receive notification of the end of an element.
  877. *
  878. *
  879. * @param namespaceURI The Namespace URI, or the empty string if the
  880. * element has no Namespace URI or if Namespace
  881. * processing is not being performed.
  882. * @param localName The local name (without prefix), or the
  883. * empty string if Namespace processing is not being
  884. * performed.
  885. * @param name The element type name
  886. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  887. * wrapping another exception.
  888. *
  889. * @throws org.xml.sax.SAXException
  890. */
  891. public void endElement(String namespaceURI, String localName, String name)
  892. throws org.xml.sax.SAXException
  893. {
  894. if (m_inEntityRef)
  895. return;
  896. m_currentIndent -= m_indentAmount;
  897. boolean hasChildNodes = childNodesWereAdded();
  898. try
  899. {
  900. final Writer writer = m_writer;
  901. if (hasChildNodes)
  902. {
  903. if (shouldIndent())
  904. indent(m_currentIndent);
  905. writer.write('<');
  906. writer.write('/');
  907. writer.write(name);
  908. writer.write('>');
  909. }
  910. else
  911. {
  912. if (m_spaceBeforeClose)
  913. writer.write(" />");
  914. else
  915. writer.write("/>");
  916. }
  917. }
  918. catch(IOException ioe)
  919. {
  920. throw new SAXException(ioe);
  921. }
  922. if (hasChildNodes)
  923. {
  924. m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
  925. }
  926. m_isprevtext = false;
  927. // m_disableOutputEscapingStates.pop();
  928. m_cdataSectionStates.pop();
  929. }
  930. /**
  931. * Process an attribute.
  932. * @param name The name of the attribute.
  933. * @param value The value of the attribute.
  934. *
  935. * @throws org.xml.sax.SAXException
  936. */
  937. protected void processAttribute(String name, String value)
  938. throws org.xml.sax.SAXException
  939. {
  940. try
  941. {
  942. final Writer writer = m_writer;
  943. writer.write(' ');
  944. writer.write(name);
  945. writer.write("=\"");
  946. writeAttrString(value, m_encoding);
  947. writer.write('\"');
  948. }
  949. catch(IOException ioe)
  950. {
  951. throw new SAXException(ioe);
  952. }
  953. }
  954. /**
  955. * Starts an un-escaping section. All characters printed within an
  956. * un-escaping section are printed as is, without escaping special
  957. * characters into entity references. Only XML and HTML serializers
  958. * need to support this method.
  959. * <p>
  960. * The contents of the un-escaping section will be delivered through
  961. * the regular <tt>characters</tt> event.
  962. *
  963. * @throws org.xml.sax.SAXException
  964. */
  965. public void startNonEscaping() throws org.xml.sax.SAXException
  966. {
  967. m_disableOutputEscapingStates.push(true);
  968. }
  969. /**
  970. * Ends an un-escaping section.
  971. *
  972. * @see #startNonEscaping
  973. *
  974. * @throws org.xml.sax.SAXException
  975. */
  976. public void endNonEscaping() throws org.xml.sax.SAXException
  977. {
  978. m_disableOutputEscapingStates.pop();
  979. }
  980. /**
  981. * Starts a whitespace preserving section. All characters printed
  982. * within a preserving section are printed without indentation and
  983. * without consolidating multiple spaces. This is equivalent to
  984. * the <tt>xml:space="preserve"</tt> attribute. Only XML
  985. * and HTML serializers need to support this method.
  986. * <p>
  987. * The contents of the whitespace preserving section will be delivered
  988. * through the regular <tt>characters</tt> event.
  989. *
  990. * @throws org.xml.sax.SAXException
  991. */
  992. public void startPreserving() throws org.xml.sax.SAXException
  993. {
  994. // Not sure this is really what we want. -sb
  995. m_preserves.push(true);
  996. m_ispreserve = true;
  997. }
  998. /**
  999. * Ends a whitespace preserving section.
  1000. *
  1001. * @see #startPreserving
  1002. *
  1003. * @throws org.xml.sax.SAXException
  1004. */
  1005. public void endPreserving() throws org.xml.sax.SAXException
  1006. {
  1007. // Not sure this is really what we want. -sb
  1008. m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
  1009. }
  1010. /**
  1011. * Receive notification of a processing instruction.
  1012. *
  1013. * @param target The processing instruction target.
  1014. * @param data The processing instruction data, or null if
  1015. * none was supplied.
  1016. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1017. * wrapping another exception.
  1018. *
  1019. * @throws org.xml.sax.SAXException
  1020. */
  1021. public void processingInstruction(String target, String data)
  1022. throws org.xml.sax.SAXException
  1023. {
  1024. if (m_inEntityRef)
  1025. return;
  1026. if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
  1027. {
  1028. startNonEscaping();
  1029. }
  1030. else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
  1031. {
  1032. endNonEscaping();
  1033. }
  1034. else
  1035. {
  1036. try
  1037. {
  1038. final Writer writer = m_writer;
  1039. writeParentTagEnd();
  1040. if (shouldIndent())
  1041. indent(m_currentIndent);
  1042. writer.write('<');
  1043. writer.write('?');
  1044. writer.write(target);
  1045. if (data.length() > 0 &&!Character.isSpaceChar(data.charAt(0)))
  1046. writer.write(' ');
  1047. int indexOfQLT = data.indexOf("?>");
  1048. if (indexOfQLT >= 0)
  1049. {
  1050. // See XSLT spec on error recovery of "?>" in PIs.
  1051. if (indexOfQLT > 0)
  1052. {
  1053. writer.write(data.substring(0, indexOfQLT));
  1054. }
  1055. writer.write("? >"); // add space between.
  1056. if ((indexOfQLT + 2) < data.length())
  1057. {
  1058. writer.write(data.substring(indexOfQLT + 2));
  1059. }
  1060. }
  1061. else
  1062. {
  1063. writer.write(data);
  1064. }
  1065. writer.write('?');
  1066. writer.write('>');
  1067. // Always output a newline char if not inside of an
  1068. // element. The whitespace is not significant in that
  1069. // case.
  1070. if (m_elemStack.isEmpty())
  1071. writer.write(m_lineSep, 0, m_lineSepLen);
  1072. m_startNewLine = true;
  1073. }
  1074. catch(IOException ioe)
  1075. {
  1076. throw new SAXException(ioe);
  1077. }
  1078. }
  1079. }
  1080. /**
  1081. * Report an XML comment anywhere in the document.
  1082. *
  1083. * This callback will be used for comments inside or outside the
  1084. * document element, including comments in the external DTD
  1085. * subset (if read).
  1086. *
  1087. * @param ch An array holding the characters in the comment.
  1088. * @param start The starting position in the array.
  1089. * @param length The number of characters to use from the array.
  1090. * @throws org.xml.sax.SAXException The application may raise an exception.
  1091. */
  1092. public void comment(char ch[], int start, int length)
  1093. throws org.xml.sax.SAXException
  1094. {
  1095. if (m_inEntityRef)
  1096. return;
  1097. writeParentTagEnd();
  1098. if (shouldIndent())
  1099. indent(m_currentIndent);
  1100. try
  1101. {
  1102. final Writer writer = m_writer;
  1103. final int limit = start + length;
  1104. boolean wasDash = false;
  1105. writer.write("<!--");
  1106. // Detect occurrences of two consecutive dashes, handle as necessary.
  1107. for (int i = start; i < limit; i++) {
  1108. if (wasDash && ch[i] == '-') {
  1109. writer.write(ch, start, i - start);
  1110. writer.write(" -");
  1111. start = i + 1;
  1112. }
  1113. wasDash = (ch[i] == '-');
  1114. }
  1115. // Output the remaining characters.
  1116. writer.write(ch, start, limit - start);
  1117. // Protect comment end from a single trailing dash
  1118. if (ch[limit-1] == '-')
  1119. writer.write(' ');
  1120. writer.write("-->");
  1121. }
  1122. catch(IOException ioe)
  1123. {
  1124. throw new SAXException(ioe);
  1125. }
  1126. m_startNewLine = true;
  1127. }
  1128. /**
  1129. * Report the start of a CDATA section.
  1130. *
  1131. * @throws org.xml.sax.SAXException The application may raise an exception.
  1132. * @see #endCDATA
  1133. */
  1134. public void startCDATA() throws org.xml.sax.SAXException
  1135. {
  1136. m_inCData = true;
  1137. }
  1138. /**
  1139. * Report the end of a CDATA section.
  1140. *
  1141. * @throws org.xml.sax.SAXException The application may raise an exception.
  1142. * @see #startCDATA
  1143. */
  1144. public void endCDATA() throws org.xml.sax.SAXException
  1145. {
  1146. m_inCData = false;
  1147. }
  1148. /**
  1149. * Receive notification of cdata.
  1150. *
  1151. * <p>The Parser will call this method to report each chunk of
  1152. * character data. SAX parsers may return all contiguous character
  1153. * data in a single chunk, or they may split it into several
  1154. * chunks; however, all of the characters in any single event
  1155. * must come from the same external entity, so that the Locator
  1156. * provides useful information.</p>
  1157. *
  1158. * <p>The application must not attempt to read from the array
  1159. * outside of the specified range.</p>
  1160. *
  1161. * <p>Note that some parsers will report whitespace using the
  1162. * ignorableWhitespace() method rather than this one (validating
  1163. * parsers must do so).</p>
  1164. *
  1165. * @param ch The characters from the XML document.
  1166. * @param start The start position in the array.
  1167. * @param length The number of characters to read from the array.
  1168. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1169. * wrapping another exception.
  1170. * @see #ignorableWhitespace
  1171. * @see org.xml.sax.Locator
  1172. *
  1173. * @throws org.xml.sax.SAXException
  1174. */
  1175. public void cdata(char ch[], int start, int length)
  1176. throws org.xml.sax.SAXException
  1177. {
  1178. try
  1179. {
  1180. writeParentTagEnd();
  1181. m_ispreserve = true;
  1182. if (shouldIndent())
  1183. indent(m_currentIndent);
  1184. boolean writeCDataBrackets = (((length >= 1) && canConvert(ch[start])));
  1185. if (writeCDataBrackets)
  1186. {
  1187. m_writer.write("<![CDATA[");
  1188. }
  1189. // m_writer.write(ch, start, length);
  1190. if (isEscapingDisabled())
  1191. {
  1192. charactersRaw(ch, start, length);
  1193. }
  1194. else
  1195. writeNormalizedChars(ch, start, length, true);
  1196. if (writeCDataBrackets)
  1197. {
  1198. m_writer.write("]]>");
  1199. }
  1200. }
  1201. catch (IOException ioe)
  1202. {
  1203. throw new org.xml.sax.SAXException(
  1204. XSLMessages.createXPATHMessage(XPATHErrorResources.ER_OIERROR, null),
  1205. ioe); //"IO error", ioe);
  1206. }
  1207. }
  1208. /** The current position in the m_charBuf or m_byteBuf. */
  1209. protected int m_pos = 0;
  1210. /**
  1211. * Append a character to the buffer.
  1212. *
  1213. * @param b byte to be written to result stream.
  1214. *
  1215. * @throws org.xml.sax.SAXException
  1216. */
  1217. protected final void accum(char b) throws org.xml.sax.SAXException
  1218. {
  1219. try
  1220. {
  1221. m_writer.write(b);
  1222. }
  1223. catch(IOException ioe)
  1224. {
  1225. throw new SAXException(ioe);
  1226. }
  1227. }
  1228. /**
  1229. * Append a character to the buffer.
  1230. *
  1231. * @param chars non-null reference to character array.
  1232. * @param start Start of characters to be written.
  1233. * @param length Number of characters to be written.
  1234. *
  1235. * @throws org.xml.sax.SAXException
  1236. */
  1237. protected final void accum(char chars[], int start, int length)
  1238. throws org.xml.sax.SAXException
  1239. {
  1240. try
  1241. {
  1242. m_writer.write(chars, start, length);
  1243. }
  1244. catch(IOException ioe)
  1245. {
  1246. throw new SAXException(ioe);
  1247. }
  1248. }
  1249. /**
  1250. * Append a character to the buffer.
  1251. *
  1252. * @param s non-null reference to string to be written to the character buffer.
  1253. *
  1254. * @throws org.xml.sax.SAXException
  1255. */
  1256. protected final void accum(String s) throws org.xml.sax.SAXException
  1257. {
  1258. try
  1259. {
  1260. m_writer.write(s);
  1261. }
  1262. catch(IOException ioe)
  1263. {
  1264. throw new SAXException(ioe);
  1265. }
  1266. }
  1267. /**
  1268. * Flush the formatter's result stream.
  1269. *
  1270. * @throws org.xml.sax.SAXException
  1271. */
  1272. public final void flushWriter() throws org.xml.sax.SAXException
  1273. {
  1274. if (null != m_writer)
  1275. {
  1276. try
  1277. {
  1278. if (m_writer instanceof WriterToUTF8Buffered)
  1279. {
  1280. if(m_shouldFlush)
  1281. ((WriterToUTF8Buffered) m_writer).flush();
  1282. else
  1283. ((WriterToUTF8Buffered) m_writer).flushBuffer();
  1284. }
  1285. if (m_writer instanceof WriterToUTF8)
  1286. {
  1287. if(m_shouldFlush)
  1288. m_writer.flush();
  1289. }
  1290. else if (m_writer instanceof WriterToASCI)
  1291. {
  1292. if(m_shouldFlush)
  1293. m_writer.flush();
  1294. }
  1295. else
  1296. {
  1297. // Flush always.
  1298. // Not a great thing if the writer was created
  1299. // by this class, but don't have a choice.
  1300. m_writer.flush();
  1301. }
  1302. }
  1303. catch (IOException ioe)
  1304. {
  1305. throw new org.xml.sax.SAXException(ioe);
  1306. }
  1307. }
  1308. }
  1309. /**
  1310. * Receive notification of character data.
  1311. *
  1312. * <p>The Parser will call this method to report each chunk of
  1313. * character data. SAX parsers may return all contiguous character
  1314. * data in a single chunk, or they may split it into several
  1315. * chunks; however, all of the characters in any single event
  1316. * must come from the same external entity, so that the Locator
  1317. * provides useful information.</p>
  1318. *
  1319. * <p>The application must not attempt to read from the array
  1320. * outside of the specified range.</p>
  1321. *
  1322. * <p>Note that some parsers will report whitespace using the
  1323. * ignorableWhitespace() method rather than this one (validating
  1324. * parsers must do so).</p>
  1325. *
  1326. * @param chars The characters from the XML document.
  1327. * @param start The start position in the array.
  1328. * @param length The number of characters to read from the array.
  1329. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1330. * wrapping another exception.
  1331. * @see #ignorableWhitespace
  1332. * @see org.xml.sax.Locator
  1333. *
  1334. * @throws org.xml.sax.SAXException
  1335. */
  1336. public void characters(char chars[], int start, int length)
  1337. throws org.xml.sax.SAXException
  1338. {
  1339. if(0 == length)
  1340. return;
  1341. // if (m_inEntityRef)
  1342. // return;
  1343. // else
  1344. if (m_inCData || m_cdataSectionStates.peekOrFalse())
  1345. {
  1346. cdata(chars, start, length);
  1347. return;
  1348. }
  1349. try
  1350. {
  1351. if (m_disableOutputEscapingStates.peekOrFalse())
  1352. {
  1353. charactersRaw(chars, start, length);
  1354. return;
  1355. }
  1356. final Writer writer = m_writer;
  1357. if (!m_elemStack.peekOrTrue())
  1358. {
  1359. writer.write('>');
  1360. m_isprevtext = false;
  1361. m_elemStack.setTop(true);
  1362. m_preserves.push(m_ispreserve);
  1363. }
  1364. int startClean = start;
  1365. int lengthClean = 0;
  1366. // int pos = 0;
  1367. int end = start + length;
  1368. boolean checkWhite = true;
  1369. final int maxCharacter = m_maxCharacter;
  1370. final BitSet specialsMap = m_charInfo.m_specialsMap;
  1371. for (int i = start; i < end; i++)
  1372. {
  1373. char ch = chars[i];
  1374. if (checkWhite
  1375. && ((ch > 0x20)
  1376. ||!((ch == 0x20) || (ch == 0x09) || (ch == 0xD)
  1377. || (ch == 0xA))))
  1378. {
  1379. m_ispreserve = true;
  1380. checkWhite = false;
  1381. }
  1382. if ((canConvert(ch) && (!specialsMap.get(ch))) || ('"' == ch))
  1383. {
  1384. lengthClean++;
  1385. }
  1386. else
  1387. {
  1388. if (lengthClean > 0)
  1389. {
  1390. writer.write(chars, startClean, lengthClean);
  1391. lengthClean = 0;
  1392. }
  1393. if (CharInfo.S_LINEFEED == ch)
  1394. {
  1395. writer.write(m_lineSep, 0, m_lineSepLen);
  1396. startClean = i + 1;
  1397. }
  1398. else
  1399. {
  1400. startClean = accumDefaultEscape(ch, i, chars, end, false);
  1401. i = startClean - 1;
  1402. }
  1403. }
  1404. }
  1405. if (lengthClean > 0)
  1406. {
  1407. writer.write(chars, startClean, lengthClean);
  1408. }
  1409. }
  1410. catch(IOException ioe)
  1411. {
  1412. throw new SAXException(ioe);
  1413. }
  1414. m_isprevtext = true;
  1415. }
  1416. /**
  1417. * If available, when the disable-output-escaping attribute is used,
  1418. * output raw text without escaping.
  1419. *
  1420. * @param ch The characters from the XML document.
  1421. * @param start The start position in the array.
  1422. * @param length The number of characters to read from the array.
  1423. *
  1424. * @throws org.xml.sax.SAXException
  1425. */
  1426. public void charactersRaw(char ch[], int start, int length)
  1427. throws org.xml.sax.SAXException
  1428. {
  1429. try
  1430. {
  1431. if (m_inEntityRef)
  1432. return;
  1433. writeParentTagEnd();
  1434. m_ispreserve = true;
  1435. m_writer.write(ch, start, length);
  1436. }
  1437. catch(IOException ioe)
  1438. {
  1439. throw new SAXException(ioe);
  1440. }
  1441. }
  1442. /**
  1443. * Return true if the character is the high member of a surrogate pair.
  1444. *
  1445. * NEEDSDOC @param c
  1446. *
  1447. * NEEDSDOC ($objectName$) @return
  1448. */
  1449. static final boolean isUTF16Surrogate(char c)
  1450. {
  1451. return (c & 0xFC00) == 0xD800;
  1452. }
  1453. /**
  1454. * Once a surrogate has been detected, get the pair as a single
  1455. * integer value.
  1456. *
  1457. * @param c the first part of the surrogate.
  1458. * @param ch Character array.
  1459. * @param i position Where the surrogate was detected.
  1460. * @param end The end index of the significant characters.
  1461. * @return i+1.
  1462. * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
  1463. */
  1464. int getURF16SurrogateValue(char c, char ch[], int i, int end)
  1465. throws org.xml.sax.SAXException
  1466. {
  1467. int next;
  1468. if (i + 1 >= end)
  1469. {
  1470. throw new org.xml.sax.SAXException(
  1471. XSLMessages.createXPATHMessage(
  1472. XPATHErrorResources.ER_INVALID_UTF16_SURROGATE,
  1473. new Object[]{ Integer.toHexString((int) c) })); //"Invalid UTF-16 surrogate detected: "
  1474. //+Integer.toHexString((int)c)+ " ?");
  1475. }
  1476. else
  1477. {
  1478. next = ch[++i];
  1479. if (!(0xdc00 <= next && next < 0xe000))
  1480. throw new org.xml.sax.SAXException(
  1481. XSLMessages.createXPATHMessage(
  1482. XPATHErrorResources.ER_INVALID_UTF16_SURROGATE,
  1483. new Object[]{
  1484. Integer.toHexString((int) c) + " "
  1485. + Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: "
  1486. //+Integer.toHexString((int)c)+" "+Integer.toHexString(next));
  1487. next = ((c - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
  1488. }
  1489. return next;
  1490. }
  1491. /**
  1492. * Once a surrogate has been detected, write the pair as a single
  1493. * character reference.
  1494. *
  1495. * @param c the first part of the surrogate.
  1496. * @param ch Character array.
  1497. * @param i position Where the surrogate was detected.
  1498. * @param end The end index of the significant characters.
  1499. * @return i+1.
  1500. * @throws IOException
  1501. * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
  1502. */
  1503. protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
  1504. throws IOException, org.xml.sax.SAXException
  1505. {
  1506. // UTF-16 surrogate
  1507. int surrogateValue = getURF16SurrogateValue(c, ch, i, end);
  1508. i++;
  1509. m_writer.write('&');
  1510. m_writer.write('#');
  1511. // m_writer.write('x');
  1512. m_writer.write(Integer.toString(surrogateValue));
  1513. m_writer.write(';');
  1514. return i;
  1515. }
  1516. /**
  1517. * Normalize the characters, but don't escape.
  1518. *
  1519. * @param ch The characters from the XML document.
  1520. * @param start The start position in the array.
  1521. * @param length The number of characters to read from the array.
  1522. * @param isCData true if a CDATA block should be built around the characters.
  1523. *
  1524. * @throws IOException
  1525. * @throws org.xml.sax.SAXException
  1526. */
  1527. void writeNormalizedChars(char ch[], int start, int length, boolean isCData)
  1528. throws IOException, org.xml.sax.SAXException
  1529. {
  1530. int end = start + length;
  1531. for (int i = start; i < end; i++)
  1532. {
  1533. char c = ch[i];
  1534. if (CharInfo.S_LINEFEED == c)
  1535. {
  1536. m_writer.write(m_lineSep, 0, m_lineSepLen);
  1537. }
  1538. else if (isCData && (!canConvert(c)))
  1539. {
  1540. if (i != 0)
  1541. m_writer.write("]]>");
  1542. // This needs to go into a function...
  1543. if (isUTF16Surrogate(c))
  1544. {
  1545. i = writeUTF16Surrogate(c, ch, i, end);
  1546. }
  1547. else
  1548. {
  1549. m_writer.write("&#");
  1550. String intStr = Integer.toString((int) c);
  1551. m_writer.write(intStr);
  1552. m_writer.write(';');
  1553. }
  1554. if ((i != 0) && (i < (end - 1)))
  1555. m_writer.write("<![CDATA[");
  1556. }
  1557. else if (isCData
  1558. && ((i < (end - 2)) && (']' == c) && (']' == ch[i + 1])
  1559. && ('>' == ch[i + 2])))
  1560. {
  1561. m_writer.write("]]]]><![CDATA[>");
  1562. i += 2;
  1563. }
  1564. else
  1565. {
  1566. if (canConvert(c))
  1567. {
  1568. m_writer.write(c);
  1569. }
  1570. // This needs to go into a function...
  1571. else if (isUTF16Surrogate(c))
  1572. {
  1573. i = writeUTF16Surrogate(c, ch, i, end);
  1574. }
  1575. else
  1576. {
  1577. m_writer.write("&#");
  1578. String intStr = Integer.toString((int) c);
  1579. m_writer.write(intStr);
  1580. m_writer.write(';');
  1581. }
  1582. }
  1583. }
  1584. }
  1585. /**
  1586. * Receive notification of ignorable whitespace in element content.
  1587. *
  1588. * Not sure how to get this invoked quite yet.
  1589. *
  1590. * @param ch The characters from the XML document.
  1591. * @param start The start position in the array.
  1592. * @param length The number of characters to read from the array.
  1593. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1594. * wrapping another exception.
  1595. * @see #characters
  1596. *
  1597. * @throws org.xml.sax.SAXException
  1598. */
  1599. public void ignorableWhitespace(char ch[], int start, int length)
  1600. throws org.xml.sax.SAXException
  1601. {
  1602. if (0 == length)
  1603. return;
  1604. characters(ch, start, length);
  1605. }
  1606. /**
  1607. * Receive notification of a skipped entity.
  1608. * @see org.xml.sax.ContentHandler#skippedEntity
  1609. *
  1610. * @param name The name of the skipped entity. If it is a
  1611. * parameter entity, the name will begin with '%', and if
  1612. * it is the external DTD subset, it will be the string
  1613. * "[dtd]".
  1614. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1615. * wrapping another exception.
  1616. */
  1617. public void skippedEntity(String name) throws org.xml.sax.SAXException
  1618. {
  1619. // TODO: Should handle
  1620. }
  1621. /**
  1622. * Report the beginning of an entity.
  1623. *
  1624. * The start and end of the document entity are not reported.
  1625. * The start and end of the external DTD subset are reported
  1626. * using the pseudo-name "[dtd]". All other events must be
  1627. * properly nested within start/end entity events.
  1628. *
  1629. * @param name The name of the entity. If it is a parameter
  1630. * entity, the name will begin with '%'.
  1631. * @throws org.xml.sax.SAXException The application may raise an exception.
  1632. * @see #endEntity
  1633. * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
  1634. * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
  1635. */
  1636. public void startEntity(String name) throws org.xml.sax.SAXException
  1637. {
  1638. if (name.equals("[dtd]"))
  1639. m_inExternalDTD = true;
  1640. m_inEntityRef = true;
  1641. }
  1642. /**
  1643. * Report the end of an entity.
  1644. *
  1645. * @param name The name of the entity that is ending.
  1646. * @throws org.xml.sax.SAXException The application may raise an exception.
  1647. * @see #startEntity
  1648. */
  1649. public void endEntity(String name) throws org.xml.sax.SAXException
  1650. {
  1651. if (name.equals("[dtd]"))
  1652. m_inExternalDTD = false;
  1653. m_inEntityRef = false;
  1654. }
  1655. /**
  1656. * Receive notivication of a entityReference.
  1657. *
  1658. * @param name The name of the entity.
  1659. *
  1660. * @throws org.xml.sax.SAXException
  1661. */
  1662. public void entityReference(String name) throws org.xml.sax.SAXException
  1663. {
  1664. writeParentTagEnd();
  1665. if (shouldIndent())
  1666. indent(m_currentIndent);
  1667. try
  1668. {
  1669. final Writer writer = m_writer;
  1670. writer.write("&");
  1671. writer.write(name);
  1672. writer.write(";");
  1673. }
  1674. catch(IOException ioe)
  1675. {
  1676. throw new SAXException(ioe);
  1677. }
  1678. }
  1679. // Implement DeclHandler
  1680. /**
  1681. * Report an element type declaration.
  1682. *
  1683. * <p>The content model will consist of the string "EMPTY", the
  1684. * string "ANY", or a parenthesised group, optionally followed
  1685. * by an occurrence indicator. The model will be normalized so
  1686. * that all whitespace is removed,and will include the enclosing
  1687. * parentheses.</p>
  1688. *
  1689. * @param name The element type name.
  1690. * @param model The content model as a normalized string.
  1691. * @exception SAXException The application may raise an exception.
  1692. */
  1693. public void elementDecl(String name, String model) throws SAXException
  1694. {
  1695. // Do not inline external DTD
  1696. if (m_inExternalDTD) return;
  1697. try
  1698. {
  1699. final Writer writer = m_writer;
  1700. if (m_inDoctype)
  1701. {
  1702. writer.write(" [");
  1703. writer.write(m_lineSep, 0, m_lineSepLen);
  1704. m_inDoctype = false;
  1705. }
  1706. writer.write("<!ELEMENT ");
  1707. writer.write(name);
  1708. writer.write(' ');
  1709. writer.write(model);
  1710. writer.write('>');
  1711. writer.write(m_lineSep, 0, m_lineSepLen);
  1712. }
  1713. catch(IOException ioe)
  1714. {
  1715. throw new SAXException(ioe);
  1716. }
  1717. }
  1718. /** NEEDSDOC Field m_elemName */
  1719. private String m_elemName = "";
  1720. /**
  1721. * Report an attribute type declaration.
  1722. *
  1723. * <p>Only the effective (first) declaration for an attribute will
  1724. * be reported. The type will be one of the strings "CDATA",
  1725. * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
  1726. * "ENTITIES", or "NOTATION", or a parenthesized token group with
  1727. * the separator "|" and all whitespace removed.</p>
  1728. *
  1729. * @param eName The name of the associated element.
  1730. * @param aName The name of the attribute.
  1731. * @param type A string representing the attribute type.
  1732. * @param valueDefault A string representing the attribute default
  1733. * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
  1734. * none of these applies.
  1735. * @param value A string representing the attribute's default value,
  1736. * or null if there is none.
  1737. * @exception SAXException The application may raise an exception.
  1738. */
  1739. public void attributeDecl(
  1740. String eName, String aName, String type, String valueDefault, String value)
  1741. throws SAXException
  1742. {
  1743. // Do not inline external DTD
  1744. if (m_inExternalDTD) return;
  1745. try
  1746. {
  1747. final Writer writer = m_writer;
  1748. if (m_inDoctype)
  1749. {
  1750. writer.write(" [");
  1751. writer.write(m_lineSep, 0, m_lineSepLen);
  1752. m_inDoctype = false;
  1753. }
  1754. writer.write("<!ATTLIST ");
  1755. writer.write(eName);
  1756. writer.write(" ");
  1757. writer.write(aName);
  1758. writer.write(" ");
  1759. writer.write(type);
  1760. if(valueDefault!=null)
  1761. {
  1762. writer.write(" ");
  1763. writer.write(valueDefault);
  1764. }
  1765. //m_writer.write(" ");
  1766. //m_writer.write(value);
  1767. writer.write(">");
  1768. writer.write(m_lineSep, 0, m_lineSepLen);
  1769. }
  1770. catch(IOException ioe)
  1771. {
  1772. throw new SAXException(ioe);
  1773. }
  1774. }
  1775. /**
  1776. * Report an internal entity declaration.
  1777. *
  1778. * <p>Only the effective (first) declaration for each entity
  1779. * will be reported.</p>
  1780. *
  1781. * @param name The name of the entity. If it is a parameter
  1782. * entity, the name will begin with '%'.
  1783. * @param value The replacement text of the entity.
  1784. * @exception SAXException The application may raise an exception.
  1785. * @see #externalEntityDecl
  1786. * @see org.xml.sax.DTDHandler#unparsedEntityDecl
  1787. */
  1788. public void internalEntityDecl(String name, String value)
  1789. throws SAXException
  1790. {
  1791. // Do not inline external DTD
  1792. if (m_inExternalDTD) return;
  1793. try
  1794. {
  1795. if (m_inDoctype)
  1796. {
  1797. m_writer.write(" [");
  1798. m_writer.write(m_lineSep, 0, m_lineSepLen);
  1799. m_inDoctype = false;
  1800. }
  1801. outputEntityDecl(name, value);
  1802. }
  1803. catch(IOException ioe)
  1804. {
  1805. throw new SAXException(ioe);
  1806. }
  1807. }
  1808. /**
  1809. * Report a parsed external entity declaration.
  1810. *
  1811. * <p>Only the effective (first) declaration for each entity
  1812. * will be reported.</p>
  1813. *
  1814. * @param name The name of the entity. If it is a parameter
  1815. * entity, the name will begin with '%'.
  1816. * @param publicId The declared public identifier of the entity, or
  1817. * null if none was declared.
  1818. * @param systemId The declared system identifier of the entity.
  1819. * @exception SAXException The application may raise an exception.
  1820. * @see #internalEntityDecl
  1821. * @see org.xml.sax.DTDHandler#unparsedEntityDecl
  1822. */
  1823. public void externalEntityDecl(
  1824. String name, String publicId, String systemId) throws SAXException{}
  1825. /**
  1826. * Handle one of the default entities, return false if it
  1827. * is not a default entity.
  1828. *
  1829. * @param ch character to be escaped.
  1830. * @param i index into character array.
  1831. * @param chars non-null reference to character array.
  1832. * @param len length of chars.
  1833. * @param escLF true if the linefeed should be escaped.
  1834. *
  1835. * @return i+1 if the character was written, else i.
  1836. *
  1837. * @throws org.xml.sax.SAXException
  1838. */
  1839. protected int accumDefaultEntity(
  1840. char ch, int i, char[] chars, int len, boolean escLF)
  1841. throws org.xml.sax.SAXException
  1842. {
  1843. try
  1844. {
  1845. if (!escLF && CharInfo.S_LINEFEED == ch)
  1846. {
  1847. m_writer.write(m_lineSep, 0, m_lineSepLen);
  1848. }
  1849. else
  1850. {
  1851. if (m_charInfo.isSpecial(ch))
  1852. {
  1853. String entityRef = m_charInfo.getEntityNameForChar(ch);
  1854. if (null != entityRef)
  1855. {
  1856. final Writer writer = m_writer;
  1857. writer.write('&');
  1858. writer.write(entityRef);
  1859. writer.write(';');
  1860. }
  1861. else
  1862. return i;
  1863. }
  1864. else
  1865. return i;
  1866. }
  1867. return i + 1;
  1868. }
  1869. catch(IOException ioe)
  1870. {
  1871. throw new SAXException(ioe);
  1872. }
  1873. }
  1874. /**
  1875. * Escape and m_writer.write a character.
  1876. *
  1877. * @param ch character to be escaped.
  1878. * @param i index into character array.
  1879. * @param chars non-null reference to character array.
  1880. * @param len length of chars.
  1881. * @param escLF true if the linefeed should be escaped.
  1882. *
  1883. * @return i+1 if the character was written, else i.
  1884. *
  1885. * @throws org.xml.sax.SAXException
  1886. */
  1887. protected int accumDefaultEscape(
  1888. char ch, int i, char[] chars, int len, boolean escLF)
  1889. throws org.xml.sax.SAXException
  1890. {
  1891. int pos = accumDefaultEntity(ch, i, chars, len, escLF);
  1892. if (i == pos)
  1893. {
  1894. pos++;
  1895. try
  1896. {
  1897. if (0xd800 <= ch && ch < 0xdc00)
  1898. {
  1899. // UTF-16 surrogate
  1900. int next;
  1901. if (i + 1 >= len)
  1902. {
  1903. throw new org.xml.sax.SAXException(
  1904. XSLMessages.createXPATHMessage(
  1905. XPATHErrorResources.ER_INVALID_UTF16_SURROGATE,
  1906. new Object[]{ Integer.toHexString(ch) })); //"Invalid UTF-16 surrogate detected: "
  1907. //+Integer.toHexString(ch)+ " ?");
  1908. }
  1909. else
  1910. {
  1911. next = chars[++i];
  1912. if (!(0xdc00 <= next && next < 0xe000))
  1913. throw new org.xml.sax.SAXException(
  1914. XSLMessages.createXPATHMessage(
  1915. XPATHErrorResources.ER_INVALID_UTF16_SURROGATE,
  1916. new Object[]{
  1917. Integer.toHexString(ch) + " "
  1918. + Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: "
  1919. //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
  1920. next = ((ch - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
  1921. }
  1922. m_writer.write("&#");
  1923. m_writer.write(Integer.toString(next));
  1924. m_writer.write(";");
  1925. /*} else if (null != ctbc && !ctbc.canConvert(ch)) {
  1926. sb.append("&#x");
  1927. sb.append(Integer.toString((int)ch, 16));
  1928. sb.append(";");*/
  1929. }
  1930. else
  1931. {
  1932. if (!canConvert(ch) || (m_charInfo.isSpecial(ch)))
  1933. {
  1934. m_writer.write("&#");
  1935. m_writer.write(Integer.toString(ch));
  1936. m_writer.write(";");
  1937. }
  1938. else
  1939. {
  1940. m_writer.write(ch);
  1941. }
  1942. }
  1943. }
  1944. catch(IOException ioe)
  1945. {
  1946. throw new SAXException(ioe);
  1947. }
  1948. }
  1949. return pos;
  1950. }
  1951. /**
  1952. * Opaque reference to the sun.io.CharToByteConverter for this
  1953. * encoding.
  1954. */
  1955. Object m_charToByteConverter = null;
  1956. /**
  1957. * Method reference to the sun.io.CharToByteConverter#canConvert method
  1958. * for this encoding. Invalid if m_charToByteConverter is null.
  1959. */
  1960. java.lang.reflect.Method m_canConvertMeth;
  1961. /**
  1962. * Boolean that tells if we already tried to get the converter.
  1963. */
  1964. boolean m_triedToGetConverter = false;
  1965. /**
  1966. * Tell if this character can be written without escaping.
  1967. */
  1968. public boolean canConvert(char ch)
  1969. {
  1970. if(ch < 127)
  1971. {
  1972. if(ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch) )
  1973. return true;
  1974. else
  1975. return false;
  1976. }
  1977. if(null == m_charToByteConverter && false == m_triedToGetConverter)
  1978. {
  1979. m_triedToGetConverter = true;
  1980. try
  1981. {
  1982. m_charToByteConverter = Encodings.getCharToByteConverter(m_encoding);
  1983. if(null != m_charToByteConverter)
  1984. {
  1985. Class argsTypes[] = new Class[1];
  1986. argsTypes[0] = Character.TYPE;
  1987. Class convClass = m_charToByteConverter.getClass();
  1988. m_canConvertMeth = convClass.getMethod("canConvert", argsTypes);
  1989. }
  1990. }
  1991. catch(Exception e)
  1992. {
  1993. // This is just an assert: no action at the moment.
  1994. System.err.println("Warning: "+e.getMessage());
  1995. }
  1996. }
  1997. if(null != m_charToByteConverter)
  1998. {
  1999. try
  2000. {
  2001. Object args[] = new Object[1];
  2002. args[0] = new Character( ch );
  2003. Boolean bool
  2004. = (Boolean)m_canConvertMeth.invoke(m_charToByteConverter, args);
  2005. return bool.booleanValue() ? !Character.isISOControl(ch) : false;
  2006. }
  2007. catch(java.lang.reflect.InvocationTargetException ite)
  2008. {
  2009. // This is just an assert: no action at the moment.
  2010. System.err.println("Warning: InvocationTargetException in canConvert!");
  2011. }
  2012. catch(java.lang.IllegalAccessException iae)
  2013. {
  2014. // This is just an assert: no action at the moment.
  2015. System.err.println("Warning: IllegalAccessException in canConvert!");
  2016. }
  2017. }
  2018. // fallback!
  2019. return ( ch <= m_maxCharacter );
  2020. }
  2021. /**
  2022. * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
  2023. * and UTF-16 surrogates for chracter references <CODE>&#xnn</CODE>.
  2024. *
  2025. * @param string String to convert to XML format.
  2026. * @param encoding CURRENTLY NOT IMPLEMENTED.
  2027. *
  2028. * @throws org.xml.sax.SAXException
  2029. */
  2030. public void writeAttrString(String string, String encoding)
  2031. throws org.xml.sax.SAXException
  2032. {
  2033. try
  2034. {
  2035. final char[] stringChars = string.toCharArray();
  2036. final int len = stringChars.length;
  2037. final Writer writer = m_writer;
  2038. for (int i = 0; i < len; i++)
  2039. {
  2040. char ch = stringChars[i];
  2041. if (canConvert(ch) && (!m_charInfo.isSpecial(ch)))
  2042. {
  2043. writer.write(ch);
  2044. }
  2045. else
  2046. {
  2047. // I guess the parser doesn't normalize cr/lf in attributes. -sb
  2048. if ((CharInfo.S_CARRIAGERETURN == ch) && ((i + 1) < len)
  2049. && (CharInfo.S_LINEFEED == stringChars[i + 1]))
  2050. {
  2051. i++;
  2052. ch = CharInfo.S_LINEFEED;
  2053. }
  2054. accumDefaultEscape(ch, i, stringChars, len, true);
  2055. }
  2056. }
  2057. }
  2058. catch(IOException ioe)
  2059. {
  2060. throw new SAXException(ioe);
  2061. }
  2062. }
  2063. /**
  2064. * Tell if, based on space preservation constraints and the doIndent property,
  2065. * if an indent should occur.
  2066. *
  2067. * @return True if an indent should occur.
  2068. */
  2069. protected boolean shouldIndent()
  2070. {
  2071. return m_doIndent && (!m_ispreserve &&!m_isprevtext);
  2072. }
  2073. /**
  2074. * Prints <var>n</var> spaces.
  2075. * @param pw The character output stream to use.
  2076. * @param n Number of spaces to print.
  2077. *
  2078. * @throws org.xml.sax.SAXException if an error occurs when writing.
  2079. */
  2080. public void printSpace(int n) throws org.xml.sax.SAXException
  2081. {
  2082. try
  2083. {
  2084. for (int i = 0; i < n; i++)
  2085. {
  2086. m_writer.write(' ');
  2087. }
  2088. }
  2089. catch(IOException ioe)
  2090. {
  2091. throw new SAXException(ioe);
  2092. }
  2093. }
  2094. /**
  2095. * Prints a newline character and <var>n</var> spaces.
  2096. * @param pw The character output stream to use.
  2097. * @param n Number of spaces to print.
  2098. *
  2099. * @throws org.xml.sax.SAXException if an error occurs during writing.
  2100. */
  2101. public void indent(int n) throws org.xml.sax.SAXException
  2102. {
  2103. if (m_startNewLine)
  2104. outputLineSep();
  2105. if (m_doIndent)
  2106. {
  2107. printSpace(n);
  2108. }
  2109. }
  2110. /**
  2111. * Specifies an output stream to which the document should be
  2112. * serialized. This method should not be called while the
  2113. * serializer is in the process of serializing a document.
  2114. * <p>
  2115. * The encoding specified in the output properties is used, or
  2116. * if no encoding was specified, the default for the selected
  2117. * output method.
  2118. *
  2119. * @param output The output stream
  2120. */
  2121. public void setOutputStream(OutputStream output)
  2122. {
  2123. try
  2124. {
  2125. init(output, m_format);
  2126. }
  2127. catch (UnsupportedEncodingException uee)
  2128. {
  2129. // Should have been warned in init, I guess...
  2130. }
  2131. }
  2132. /**
  2133. * Get the output stream where the events will be serialized to.
  2134. *
  2135. * @return reference to the result stream, or null of only a writer was
  2136. * set.
  2137. */
  2138. public OutputStream getOutputStream()
  2139. {
  2140. if (m_writer instanceof WriterToUTF8Buffered)
  2141. return ((WriterToUTF8Buffered) m_writer).getOutputStream();
  2142. if (m_writer instanceof WriterToUTF8)
  2143. return ((WriterToUTF8) m_writer).getOutputStream();
  2144. else if (m_writer instanceof WriterToASCI)
  2145. return ((WriterToASCI) m_writer).getOutputStream();
  2146. else
  2147. return null;
  2148. }
  2149. /**
  2150. * Specifies a writer to which the document should be serialized.
  2151. * This method should not be called while the serializer is in
  2152. * the process of serializing a document.
  2153. *
  2154. * @param writer The output writer stream
  2155. */
  2156. public void setWriter(Writer writer)
  2157. {
  2158. m_writer = writer;
  2159. }
  2160. /**
  2161. * Get the character stream where the events will be serialized to.
  2162. *
  2163. * @return Reference to the result Writer, or null.
  2164. */
  2165. public Writer getWriter()
  2166. {
  2167. return m_writer;
  2168. }
  2169. /**
  2170. * Specifies an output format for this serializer. It the
  2171. * serializer has already been associated with an output format,
  2172. * it will switch to the new format. This method should not be
  2173. * called while the serializer is in the process of serializing
  2174. * a document.
  2175. *
  2176. * @param format The output format to use
  2177. */
  2178. public void setOutputFormat(Properties format)
  2179. {
  2180. boolean shouldFlush = m_shouldFlush;
  2181. init(m_writer, format, false);
  2182. m_shouldFlush = shouldFlush;
  2183. }
  2184. /**
  2185. * Returns the output format for this serializer.
  2186. *
  2187. * @return The output format in use
  2188. */
  2189. public Properties getOutputFormat()
  2190. {
  2191. return m_format;
  2192. }
  2193. /**
  2194. * Return a {@link ContentHandler} interface into this serializer.
  2195. * If the serializer does not support the {@link ContentHandler}
  2196. * interface, it should return null.
  2197. *
  2198. * @return A {@link ContentHandler} interface into this serializer,
  2199. * or null if the serializer is not SAX 2 capable
  2200. * @throws IOException An I/O exception occured
  2201. */
  2202. public ContentHandler asContentHandler() throws IOException
  2203. {
  2204. return this;
  2205. }
  2206. /**
  2207. * Return a {@link DOMSerializer} interface into this serializer.
  2208. * If the serializer does not support the {@link DOMSerializer}
  2209. * interface, it should return null.
  2210. *
  2211. * @return A {@link DOMSerializer} interface into this serializer,
  2212. * or null if the serializer is not DOM capable
  2213. * @throws IOException An I/O exception occured
  2214. */
  2215. public DOMSerializer asDOMSerializer() throws IOException
  2216. {
  2217. return this; // for now
  2218. }
  2219. /**
  2220. * Resets the serializer. If this method returns true, the
  2221. * serializer may be used for subsequent serialization of new
  2222. * documents. It is possible to change the output format and
  2223. * output stream prior to serializing, or to use the existing
  2224. * output format and output stream.
  2225. *
  2226. * @return True if serializer has been reset and can be reused
  2227. */
  2228. public boolean reset()
  2229. {
  2230. return false;
  2231. }
  2232. /**
  2233. * Serializes the DOM node. Throws an exception only if an I/O
  2234. * exception occured while serializing.
  2235. *
  2236. * @param elem The element to serialize
  2237. *
  2238. * @param node Node to serialize.
  2239. * @throws IOException An I/O exception occured while serializing
  2240. */
  2241. public void serialize(Node node) throws IOException
  2242. {
  2243. try
  2244. {
  2245. TreeWalker walker = new TreeWalker(this,
  2246. new org.apache.xpath.DOM2Helper());
  2247. walker.traverse(node);
  2248. }
  2249. catch (org.xml.sax.SAXException se)
  2250. {
  2251. throw new WrappedRuntimeException(se);
  2252. }
  2253. }
  2254. } //ToXMLStringVisitor