1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: ToSAXHandler.java,v 1.10 2004/02/17 04:18:18 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.serializer;
  20. import java.util.Vector;
  21. import org.xml.sax.Attributes;
  22. import org.xml.sax.ContentHandler;
  23. import org.xml.sax.ErrorHandler;
  24. import org.xml.sax.SAXException;
  25. import org.xml.sax.SAXParseException;
  26. import org.xml.sax.ext.LexicalHandler;
  27. /**
  28. * @author Santiago Pericas-Geertsen
  29. * @author G. Todd Miller
  30. */
  31. abstract public class ToSAXHandler extends SerializerBase
  32. {
  33. public ToSAXHandler()
  34. {
  35. }
  36. public ToSAXHandler(
  37. ContentHandler hdlr,
  38. LexicalHandler lex,
  39. String encoding)
  40. {
  41. setContentHandler(hdlr);
  42. setLexHandler(lex);
  43. setEncoding(encoding);
  44. }
  45. public ToSAXHandler(ContentHandler handler, String encoding)
  46. {
  47. setContentHandler(handler);
  48. setEncoding(encoding);
  49. }
  50. /**
  51. * Underlying SAX handler. Taken from XSLTC
  52. */
  53. protected ContentHandler m_saxHandler;
  54. /**
  55. * Underlying LexicalHandler. Taken from XSLTC
  56. */
  57. protected LexicalHandler m_lexHandler;
  58. /**
  59. * A startPrefixMapping() call on a ToSAXHandler will pass that call
  60. * on to the wrapped ContentHandler, but should we also mirror these calls
  61. * with matching attributes, if so this field is true.
  62. * For example if this field is true then a call such as
  63. * startPrefixMapping("prefix1","uri1") will also cause the additional
  64. * internally generated attribute xmlns:prefix1="uri1" to be effectively added
  65. * to the attributes passed to the wrapped ContentHandler.
  66. */
  67. private boolean m_shouldGenerateNSAttribute = true;
  68. /** If this is true, then the content handler wrapped by this
  69. * serializer implements the TransformState interface which
  70. * will give the content handler access to the state of
  71. * the transform. */
  72. protected TransformStateSetter m_state = null;
  73. /**
  74. * Pass callback to the SAX Handler
  75. */
  76. protected void startDocumentInternal() throws SAXException
  77. {
  78. if (m_needToCallStartDocument)
  79. {
  80. super.startDocumentInternal();
  81. m_saxHandler.startDocument();
  82. m_needToCallStartDocument = false;
  83. }
  84. }
  85. /**
  86. * Do nothing.
  87. * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
  88. */
  89. public void startDTD(String arg0, String arg1, String arg2)
  90. throws SAXException
  91. {
  92. // do nothing for now
  93. }
  94. /**
  95. * Receive notification of character data.
  96. *
  97. * @param chars The string of characters to process.
  98. *
  99. * @throws org.xml.sax.SAXException
  100. *
  101. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#characters(String)
  102. */
  103. public void characters(String characters) throws SAXException
  104. {
  105. final int len = characters.length();
  106. if (len > m_charsBuff.length)
  107. {
  108. m_charsBuff = new char[len*2 + 1];
  109. }
  110. characters.getChars(0,len, m_charsBuff, 0);
  111. characters(m_charsBuff, 0, len);
  112. }
  113. /**
  114. * Receive notification of a comment.
  115. *
  116. * @see com.sun.org.apache.xml.internal.serializer.ExtendedLexicalHandler#comment(String)
  117. */
  118. public void comment(String comment) throws SAXException
  119. {
  120. // Close any open element before emitting comment
  121. if (m_elemContext.m_startTagOpen)
  122. {
  123. closeStartTag();
  124. }
  125. else if (m_cdataTagOpen)
  126. {
  127. closeCDATA();
  128. }
  129. // Ignore if a lexical handler has not been set
  130. if (m_lexHandler != null)
  131. {
  132. final int len = comment.length();
  133. if (len > m_charsBuff.length)
  134. {
  135. m_charsBuff = new char[len*2 + 1];
  136. }
  137. comment.getChars(0,len, m_charsBuff, 0);
  138. m_lexHandler.comment(m_charsBuff, 0, len);
  139. // time to fire off comment event
  140. if (m_tracer != null)
  141. super.fireCommentEvent(m_charsBuff, 0, len);
  142. }
  143. }
  144. /**
  145. * Do nothing as this is an abstract class. All subclasses will need to
  146. * define their behavior if it is different.
  147. * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
  148. */
  149. public void processingInstruction(String target, String data)
  150. throws SAXException
  151. {
  152. // Redefined in SAXXMLOutput
  153. }
  154. protected void closeStartTag() throws SAXException
  155. {
  156. }
  157. protected void closeCDATA() throws SAXException
  158. {
  159. // Redefined in SAXXMLOutput
  160. }
  161. /**
  162. * Receive notification of the beginning of an element, although this is a
  163. * SAX method additional namespace or attribute information can occur before
  164. * or after this call, that is associated with this element.
  165. *
  166. *
  167. * @param namespaceURI The Namespace URI, or the empty string if the
  168. * element has no Namespace URI or if Namespace
  169. * processing is not being performed.
  170. * @param localName The local name (without prefix), or the
  171. * empty string if Namespace processing is not being
  172. * performed.
  173. * @param name The element type name.
  174. * @param atts The attributes attached to the element, if any.
  175. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  176. * wrapping another exception.
  177. * @see org.xml.sax.ContentHandler#startElement
  178. * @see org.xml.sax.ContentHandler#endElement
  179. * @see org.xml.sax.AttributeList
  180. *
  181. * @throws org.xml.sax.SAXException
  182. *
  183. * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes)
  184. */
  185. public void startElement(
  186. String arg0,
  187. String arg1,
  188. String arg2,
  189. Attributes arg3)
  190. throws SAXException
  191. {
  192. if (m_state != null) {
  193. m_state.resetState(getTransformer());
  194. }
  195. // fire off the start element event
  196. if (m_tracer != null)
  197. super.fireStartElem(arg2);
  198. }
  199. /**
  200. * Sets the LexicalHandler.
  201. * @param _lexHandler The LexicalHandler to set
  202. */
  203. public void setLexHandler(LexicalHandler _lexHandler)
  204. {
  205. this.m_lexHandler = _lexHandler;
  206. }
  207. /**
  208. * Sets the SAX ContentHandler.
  209. * @param m_saxHandler The ContentHandler to set
  210. */
  211. public void setContentHandler(ContentHandler _saxHandler)
  212. {
  213. this.m_saxHandler = _saxHandler;
  214. if (m_lexHandler == null && _saxHandler instanceof LexicalHandler)
  215. {
  216. // we are not overwriting an existing LexicalHandler, and _saxHandler
  217. // is also implements LexicalHandler, so lets use it
  218. m_lexHandler = (LexicalHandler) _saxHandler;
  219. }
  220. }
  221. /**
  222. * Does nothing. The setting of CDATA section elements has an impact on
  223. * stream serializers.
  224. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setCdataSectionElements(java.util.Vector)
  225. */
  226. public void setCdataSectionElements(Vector URI_and_localNames)
  227. {
  228. // do nothing
  229. }
  230. /** Set whether or not namespace declarations (e.g.
  231. * xmlns:foo) should appear as attributes of
  232. * elements
  233. * @param doOutputNSAttr whether or not namespace declarations
  234. * should appear as attributes
  235. */
  236. public void setShouldOutputNSAttr(boolean doOutputNSAttr)
  237. {
  238. m_shouldGenerateNSAttribute = doOutputNSAttr;
  239. }
  240. /**
  241. * Returns true if namespace declarations from calls such as
  242. * startPrefixMapping("prefix1","uri1") should
  243. * also be mirrored with self generated additional attributes of elements
  244. * that declare the namespace, for example the attribute xmlns:prefix1="uri1"
  245. */
  246. boolean getShouldOutputNSAttr()
  247. {
  248. return m_shouldGenerateNSAttribute;
  249. }
  250. /**
  251. * This method flushes any pending events, which can be startDocument()
  252. * closing the opening tag of an element, or closing an open CDATA section.
  253. */
  254. public void flushPending() throws SAXException
  255. {
  256. if (m_needToCallStartDocument)
  257. {
  258. startDocumentInternal();
  259. m_needToCallStartDocument = false;
  260. }
  261. if (m_elemContext.m_startTagOpen)
  262. {
  263. closeStartTag();
  264. m_elemContext.m_startTagOpen = false;
  265. }
  266. if (m_cdataTagOpen)
  267. {
  268. closeCDATA();
  269. m_cdataTagOpen = false;
  270. }
  271. }
  272. /**
  273. * Pass in a reference to a TransformState object, which
  274. * can be used during SAX ContentHandler events to obtain
  275. * information about he state of the transformation. This
  276. * method will be called before each startDocument event.
  277. *
  278. * @param ts A reference to a TransformState object
  279. */
  280. public void setTransformState(TransformStateSetter ts) {
  281. this.m_state = ts;
  282. }
  283. /**
  284. * Receives notification that an element starts, but attributes are not
  285. * fully known yet.
  286. *
  287. * @param uri the URI of the namespace of the element (optional)
  288. * @param localName the element name, but without prefix (optional)
  289. * @param qName the element name, with prefix, if any (required)
  290. *
  291. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#startElement(String, String, String)
  292. */
  293. public void startElement(String uri, String localName, String qName)
  294. throws SAXException {
  295. if (m_state != null) {
  296. m_state.resetState(getTransformer());
  297. }
  298. // fire off the start element event
  299. if (m_tracer != null)
  300. super.fireStartElem(qName);
  301. }
  302. /**
  303. * An element starts, but attributes are not fully known yet.
  304. *
  305. * @param elementName the element name, with prefix (if any).
  306. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#startElement(String)
  307. */
  308. public void startElement(String qName) throws SAXException {
  309. if (m_state != null) {
  310. m_state.resetState(getTransformer());
  311. }
  312. // fire off the start element event
  313. if (m_tracer != null)
  314. super.fireStartElem(qName);
  315. }
  316. /**
  317. * This method gets the node's value as a String and uses that String as if
  318. * it were an input character notification.
  319. * @param node the Node to serialize
  320. * @throws org.xml.sax.SAXException
  321. */
  322. public void characters(org.w3c.dom.Node node)
  323. throws org.xml.sax.SAXException
  324. {
  325. // remember the current node
  326. if (m_state != null)
  327. {
  328. m_state.setCurrentNode(node);
  329. }
  330. // Get the node's value as a String and use that String as if
  331. // it were an input character notification.
  332. String data = node.getNodeValue();
  333. if (data != null) {
  334. this.characters(data);
  335. }
  336. }
  337. /**
  338. * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
  339. */
  340. public void fatalError(SAXParseException exc) throws SAXException {
  341. super.fatalError(exc);
  342. m_needToCallStartDocument = false;
  343. if (m_saxHandler instanceof ErrorHandler) {
  344. ((ErrorHandler)m_saxHandler).fatalError(exc);
  345. }
  346. }
  347. /**
  348. * @see org.xml.sax.ErrorHandler#error(SAXParseException)
  349. */
  350. public void error(SAXParseException exc) throws SAXException {
  351. super.error(exc);
  352. if (m_saxHandler instanceof ErrorHandler)
  353. ((ErrorHandler)m_saxHandler).error(exc);
  354. }
  355. /**
  356. * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
  357. */
  358. public void warning(SAXParseException exc) throws SAXException {
  359. super.warning(exc);
  360. if (m_saxHandler instanceof ErrorHandler)
  361. ((ErrorHandler)m_saxHandler).warning(exc);
  362. }
  363. /**
  364. * Try's to reset the super class and reset this class for
  365. * re-use, so that you don't need to create a new serializer
  366. * (mostly for performance reasons).
  367. *
  368. * @return true if the class was successfuly reset.
  369. * @see com.sun.org.apache.xml.internal.serializer.Serializer#reset()
  370. */
  371. public boolean reset()
  372. {
  373. boolean wasReset = false;
  374. if (super.reset())
  375. {
  376. resetToSAXHandler();
  377. wasReset = true;
  378. }
  379. return wasReset;
  380. }
  381. /**
  382. * Reset all of the fields owned by ToSAXHandler class
  383. *
  384. */
  385. private void resetToSAXHandler()
  386. {
  387. this.m_lexHandler = null;
  388. this.m_saxHandler = null;
  389. this.m_state = null;
  390. this.m_shouldGenerateNSAttribute = false;
  391. }
  392. /**
  393. * Add a unique attribute
  394. */
  395. public void addUniqueAttribute(String qName, String value, int flags)
  396. throws SAXException
  397. {
  398. addAttribute(qName, value);
  399. }
  400. }