1. /*
  2. * Copyright 1999-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: IncrementalSAXSource_Filter.java,v 1.11 2004/02/16 23:06:11 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.dtm.ref;
  20. import java.io.IOException;
  21. import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  22. import com.sun.org.apache.xml.internal.res.XMLMessages;
  23. import com.sun.org.apache.xml.internal.utils.ThreadControllerWrapper;
  24. import org.xml.sax.Attributes;
  25. import org.xml.sax.ContentHandler;
  26. import org.xml.sax.DTDHandler;
  27. import org.xml.sax.ErrorHandler;
  28. import org.xml.sax.InputSource;
  29. import org.xml.sax.Locator;
  30. import org.xml.sax.SAXException;
  31. import org.xml.sax.SAXNotRecognizedException;
  32. import org.xml.sax.SAXNotSupportedException;
  33. import org.xml.sax.SAXParseException;
  34. import org.xml.sax.XMLReader;
  35. import org.xml.sax.ext.LexicalHandler;
  36. /** <p>IncrementalSAXSource_Filter implements IncrementalSAXSource, using a
  37. * standard SAX2 event source as its input and parcelling out those
  38. * events gradually in reponse to deliverMoreNodes() requests. Output from the
  39. * filter will be passed along to a SAX handler registered as our
  40. * listener, but those callbacks will pass through a counting stage
  41. * which periodically yields control back to the controller coroutine.
  42. * </p>
  43. *
  44. * <p>%REVIEW%: This filter is not currenly intended to be reusable
  45. * for parsing additional streams/documents. We may want to consider
  46. * making it resettable at some point in the future. But it's a
  47. * small object, so that'd be mostly a convenience issue; the cost
  48. * of allocating each time is trivial compared to the cost of processing
  49. * any nontrival stream.</p>
  50. *
  51. * <p>For a brief usage example, see the unit-test _main() method.</p>
  52. *
  53. * <p>This is a simplification of the old CoroutineSAXParser, focusing
  54. * specifically on filtering. The resulting controller protocol is _far_
  55. * simpler and less error-prone; the only controller operation is deliverMoreNodes(),
  56. * and the only requirement is that deliverMoreNodes(false) be called if you want to
  57. * discard the rest of the stream and the previous deliverMoreNodes() didn't return
  58. * false.
  59. * */
  60. public class IncrementalSAXSource_Filter
  61. implements IncrementalSAXSource, ContentHandler, DTDHandler, LexicalHandler, ErrorHandler, Runnable
  62. {
  63. boolean DEBUG=false; //Internal status report
  64. //
  65. // Data
  66. //
  67. private CoroutineManager fCoroutineManager = null;
  68. private int fControllerCoroutineID = -1;
  69. private int fSourceCoroutineID = -1;
  70. private ContentHandler clientContentHandler=null; // %REVIEW% support multiple?
  71. private LexicalHandler clientLexicalHandler=null; // %REVIEW% support multiple?
  72. private DTDHandler clientDTDHandler=null; // %REVIEW% support multiple?
  73. private ErrorHandler clientErrorHandler=null; // %REVIEW% support multiple?
  74. private int eventcounter;
  75. private int frequency=5;
  76. // Flag indicating that no more events should be delivered -- either
  77. // because input stream ran to completion (endDocument), or because
  78. // the user requested an early stop via deliverMoreNodes(false).
  79. private boolean fNoMoreEvents=false;
  80. // Support for startParse()
  81. private XMLReader fXMLReader=null;
  82. private InputSource fXMLReaderInputSource=null;
  83. //
  84. // Constructors
  85. //
  86. public IncrementalSAXSource_Filter() {
  87. this.init( new CoroutineManager(), -1, -1);
  88. }
  89. /** Create a IncrementalSAXSource_Filter which is not yet bound to a specific
  90. * SAX event source.
  91. * */
  92. public IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
  93. {
  94. this.init( co, controllerCoroutineID, -1 );
  95. }
  96. //
  97. // Factories
  98. //
  99. static public IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co, int controllerCoroutineID) {
  100. return new IncrementalSAXSource_Filter(co, controllerCoroutineID);
  101. }
  102. //
  103. // Public methods
  104. //
  105. public void init( CoroutineManager co, int controllerCoroutineID,
  106. int sourceCoroutineID)
  107. {
  108. if(co==null)
  109. co = new CoroutineManager();
  110. fCoroutineManager = co;
  111. fControllerCoroutineID = co.co_joinCoroutineSet(controllerCoroutineID);
  112. fSourceCoroutineID = co.co_joinCoroutineSet(sourceCoroutineID);
  113. if (fControllerCoroutineID == -1 || fSourceCoroutineID == -1)
  114. throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COJOINROUTINESET_FAILED, null)); //"co_joinCoroutineSet() failed");
  115. fNoMoreEvents=false;
  116. eventcounter=frequency;
  117. }
  118. /** Bind our input streams to an XMLReader.
  119. *
  120. * Just a convenience routine; obviously you can explicitly register
  121. * this as a listener with the same effect.
  122. * */
  123. public void setXMLReader(XMLReader eventsource)
  124. {
  125. fXMLReader=eventsource;
  126. eventsource.setContentHandler(this);
  127. eventsource.setDTDHandler(this);
  128. eventsource.setErrorHandler(this); // to report fatal errors in filtering mode
  129. // Not supported by all SAX2 filters:
  130. try
  131. {
  132. eventsource.
  133. setProperty("http://xml.org/sax/properties/lexical-handler",
  134. this);
  135. }
  136. catch(SAXNotRecognizedException e)
  137. {
  138. // Nothing we can do about it
  139. }
  140. catch(SAXNotSupportedException e)
  141. {
  142. // Nothing we can do about it
  143. }
  144. // Should we also bind as other varieties of handler?
  145. // (DTDHandler and so on)
  146. }
  147. // Register a content handler for us to output to
  148. public void setContentHandler(ContentHandler handler)
  149. {
  150. clientContentHandler=handler;
  151. }
  152. // Register a DTD handler for us to output to
  153. public void setDTDHandler(DTDHandler handler)
  154. {
  155. clientDTDHandler=handler;
  156. }
  157. // Register a lexical handler for us to output to
  158. // Not all filters support this...
  159. // ??? Should we register directly on the filter?
  160. // NOTE NAME -- subclassing issue in the Xerces version
  161. public void setLexicalHandler(LexicalHandler handler)
  162. {
  163. clientLexicalHandler=handler;
  164. }
  165. // Register an error handler for us to output to
  166. // NOTE NAME -- subclassing issue in the Xerces version
  167. public void setErrHandler(ErrorHandler handler)
  168. {
  169. clientErrorHandler=handler;
  170. }
  171. // Set the number of events between resumes of our coroutine
  172. // Immediately resets number of events before _next_ resume as well.
  173. public void setReturnFrequency(int events)
  174. {
  175. if(events<1) events=1;
  176. frequency=eventcounter=events;
  177. }
  178. //
  179. // ContentHandler methods
  180. // These pass the data to our client ContentHandler...
  181. // but they also count the number of events passing through,
  182. // and resume our coroutine each time that counter hits zero and
  183. // is reset.
  184. //
  185. // Note that for everything except endDocument and fatalError, we do the count-and-yield
  186. // BEFORE passing the call along. I'm hoping that this will encourage JIT
  187. // compilers to realize that these are tail-calls, reducing the expense of
  188. // the additional layer of data flow.
  189. //
  190. // %REVIEW% Glenn suggests that pausing after endElement, endDocument,
  191. // and characters may be sufficient. I actually may not want to
  192. // stop after characters, since in our application these wind up being
  193. // concatenated before they're processed... but that risks huge blocks of
  194. // text causing greater than usual readahead. (Unlikely? Consider the
  195. // possibility of a large base-64 block in a SOAP stream.)
  196. //
  197. public void characters(char[] ch, int start, int length)
  198. throws org.xml.sax.SAXException
  199. {
  200. if(--eventcounter<=0)
  201. {
  202. co_yield(true);
  203. eventcounter=frequency;
  204. }
  205. if(clientContentHandler!=null)
  206. clientContentHandler.characters(ch,start,length);
  207. }
  208. public void endDocument()
  209. throws org.xml.sax.SAXException
  210. {
  211. // EXCEPTION: In this case we need to run the event BEFORE we yield.
  212. if(clientContentHandler!=null)
  213. clientContentHandler.endDocument();
  214. eventcounter=0;
  215. co_yield(false);
  216. }
  217. public void endElement(java.lang.String namespaceURI, java.lang.String localName,
  218. java.lang.String qName)
  219. throws org.xml.sax.SAXException
  220. {
  221. if(--eventcounter<=0)
  222. {
  223. co_yield(true);
  224. eventcounter=frequency;
  225. }
  226. if(clientContentHandler!=null)
  227. clientContentHandler.endElement(namespaceURI,localName,qName);
  228. }
  229. public void endPrefixMapping(java.lang.String prefix)
  230. throws org.xml.sax.SAXException
  231. {
  232. if(--eventcounter<=0)
  233. {
  234. co_yield(true);
  235. eventcounter=frequency;
  236. }
  237. if(clientContentHandler!=null)
  238. clientContentHandler.endPrefixMapping(prefix);
  239. }
  240. public void ignorableWhitespace(char[] ch, int start, int length)
  241. throws org.xml.sax.SAXException
  242. {
  243. if(--eventcounter<=0)
  244. {
  245. co_yield(true);
  246. eventcounter=frequency;
  247. }
  248. if(clientContentHandler!=null)
  249. clientContentHandler.ignorableWhitespace(ch,start,length);
  250. }
  251. public void processingInstruction(java.lang.String target, java.lang.String data)
  252. throws org.xml.sax.SAXException
  253. {
  254. if(--eventcounter<=0)
  255. {
  256. co_yield(true);
  257. eventcounter=frequency;
  258. }
  259. if(clientContentHandler!=null)
  260. clientContentHandler.processingInstruction(target,data);
  261. }
  262. public void setDocumentLocator(Locator locator)
  263. {
  264. if(--eventcounter<=0)
  265. {
  266. // This can cause a hang. -sb
  267. // co_yield(true);
  268. eventcounter=frequency;
  269. }
  270. if(clientContentHandler!=null)
  271. clientContentHandler.setDocumentLocator(locator);
  272. }
  273. public void skippedEntity(java.lang.String name)
  274. throws org.xml.sax.SAXException
  275. {
  276. if(--eventcounter<=0)
  277. {
  278. co_yield(true);
  279. eventcounter=frequency;
  280. }
  281. if(clientContentHandler!=null)
  282. clientContentHandler.skippedEntity(name);
  283. }
  284. public void startDocument()
  285. throws org.xml.sax.SAXException
  286. {
  287. co_entry_pause();
  288. // Otherwise, begin normal event delivery
  289. if(--eventcounter<=0)
  290. {
  291. co_yield(true);
  292. eventcounter=frequency;
  293. }
  294. if(clientContentHandler!=null)
  295. clientContentHandler.startDocument();
  296. }
  297. public void startElement(java.lang.String namespaceURI, java.lang.String localName,
  298. java.lang.String qName, Attributes atts)
  299. throws org.xml.sax.SAXException
  300. {
  301. if(--eventcounter<=0)
  302. {
  303. co_yield(true);
  304. eventcounter=frequency;
  305. }
  306. if(clientContentHandler!=null)
  307. clientContentHandler.startElement(namespaceURI, localName, qName, atts);
  308. }
  309. public void startPrefixMapping(java.lang.String prefix, java.lang.String uri)
  310. throws org.xml.sax.SAXException
  311. {
  312. if(--eventcounter<=0)
  313. {
  314. co_yield(true);
  315. eventcounter=frequency;
  316. }
  317. if(clientContentHandler!=null)
  318. clientContentHandler.startPrefixMapping(prefix,uri);
  319. }
  320. //
  321. // LexicalHandler support. Not all SAX2 filters support these events
  322. // but we may want to pass them through when they exist...
  323. //
  324. // %REVIEW% These do NOT currently affect the eventcounter; I'm asserting
  325. // that they're rare enough that it makes little or no sense to
  326. // pause after them. As such, it may make more sense for folks who
  327. // actually want to use them to register directly with the filter.
  328. // But I want 'em here for now, to remind us to recheck this assertion!
  329. //
  330. public void comment(char[] ch, int start, int length)
  331. throws org.xml.sax.SAXException
  332. {
  333. if(null!=clientLexicalHandler)
  334. clientLexicalHandler.comment(ch,start,length);
  335. }
  336. public void endCDATA()
  337. throws org.xml.sax.SAXException
  338. {
  339. if(null!=clientLexicalHandler)
  340. clientLexicalHandler.endCDATA();
  341. }
  342. public void endDTD()
  343. throws org.xml.sax.SAXException
  344. {
  345. if(null!=clientLexicalHandler)
  346. clientLexicalHandler.endDTD();
  347. }
  348. public void endEntity(java.lang.String name)
  349. throws org.xml.sax.SAXException
  350. {
  351. if(null!=clientLexicalHandler)
  352. clientLexicalHandler.endEntity(name);
  353. }
  354. public void startCDATA()
  355. throws org.xml.sax.SAXException
  356. {
  357. if(null!=clientLexicalHandler)
  358. clientLexicalHandler.startCDATA();
  359. }
  360. public void startDTD(java.lang.String name, java.lang.String publicId,
  361. java.lang.String systemId)
  362. throws org.xml.sax.SAXException
  363. {
  364. if(null!=clientLexicalHandler)
  365. clientLexicalHandler. startDTD(name, publicId, systemId);
  366. }
  367. public void startEntity(java.lang.String name)
  368. throws org.xml.sax.SAXException
  369. {
  370. if(null!=clientLexicalHandler)
  371. clientLexicalHandler.startEntity(name);
  372. }
  373. //
  374. // DTDHandler support.
  375. public void notationDecl(String a, String b, String c) throws SAXException
  376. {
  377. if(null!=clientDTDHandler)
  378. clientDTDHandler.notationDecl(a,b,c);
  379. }
  380. public void unparsedEntityDecl(String a, String b, String c, String d) throws SAXException
  381. {
  382. if(null!=clientDTDHandler)
  383. clientDTDHandler.unparsedEntityDecl(a,b,c,d);
  384. }
  385. //
  386. // ErrorHandler support.
  387. //
  388. // PROBLEM: Xerces is apparently _not_ calling the ErrorHandler for
  389. // exceptions thrown by the ContentHandler, which prevents us from
  390. // handling this properly when running in filtering mode with Xerces
  391. // as our event source. It's unclear whether this is a Xerces bug
  392. // or a SAX design flaw.
  393. //
  394. // %REVIEW% Current solution: In filtering mode, it is REQUIRED that
  395. // event source make sure this method is invoked if the event stream
  396. // abends before endDocument is delivered. If that means explicitly calling
  397. // us in the exception handling code because it won't be delivered as part
  398. // of the normal SAX ErrorHandler stream, that's fine; Not Our Problem.
  399. //
  400. public void error(SAXParseException exception) throws SAXException
  401. {
  402. if(null!=clientErrorHandler)
  403. clientErrorHandler.error(exception);
  404. }
  405. public void fatalError(SAXParseException exception) throws SAXException
  406. {
  407. // EXCEPTION: In this case we need to run the event BEFORE we yield --
  408. // just as with endDocument, this terminates the event stream.
  409. if(null!=clientErrorHandler)
  410. clientErrorHandler.error(exception);
  411. eventcounter=0;
  412. co_yield(false);
  413. }
  414. public void warning(SAXParseException exception) throws SAXException
  415. {
  416. if(null!=clientErrorHandler)
  417. clientErrorHandler.error(exception);
  418. }
  419. //
  420. // coroutine support
  421. //
  422. public int getSourceCoroutineID() {
  423. return fSourceCoroutineID;
  424. }
  425. public int getControllerCoroutineID() {
  426. return fControllerCoroutineID;
  427. }
  428. /** @return the CoroutineManager this CoroutineFilter object is bound to.
  429. * If you're using the do...() methods, applications should only
  430. * need to talk to the CoroutineManager once, to obtain the
  431. * application's Coroutine ID.
  432. * */
  433. public CoroutineManager getCoroutineManager()
  434. {
  435. return fCoroutineManager;
  436. }
  437. /** <p>In the SAX delegation code, I've inlined the count-down in
  438. * the hope of encouraging compilers to deliver better
  439. * performance. However, if we subclass (eg to directly connect the
  440. * output to a DTM builder), that would require calling super in
  441. * order to run that logic... which seems inelegant. Hence this
  442. * routine for the convenience of subclasses: every [frequency]
  443. * invocations, issue a co_yield.</p>
  444. *
  445. * @param moreExepected Should always be true unless this is being called
  446. * at the end of endDocument() handling.
  447. * */
  448. protected void count_and_yield(boolean moreExpected) throws SAXException
  449. {
  450. if(!moreExpected) eventcounter=0;
  451. if(--eventcounter<=0)
  452. {
  453. co_yield(true);
  454. eventcounter=frequency;
  455. }
  456. }
  457. /**
  458. * co_entry_pause is called in startDocument() before anything else
  459. * happens. It causes the filter to wait for a "go ahead" request
  460. * from the controller before delivering any events. Note that
  461. * the very first thing the controller tells us may be "I don't
  462. * need events after all"!
  463. */
  464. private void co_entry_pause() throws SAXException
  465. {
  466. if(fCoroutineManager==null)
  467. {
  468. // Nobody called init()? Do it now...
  469. init(null,-1,-1);
  470. }
  471. try
  472. {
  473. Object arg=fCoroutineManager.co_entry_pause(fSourceCoroutineID);
  474. if(arg==Boolean.FALSE)
  475. co_yield(false);
  476. }
  477. catch(NoSuchMethodException e)
  478. {
  479. // Coroutine system says we haven't registered. That's an
  480. // application coding error, and is unrecoverable.
  481. if(DEBUG) e.printStackTrace();
  482. throw new SAXException(e);
  483. }
  484. }
  485. /**
  486. * Co_Yield handles coroutine interactions while a parse is in progress.
  487. *
  488. * When moreRemains==true, we are pausing after delivering events, to
  489. * ask if more are needed. We will resume the controller thread with
  490. * co_resume(Boolean.TRUE, ...)
  491. * When control is passed back it may indicate
  492. * Boolean.TRUE indication to continue delivering events
  493. * Boolean.FALSE indication to discontinue events and shut down.
  494. *
  495. * When moreRemains==false, we shut down immediately without asking the
  496. * controller's permission. Normally this means end of document has been
  497. * reached.
  498. *
  499. * Shutting down a IncrementalSAXSource_Filter requires terminating the incoming
  500. * SAX event stream. If we are in control of that stream (if it came
  501. * from an XMLReader passed to our startReader() method), we can do so
  502. * very quickly by throwing a reserved exception to it. If the stream is
  503. * coming from another source, we can't do that because its caller may
  504. * not be prepared for this "normal abnormal exit", and instead we put
  505. * ourselves in a "spin" mode where events are discarded.
  506. */
  507. private void co_yield(boolean moreRemains) throws SAXException
  508. {
  509. // Horrendous kluge to run filter to completion. See below.
  510. if(fNoMoreEvents)
  511. return;
  512. try // Coroutine manager might throw no-such.
  513. {
  514. Object arg=Boolean.FALSE;
  515. if(moreRemains)
  516. {
  517. // Yield control, resume parsing when done
  518. arg = fCoroutineManager.co_resume(Boolean.TRUE, fSourceCoroutineID,
  519. fControllerCoroutineID);
  520. }
  521. // If we're at end of document or were told to stop early
  522. if(arg==Boolean.FALSE)
  523. {
  524. fNoMoreEvents=true;
  525. if(fXMLReader!=null) // Running under startParseThread()
  526. throw new StopException(); // We'll co_exit from there.
  527. // Yield control. We do NOT expect anyone to ever ask us again.
  528. fCoroutineManager.co_exit_to(Boolean.FALSE, fSourceCoroutineID,
  529. fControllerCoroutineID);
  530. }
  531. }
  532. catch(NoSuchMethodException e)
  533. {
  534. // Shouldn't happen unless we've miscoded our coroutine logic
  535. // "Shut down the garbage smashers on the detention level!"
  536. fNoMoreEvents=true;
  537. fCoroutineManager.co_exit(fSourceCoroutineID);
  538. throw new SAXException(e);
  539. }
  540. }
  541. //
  542. // Convenience: Run an XMLReader in a thread
  543. //
  544. /** Launch a thread that will run an XMLReader's parse() operation within
  545. * a thread, feeding events to this IncrementalSAXSource_Filter. Mostly a convenience
  546. * routine, but has the advantage that -- since we invoked parse() --
  547. * we can halt parsing quickly via a StopException rather than waiting
  548. * for the SAX stream to end by itself.
  549. *
  550. * @throws SAXException is parse thread is already in progress
  551. * or parsing can not be started.
  552. * */
  553. public void startParse(InputSource source) throws SAXException
  554. {
  555. if(fNoMoreEvents)
  556. throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_INCRSAXSRCFILTER_NOT_RESTARTABLE, null)); //"IncrmentalSAXSource_Filter not currently restartable.");
  557. if(fXMLReader==null)
  558. throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_XMLRDR_NOT_BEFORE_STARTPARSE, null)); //"XMLReader not before startParse request");
  559. fXMLReaderInputSource=source;
  560. // Xalan thread pooling...
  561. // com.sun.org.apache.xalan.internal.transformer.TransformerImpl.runTransformThread(this);
  562. ThreadControllerWrapper.runThread(this, -1);
  563. }
  564. /* Thread logic to support startParseThread()
  565. */
  566. public void run()
  567. {
  568. // Guard against direct invocation of start().
  569. if(fXMLReader==null) return;
  570. if(DEBUG)System.out.println("IncrementalSAXSource_Filter parse thread launched");
  571. // Initially assume we'll run successfully.
  572. Object arg=Boolean.FALSE;
  573. // For the duration of this operation, all coroutine handshaking
  574. // will occur in the co_yield method. That's the nice thing about
  575. // coroutines; they give us a way to hand off control from the
  576. // middle of a synchronous method.
  577. try
  578. {
  579. fXMLReader.parse(fXMLReaderInputSource);
  580. }
  581. catch(IOException ex)
  582. {
  583. arg=ex;
  584. }
  585. catch(StopException ex)
  586. {
  587. // Expected and harmless
  588. if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
  589. }
  590. catch (SAXException ex)
  591. {
  592. Exception inner=ex.getException();
  593. if(inner instanceof StopException){
  594. // Expected and harmless
  595. if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
  596. }
  597. else
  598. {
  599. // Unexpected malfunction
  600. if(DEBUG)
  601. {
  602. System.out.println("Active IncrementalSAXSource_Filter UNEXPECTED SAX exception: "+inner);
  603. inner.printStackTrace();
  604. }
  605. arg=ex;
  606. }
  607. } // end parse
  608. // Mark as no longer running in thread.
  609. fXMLReader=null;
  610. try
  611. {
  612. // Mark as done and yield control to the controller coroutine
  613. fNoMoreEvents=true;
  614. fCoroutineManager.co_exit_to(arg, fSourceCoroutineID,
  615. fControllerCoroutineID);
  616. }
  617. catch(java.lang.NoSuchMethodException e)
  618. {
  619. // Shouldn't happen unless we've miscoded our coroutine logic
  620. // "CPO, shut down the garbage smashers on the detention level!"
  621. e.printStackTrace(System.err);
  622. fCoroutineManager.co_exit(fSourceCoroutineID);
  623. }
  624. }
  625. /** Used to quickly terminate parse when running under a
  626. startParse() thread. Only its type is important. */
  627. class StopException extends RuntimeException
  628. {
  629. }
  630. /** deliverMoreNodes() is a simple API which tells the coroutine
  631. * parser that we need more nodes. This is intended to be called
  632. * from one of our partner routines, and serves to encapsulate the
  633. * details of how incremental parsing has been achieved.
  634. *
  635. * @param parsemore If true, tells the incremental filter to generate
  636. * another chunk of output. If false, tells the filter that we're
  637. * satisfied and it can terminate parsing of this document.
  638. *
  639. * @return Boolean.TRUE if there may be more events available by invoking
  640. * deliverMoreNodes() again. Boolean.FALSE if parsing has run to completion (or been
  641. * terminated by deliverMoreNodes(false). Or an exception object if something
  642. * malfunctioned. %REVIEW% We _could_ actually throw the exception, but
  643. * that would require runinng deliverMoreNodes() in a try/catch... and for many
  644. * applications, exception will be simply be treated as "not TRUE" in
  645. * any case.
  646. * */
  647. public Object deliverMoreNodes(boolean parsemore)
  648. {
  649. // If parsing is already done, we can immediately say so
  650. if(fNoMoreEvents)
  651. return Boolean.FALSE;
  652. try
  653. {
  654. Object result =
  655. fCoroutineManager.co_resume(parsemore?Boolean.TRUE:Boolean.FALSE,
  656. fControllerCoroutineID, fSourceCoroutineID);
  657. if(result==Boolean.FALSE)
  658. fCoroutineManager.co_exit(fControllerCoroutineID);
  659. return result;
  660. }
  661. // SHOULD NEVER OCCUR, since the coroutine number and coroutine manager
  662. // are those previously established for this IncrementalSAXSource_Filter...
  663. // So I'm just going to return it as a parsing exception, for now.
  664. catch(NoSuchMethodException e)
  665. {
  666. return e;
  667. }
  668. }
  669. //================================================================
  670. /** Simple unit test. Attempt coroutine parsing of document indicated
  671. * by first argument (as a URI), report progress.
  672. */
  673. /*
  674. public static void _main(String args[])
  675. {
  676. System.out.println("Starting...");
  677. org.xml.sax.XMLReader theSAXParser=
  678. new com.sun.org.apache.xerces.internal.parsers.SAXParser();
  679. for(int arg=0;arg<args.length;++arg)
  680. {
  681. // The filter is not currently designed to be restartable
  682. // after a parse has ended. Generate a new one each time.
  683. IncrementalSAXSource_Filter filter=
  684. new IncrementalSAXSource_Filter();
  685. // Use a serializer as our sample output
  686. com.sun.org.apache.xml.internal.serialize.XMLSerializer trace;
  687. trace=new com.sun.org.apache.xml.internal.serialize.XMLSerializer(System.out,null);
  688. filter.setContentHandler(trace);
  689. filter.setLexicalHandler(trace);
  690. try
  691. {
  692. InputSource source = new InputSource(args[arg]);
  693. Object result=null;
  694. boolean more=true;
  695. // init not issued; we _should_ automagically Do The Right Thing
  696. // Bind parser, kick off parsing in a thread
  697. filter.setXMLReader(theSAXParser);
  698. filter.startParse(source);
  699. for(result = filter.deliverMoreNodes(more);
  700. (result instanceof Boolean && ((Boolean)result)==Boolean.TRUE);
  701. result = filter.deliverMoreNodes(more))
  702. {
  703. System.out.println("\nSome parsing successful, trying more.\n");
  704. // Special test: Terminate parsing early.
  705. if(arg+1<args.length && "!".equals(args[arg+1]))
  706. {
  707. ++arg;
  708. more=false;
  709. }
  710. }
  711. if (result instanceof Boolean && ((Boolean)result)==Boolean.FALSE)
  712. {
  713. System.out.println("\nFilter ended (EOF or on request).\n");
  714. }
  715. else if (result == null) {
  716. System.out.println("\nUNEXPECTED: Filter says shut down prematurely.\n");
  717. }
  718. else if (result instanceof Exception) {
  719. System.out.println("\nFilter threw exception:");
  720. ((Exception)result).printStackTrace();
  721. }
  722. }
  723. catch(SAXException e)
  724. {
  725. e.printStackTrace();
  726. }
  727. } // end for
  728. }
  729. */
  730. } // class IncrementalSAXSource_Filter