TeeConsumer.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /* TeeConsumer.java --
  2. Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.xml.pipeline;
  32. import org.xml.sax.Attributes;
  33. import org.xml.sax.ContentHandler;
  34. import org.xml.sax.DTDHandler;
  35. import org.xml.sax.ErrorHandler;
  36. import org.xml.sax.Locator;
  37. import org.xml.sax.SAXException;
  38. import org.xml.sax.SAXNotRecognizedException;
  39. import org.xml.sax.ext.DeclHandler;
  40. import org.xml.sax.ext.LexicalHandler;
  41. /**
  42. * Fans its events out to two other consumers, a "tee" filter stage in an
  43. * event pipeline. Networks can be assembled with multiple output points.
  44. *
  45. * <p> Error handling should be simple if you remember that exceptions
  46. * you throw will cancel later stages in that callback's pipeline, and
  47. * generally the producer will stop if it sees such an exception. You
  48. * may want to protect your pipeline against such backflows, making a
  49. * kind of reverse filter (or valve?) so that certain exceptions thrown by
  50. * your pipeline will caught and handled before the producer sees them.
  51. * Just use a "try/catch" block, rememebering that really important
  52. * cleanup tasks should be in "finally" clauses.
  53. *
  54. * <p> That issue isn't unique to "tee" consumers, but tee consumers have
  55. * the additional twist that exceptions thrown by the first consumer
  56. * will cause the second consumer not to see the callback (except for
  57. * the endDocument callback, which signals state cleanup).
  58. *
  59. * @author David Brownell
  60. */
  61. final public class TeeConsumer
  62. implements EventConsumer,
  63. ContentHandler, DTDHandler,
  64. LexicalHandler,DeclHandler
  65. {
  66. private EventConsumer first, rest;
  67. // cached to minimize time overhead
  68. private ContentHandler docFirst, docRest;
  69. private DeclHandler declFirst, declRest;
  70. private LexicalHandler lexFirst, lexRest;
  71. /**
  72. * Constructs a consumer which sends all its events to the first
  73. * consumer, and then the second one. If the first consumer throws
  74. * an exception, the second one will not see the event which
  75. * caused that exception to be reported.
  76. *
  77. * @param car The first consumer to get the events
  78. * @param cdr The second consumer to get the events
  79. */
  80. public TeeConsumer (EventConsumer car, EventConsumer cdr)
  81. {
  82. if (car == null || cdr == null)
  83. throw new NullPointerException ();
  84. first = car;
  85. rest = cdr;
  86. //
  87. // Cache the handlers.
  88. //
  89. docFirst = first.getContentHandler ();
  90. docRest = rest.getContentHandler ();
  91. // DTD handler isn't cached (rarely needed)
  92. try {
  93. declFirst = null;
  94. declFirst = (DeclHandler) first.getProperty (
  95. EventFilter.DECL_HANDLER);
  96. } catch (SAXException e) {}
  97. try {
  98. declRest = null;
  99. declRest = (DeclHandler) rest.getProperty (
  100. EventFilter.DECL_HANDLER);
  101. } catch (SAXException e) {}
  102. try {
  103. lexFirst = null;
  104. lexFirst = (LexicalHandler) first.getProperty (
  105. EventFilter.LEXICAL_HANDLER);
  106. } catch (SAXException e) {}
  107. try {
  108. lexRest = null;
  109. lexRest = (LexicalHandler) rest.getProperty (
  110. EventFilter.LEXICAL_HANDLER);
  111. } catch (SAXException e) {}
  112. }
  113. /* FIXME
  114. /**
  115. * Constructs a pipeline, and is otherwise a shorthand for the
  116. * two-consumer constructor for this class.
  117. *
  118. * @param first Description of the first pipeline to get events,
  119. * which will be passed to {@link PipelineFactory#createPipeline}
  120. * @param rest The second pipeline to get the events
  121. * /
  122. // constructor used by PipelineFactory
  123. public TeeConsumer (String first, EventConsumer rest)
  124. throws IOException
  125. {
  126. this (PipelineFactory.createPipeline (first), rest);
  127. }
  128. */
  129. /** Returns the first pipeline to get event calls. */
  130. public EventConsumer getFirst ()
  131. { return first; }
  132. /** Returns the second pipeline to get event calls. */
  133. public EventConsumer getRest ()
  134. { return rest; }
  135. /** Returns the content handler being used. */
  136. final public ContentHandler getContentHandler ()
  137. {
  138. if (docRest == null)
  139. return docFirst;
  140. if (docFirst == null)
  141. return docRest;
  142. return this;
  143. }
  144. /** Returns the dtd handler being used. */
  145. final public DTDHandler getDTDHandler ()
  146. {
  147. // not cached (hardly used)
  148. if (rest.getDTDHandler () == null)
  149. return first.getDTDHandler ();
  150. if (first.getDTDHandler () == null)
  151. return rest.getDTDHandler ();
  152. return this;
  153. }
  154. /** Returns the declaration or lexical handler being used. */
  155. final public Object getProperty (String id)
  156. throws SAXNotRecognizedException
  157. {
  158. //
  159. // in degenerate cases, we have no work to do.
  160. //
  161. Object firstProp = null, restProp = null;
  162. try { firstProp = first.getProperty (id); }
  163. catch (SAXNotRecognizedException e) { /* ignore */ }
  164. try { restProp = rest.getProperty (id); }
  165. catch (SAXNotRecognizedException e) { /* ignore */ }
  166. if (restProp == null)
  167. return firstProp;
  168. if (firstProp == null)
  169. return restProp;
  170. //
  171. // we've got work to do; handle two builtin cases.
  172. //
  173. if (EventFilter.DECL_HANDLER.equals (id))
  174. return this;
  175. if (EventFilter.LEXICAL_HANDLER.equals (id))
  176. return this;
  177. //
  178. // non-degenerate, handled by both consumers, but we don't know
  179. // how to handle this.
  180. //
  181. throw new SAXNotRecognizedException ("can't tee: " + id);
  182. }
  183. /**
  184. * Provides the error handler to both subsequent nodes of
  185. * this filter stage.
  186. */
  187. public void setErrorHandler (ErrorHandler handler)
  188. {
  189. first.setErrorHandler (handler);
  190. rest.setErrorHandler (handler);
  191. }
  192. //
  193. // ContentHandler
  194. //
  195. public void setDocumentLocator (Locator locator)
  196. {
  197. // this call is not made by all parsers
  198. docFirst.setDocumentLocator (locator);
  199. docRest.setDocumentLocator (locator);
  200. }
  201. public void startDocument ()
  202. throws SAXException
  203. {
  204. docFirst.startDocument ();
  205. docRest.startDocument ();
  206. }
  207. public void endDocument ()
  208. throws SAXException
  209. {
  210. try {
  211. docFirst.endDocument ();
  212. } finally {
  213. docRest.endDocument ();
  214. }
  215. }
  216. public void startPrefixMapping (String prefix, String uri)
  217. throws SAXException
  218. {
  219. docFirst.startPrefixMapping (prefix, uri);
  220. docRest.startPrefixMapping (prefix, uri);
  221. }
  222. public void endPrefixMapping (String prefix)
  223. throws SAXException
  224. {
  225. docFirst.endPrefixMapping (prefix);
  226. docRest.endPrefixMapping (prefix);
  227. }
  228. public void skippedEntity (String name)
  229. throws SAXException
  230. {
  231. docFirst.skippedEntity (name);
  232. docRest.skippedEntity (name);
  233. }
  234. public void startElement (String uri, String localName,
  235. String qName, Attributes atts)
  236. throws SAXException
  237. {
  238. docFirst.startElement (uri, localName, qName, atts);
  239. docRest.startElement (uri, localName, qName, atts);
  240. }
  241. public void endElement (String uri, String localName, String qName)
  242. throws SAXException
  243. {
  244. docFirst.endElement (uri, localName, qName);
  245. docRest.endElement (uri, localName, qName);
  246. }
  247. public void processingInstruction (String target, String data)
  248. throws SAXException
  249. {
  250. docFirst.processingInstruction (target, data);
  251. docRest.processingInstruction (target, data);
  252. }
  253. public void characters (char ch [], int start, int length)
  254. throws SAXException
  255. {
  256. docFirst.characters (ch, start, length);
  257. docRest.characters (ch, start, length);
  258. }
  259. public void ignorableWhitespace (char ch [], int start, int length)
  260. throws SAXException
  261. {
  262. docFirst.ignorableWhitespace (ch, start, length);
  263. docRest.ignorableWhitespace (ch, start, length);
  264. }
  265. //
  266. // DTDHandler
  267. //
  268. public void notationDecl (String name, String publicId, String systemId)
  269. throws SAXException
  270. {
  271. DTDHandler l1 = first.getDTDHandler ();
  272. DTDHandler l2 = rest.getDTDHandler ();
  273. l1.notationDecl (name, publicId, systemId);
  274. l2.notationDecl (name, publicId, systemId);
  275. }
  276. public void unparsedEntityDecl (String name,
  277. String publicId, String systemId,
  278. String notationName
  279. ) throws SAXException
  280. {
  281. DTDHandler l1 = first.getDTDHandler ();
  282. DTDHandler l2 = rest.getDTDHandler ();
  283. l1.unparsedEntityDecl (name, publicId, systemId, notationName);
  284. l2.unparsedEntityDecl (name, publicId, systemId, notationName);
  285. }
  286. //
  287. // DeclHandler
  288. //
  289. public void attributeDecl (String eName, String aName,
  290. String type,
  291. String mode, String value)
  292. throws SAXException
  293. {
  294. declFirst.attributeDecl (eName, aName, type, mode, value);
  295. declRest.attributeDecl (eName, aName, type, mode, value);
  296. }
  297. public void elementDecl (String name, String model)
  298. throws SAXException
  299. {
  300. declFirst.elementDecl (name, model);
  301. declRest.elementDecl (name, model);
  302. }
  303. public void externalEntityDecl (String name,
  304. String publicId, String systemId)
  305. throws SAXException
  306. {
  307. declFirst.externalEntityDecl (name, publicId, systemId);
  308. declRest.externalEntityDecl (name, publicId, systemId);
  309. }
  310. public void internalEntityDecl (String name, String value)
  311. throws SAXException
  312. {
  313. declFirst.internalEntityDecl (name, value);
  314. declRest.internalEntityDecl (name, value);
  315. }
  316. //
  317. // LexicalHandler
  318. //
  319. public void comment (char ch [], int start, int length)
  320. throws SAXException
  321. {
  322. lexFirst.comment (ch, start, length);
  323. lexRest.comment (ch, start, length);
  324. }
  325. public void startCDATA ()
  326. throws SAXException
  327. {
  328. lexFirst.startCDATA ();
  329. lexRest.startCDATA ();
  330. }
  331. public void endCDATA ()
  332. throws SAXException
  333. {
  334. lexFirst.endCDATA ();
  335. lexRest.endCDATA ();
  336. }
  337. public void startEntity (String name)
  338. throws SAXException
  339. {
  340. lexFirst.startEntity (name);
  341. lexRest.startEntity (name);
  342. }
  343. public void endEntity (String name)
  344. throws SAXException
  345. {
  346. lexFirst.endEntity (name);
  347. lexRest.endEntity (name);
  348. }
  349. public void startDTD (String name, String publicId, String systemId)
  350. throws SAXException
  351. {
  352. lexFirst.startDTD (name, publicId, systemId);
  353. lexRest.startDTD (name, publicId, systemId);
  354. }
  355. public void endDTD ()
  356. throws SAXException
  357. {
  358. lexFirst.endDTD ();
  359. lexRest.endDTD ();
  360. }
  361. }