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
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 * */
112 public IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
113 {
114 this.init( co, controllerCoroutineID, -1 );
115 }
116
117 //
118 // Factories
119 //
120 static public IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co, int controllerCoroutineID) {
121 return new IncrementalSAXSource_Filter(co, controllerCoroutineID);
122 }
123
124 //
125 // Public methods
126 //
127
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 * */
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
176 public void setContentHandler(ContentHandler handler)
177 {
178 clientContentHandler=handler;
179 }
180 // Register a DTD handler for us to output to
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
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
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.
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 //
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 //
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 }
369 public void endCDATA()
370 throws org.xml.sax.SAXException
371 {
372 if(null!=clientLexicalHandler)
373 clientLexicalHandler.endCDATA();
374 }
375 public void endDTD()
376 throws org.xml.sax.SAXException
377 {
378 if(null!=clientLexicalHandler)
379 clientLexicalHandler.endDTD();
380 }
381 public void endEntity(java.lang.String name)
382 throws org.xml.sax.SAXException
383 {
384 if(null!=clientLexicalHandler)
385 clientLexicalHandler.endEntity(name);
386 }
387 public void startCDATA()
388 throws org.xml.sax.SAXException
389 {
390 if(null!=clientLexicalHandler)
391 clientLexicalHandler.startCDATA();
392 }
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 }
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
410 public void notationDecl(String a, String b, String c) throws SAXException
411 {
412 if(null!=clientDTDHandler)
413 clientDTDHandler.notationDecl(a,b,c);
414 }
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 //
436 public void error(SAXParseException exception) throws SAXException
437 {
438 if(null!=clientErrorHandler)
439 clientErrorHandler.error(exception);
440 }
441
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
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
465 public int getSourceCoroutineID() {
466 return fSourceCoroutineID;
467 }
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 * */
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 * */
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 */
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 */
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 * */
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 */
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 * */
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