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