KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xml > dtm > ref > IncrementalSAXSource_Filter


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
20 package org.apache.xml.dtm.ref;
21
22 import java.io.IOException JavaDoc;
23
24 import org.apache.xml.res.XMLErrorResources;
25 import org.apache.xml.res.XMLMessages;
26 import org.apache.xml.utils.ThreadControllerWrapper;
27
28 import org.xml.sax.Attributes JavaDoc;
29 import org.xml.sax.ContentHandler JavaDoc;
30 import org.xml.sax.DTDHandler JavaDoc;
31 import org.xml.sax.ErrorHandler JavaDoc;
32 import org.xml.sax.InputSource JavaDoc;
33 import org.xml.sax.Locator JavaDoc;
34 import org.xml.sax.SAXException JavaDoc;
35 import org.xml.sax.SAXNotRecognizedException JavaDoc;
36 import org.xml.sax.SAXNotSupportedException JavaDoc;
37 import org.xml.sax.SAXParseException JavaDoc;
38 import org.xml.sax.XMLReader JavaDoc;
39 import org.xml.sax.ext.LexicalHandler JavaDoc;
40
41 /** <p>IncrementalSAXSource_Filter implements IncrementalSAXSource, using a
42  * standard SAX2 event source as its input and parcelling out those
43  * events gradually in reponse to deliverMoreNodes() requests. Output from the
44  * filter will be passed along to a SAX handler registered as our
45  * listener, but those callbacks will pass through a counting stage
46  * which periodically yields control back to the controller coroutine.
47  * </p>
48  *
49  * <p>%REVIEW%: This filter is not currenly intended to be reusable
50  * for parsing additional streams/documents. We may want to consider
51  * making it resettable at some point in the future. But it's a
52  * small object, so that'd be mostly a convenience issue; the cost
53  * of allocating each time is trivial compared to the cost of processing
54  * any nontrival stream.</p>
55  *
56  * <p>For a brief usage example, see the unit-test main() method.</p>
57  *
58  * <p>This is a simplification of the old CoroutineSAXParser, focusing
59  * specifically on filtering. The resulting controller protocol is _far_
60  * simpler and less error-prone; the only controller operation is deliverMoreNodes(),
61  * and the only requirement is that deliverMoreNodes(false) be called if you want to
62  * discard the rest of the stream and the previous deliverMoreNodes() didn't return
63  * false.
64  * */

65 public class IncrementalSAXSource_Filter
66 implements IncrementalSAXSource, ContentHandler JavaDoc, DTDHandler JavaDoc, LexicalHandler JavaDoc, ErrorHandler JavaDoc, Runnable JavaDoc
67 {
68   boolean DEBUG=false; //Internal status report
69

70   //
71
// Data
72
//
73
private CoroutineManager fCoroutineManager = null;
74   private int fControllerCoroutineID = -1;
75   private int fSourceCoroutineID = -1;
76
77   private ContentHandler JavaDoc clientContentHandler=null; // %REVIEW% support multiple?
78
private LexicalHandler JavaDoc clientLexicalHandler=null; // %REVIEW% support multiple?
79
private DTDHandler JavaDoc clientDTDHandler=null; // %REVIEW% support multiple?
80
private ErrorHandler JavaDoc clientErrorHandler=null; // %REVIEW% support multiple?
81
private int eventcounter;
82   private int frequency=5;
83
84   // Flag indicating that no more events should be delivered -- either
85
// because input stream ran to completion (endDocument), or because
86
// the user requested an early stop via deliverMoreNodes(false).
87
private boolean fNoMoreEvents=false;
88
89   // Support for startParse()
90
private XMLReader JavaDoc fXMLReader=null;
91   private InputSource JavaDoc fXMLReaderInputSource=null;
92
93   //
94
// Constructors
95
//
96

97   public IncrementalSAXSource_Filter() {
98     this.init( new CoroutineManager(), -1, -1);
99   }
100   
101   /** Create a IncrementalSAXSource_Filter which is not yet bound to a specific
102    * SAX event source.
103    * */

104   public IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
105   {
106     this.init( co, controllerCoroutineID, -1 );
107   }
108
109   //
110
// Factories
111
//
112
static public IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co, int controllerCoroutineID) {
113     return new IncrementalSAXSource_Filter(co, controllerCoroutineID);
114   }
115
116   //
117
// Public methods
118
//
119

120   public void init( CoroutineManager co, int controllerCoroutineID,
121                     int sourceCoroutineID)
122   {
123     if(co==null)
124       co = new CoroutineManager();
125     fCoroutineManager = co;
126     fControllerCoroutineID = co.co_joinCoroutineSet(controllerCoroutineID);
127     fSourceCoroutineID = co.co_joinCoroutineSet(sourceCoroutineID);
128     if (fControllerCoroutineID == -1 || fSourceCoroutineID == -1)
129       throw new RuntimeException JavaDoc(XMLMessages.createXMLMessage(XMLErrorResources.ER_COJOINROUTINESET_FAILED, null)); //"co_joinCoroutineSet() failed");
130

131     fNoMoreEvents=false;
132     eventcounter=frequency;
133   }
134     
135   /** Bind our input streams to an XMLReader.
136    *
137    * Just a convenience routine; obviously you can explicitly register
138    * this as a listener with the same effect.
139    * */

140   public void setXMLReader(XMLReader JavaDoc eventsource)
141   {
142     fXMLReader=eventsource;
143     eventsource.setContentHandler(this);
144     eventsource.setDTDHandler(this);
145     eventsource.setErrorHandler(this); // to report fatal errors in filtering mode
146

147     // Not supported by all SAX2 filters:
148
try
149     {
150       eventsource.
151         setProperty("http://xml.org/sax/properties/lexical-handler",
152                     this);
153     }
154     catch(SAXNotRecognizedException JavaDoc e)
155     {
156       // Nothing we can do about it
157
}
158     catch(SAXNotSupportedException JavaDoc e)
159     {
160       // Nothing we can do about it
161
}
162
163     // Should we also bind as other varieties of handler?
164
// (DTDHandler and so on)
165
}
166
167   // Register a content handler for us to output to
168
public void setContentHandler(ContentHandler JavaDoc handler)
169   {
170     clientContentHandler=handler;
171   }
172   // Register a DTD handler for us to output to
173
public void setDTDHandler(DTDHandler JavaDoc handler)
174   {
175     clientDTDHandler=handler;
176   }
177   // Register a lexical handler for us to output to
178
// Not all filters support this...
179
// ??? Should we register directly on the filter?
180
// NOTE NAME -- subclassing issue in the Xerces version
181
public void setLexicalHandler(LexicalHandler JavaDoc handler)
182   {
183     clientLexicalHandler=handler;
184   }
185   // Register an error handler for us to output to
186
// NOTE NAME -- subclassing issue in the Xerces version
187
public void setErrHandler(ErrorHandler JavaDoc handler)
188   {
189     clientErrorHandler=handler;
190   }
191
192   // Set the number of events between resumes of our coroutine
193
// Immediately resets number of events before _next_ resume as well.
194
public void setReturnFrequency(int events)
195   {
196     if(events<1) events=1;
197     frequency=eventcounter=events;
198   }
199   
200   //
201
// ContentHandler methods
202
// These pass the data to our client ContentHandler...
203
// but they also count the number of events passing through,
204
// and resume our coroutine each time that counter hits zero and
205
// is reset.
206
//
207
// Note that for everything except endDocument and fatalError, we do the count-and-yield
208
// BEFORE passing the call along. I'm hoping that this will encourage JIT
209
// compilers to realize that these are tail-calls, reducing the expense of
210
// the additional layer of data flow.
211
//
212
// %REVIEW% Glenn suggests that pausing after endElement, endDocument,
213
// and characters may be sufficient. I actually may not want to
214
// stop after characters, since in our application these wind up being
215
// concatenated before they're processed... but that risks huge blocks of
216
// text causing greater than usual readahead. (Unlikely? Consider the
217
// possibility of a large base-64 block in a SOAP stream.)
218
//
219
public void characters(char[] ch, int start, int length)
220        throws org.xml.sax.SAXException JavaDoc
221   {
222     if(--eventcounter<=0)
223       {
224         co_yield(true);
225         eventcounter=frequency;
226       }
227     if(clientContentHandler!=null)
228       clientContentHandler.characters(ch,start,length);
229   }
230   public void endDocument()
231        throws org.xml.sax.SAXException JavaDoc
232   {
233     // EXCEPTION: In this case we need to run the event BEFORE we yield.
234
if(clientContentHandler!=null)
235       clientContentHandler.endDocument();
236
237     eventcounter=0;
238     co_yield(false);
239   }
240   public void endElement(java.lang.String JavaDoc namespaceURI, java.lang.String JavaDoc localName,
241       java.lang.String JavaDoc qName)
242        throws org.xml.sax.SAXException JavaDoc
243   {
244     if(--eventcounter<=0)
245       {
246         co_yield(true);
247         eventcounter=frequency;
248       }
249     if(clientContentHandler!=null)
250       clientContentHandler.endElement(namespaceURI,localName,qName);
251   }
252   public void endPrefixMapping(java.lang.String JavaDoc prefix)
253        throws org.xml.sax.SAXException JavaDoc
254   {
255     if(--eventcounter<=0)
256       {
257         co_yield(true);
258         eventcounter=frequency;
259       }
260     if(clientContentHandler!=null)
261       clientContentHandler.endPrefixMapping(prefix);
262   }
263   public void ignorableWhitespace(char[] ch, int start, int length)
264        throws org.xml.sax.SAXException JavaDoc
265   {
266     if(--eventcounter<=0)
267       {
268         co_yield(true);
269         eventcounter=frequency;
270       }
271     if(clientContentHandler!=null)
272       clientContentHandler.ignorableWhitespace(ch,start,length);
273   }
274   public void processingInstruction(java.lang.String JavaDoc target, java.lang.String JavaDoc data)
275        throws org.xml.sax.SAXException JavaDoc
276   {
277     if(--eventcounter<=0)
278       {
279         co_yield(true);
280         eventcounter=frequency;
281       }
282     if(clientContentHandler!=null)
283       clientContentHandler.processingInstruction(target,data);
284   }
285   public void setDocumentLocator(Locator JavaDoc locator)
286   {
287     if(--eventcounter<=0)
288       {
289         // This can cause a hang. -sb
290
// co_yield(true);
291
eventcounter=frequency;
292       }
293     if(clientContentHandler!=null)
294       clientContentHandler.setDocumentLocator(locator);
295   }
296   public void skippedEntity(java.lang.String JavaDoc name)
297        throws org.xml.sax.SAXException JavaDoc
298   {
299     if(--eventcounter<=0)
300       {
301         co_yield(true);
302         eventcounter=frequency;
303       }
304     if(clientContentHandler!=null)
305       clientContentHandler.skippedEntity(name);
306   }
307   public void startDocument()
308        throws org.xml.sax.SAXException JavaDoc
309   {
310     co_entry_pause();
311
312     // Otherwise, begin normal event delivery
313
if(--eventcounter<=0)
314       {
315         co_yield(true);
316         eventcounter=frequency;
317       }
318     if(clientContentHandler!=null)
319       clientContentHandler.startDocument();
320   }
321   public void startElement(java.lang.String JavaDoc namespaceURI, java.lang.String JavaDoc localName,
322       java.lang.String JavaDoc qName, Attributes JavaDoc atts)
323        throws org.xml.sax.SAXException JavaDoc
324   {
325     if(--eventcounter<=0)
326       {
327         co_yield(true);
328         eventcounter=frequency;
329       }
330     if(clientContentHandler!=null)
331       clientContentHandler.startElement(namespaceURI, localName, qName, atts);
332   }
333   public void startPrefixMapping(java.lang.String JavaDoc prefix, java.lang.String JavaDoc uri)
334        throws org.xml.sax.SAXException JavaDoc
335   {
336     if(--eventcounter<=0)
337       {
338         co_yield(true);
339         eventcounter=frequency;
340       }
341     if(clientContentHandler!=null)
342       clientContentHandler.startPrefixMapping(prefix,uri);
343   }
344
345   //
346
// LexicalHandler support. Not all SAX2 filters support these events
347
// but we may want to pass them through when they exist...
348
//
349
// %REVIEW% These do NOT currently affect the eventcounter; I'm asserting
350
// that they're rare enough that it makes little or no sense to
351
// pause after them. As such, it may make more sense for folks who
352
// actually want to use them to register directly with the filter.
353
// But I want 'em here for now, to remind us to recheck this assertion!
354
//
355
public void comment(char[] ch, int start, int length)
356        throws org.xml.sax.SAXException JavaDoc
357   {
358     if(null!=clientLexicalHandler)
359       clientLexicalHandler.comment(ch,start,length);
360   }
361   public void endCDATA()
362        throws org.xml.sax.SAXException JavaDoc
363   {
364     if(null!=clientLexicalHandler)
365       clientLexicalHandler.endCDATA();
366   }
367   public void endDTD()
368        throws org.xml.sax.SAXException JavaDoc
369   {
370     if(null!=clientLexicalHandler)
371       clientLexicalHandler.endDTD();
372   }
373   public void endEntity(java.lang.String JavaDoc name)
374        throws org.xml.sax.SAXException JavaDoc
375   {
376     if(null!=clientLexicalHandler)
377       clientLexicalHandler.endEntity(name);
378   }
379   public void startCDATA()
380        throws org.xml.sax.SAXException JavaDoc
381   {
382     if(null!=clientLexicalHandler)
383       clientLexicalHandler.startCDATA();
384   }
385   public void startDTD(java.lang.String JavaDoc name, java.lang.String JavaDoc publicId,
386       java.lang.String JavaDoc systemId)
387        throws org.xml.sax.SAXException JavaDoc
388   {
389     if(null!=clientLexicalHandler)
390       clientLexicalHandler. startDTD(name, publicId, systemId);
391   }
392   public void startEntity(java.lang.String JavaDoc name)
393        throws org.xml.sax.SAXException JavaDoc
394   {
395     if(null!=clientLexicalHandler)
396       clientLexicalHandler.startEntity(name);
397   }
398
399   //
400
// DTDHandler support.
401

402   public void notationDecl(String JavaDoc a, String JavaDoc b, String JavaDoc c) throws SAXException JavaDoc
403   {
404     if(null!=clientDTDHandler)
405         clientDTDHandler.notationDecl(a,b,c);
406   }
407   public void unparsedEntityDecl(String JavaDoc a, String JavaDoc b, String JavaDoc c, String JavaDoc d) throws SAXException JavaDoc
408   {
409     if(null!=clientDTDHandler)
410         clientDTDHandler.unparsedEntityDecl(a,b,c,d);
411   }
412   
413   //
414
// ErrorHandler support.
415
//
416
// PROBLEM: Xerces is apparently _not_ calling the ErrorHandler for
417
// exceptions thrown by the ContentHandler, which prevents us from
418
// handling this properly when running in filtering mode with Xerces
419
// as our event source. It's unclear whether this is a Xerces bug
420
// or a SAX design flaw.
421
//
422
// %REVIEW% Current solution: In filtering mode, it is REQUIRED that
423
// event source make sure this method is invoked if the event stream
424
// abends before endDocument is delivered. If that means explicitly calling
425
// us in the exception handling code because it won't be delivered as part
426
// of the normal SAX ErrorHandler stream, that's fine; Not Our Problem.
427
//
428
public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc
429   {
430     if(null!=clientErrorHandler)
431       clientErrorHandler.error(exception);
432   }
433   
434   public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc
435   {
436     // EXCEPTION: In this case we need to run the event BEFORE we yield --
437
// just as with endDocument, this terminates the event stream.
438
if(null!=clientErrorHandler)
439       clientErrorHandler.error(exception);
440
441     eventcounter=0;
442     co_yield(false);
443
444   }
445   
446   public void warning(SAXParseException JavaDoc exception) throws SAXException JavaDoc
447   {
448     if(null!=clientErrorHandler)
449       clientErrorHandler.error(exception);
450   }
451   
452
453   //
454
// coroutine support
455
//
456

457   public int getSourceCoroutineID() {
458     return fSourceCoroutineID;
459   }
460   public int getControllerCoroutineID() {
461     return fControllerCoroutineID;
462   }
463
464   /** @return the CoroutineManager this CoroutineFilter object is bound to.
465    * If you're using the do...() methods, applications should only
466    * need to talk to the CoroutineManager once, to obtain the
467    * application's Coroutine ID.
468    * */

469   public CoroutineManager getCoroutineManager()
470   {
471     return fCoroutineManager;
472   }
473
474   /** <p>In the SAX delegation code, I've inlined the count-down in
475    * the hope of encouraging compilers to deliver better
476    * performance. However, if we subclass (eg to directly connect the
477    * output to a DTM builder), that would require calling super in
478    * order to run that logic... which seems inelegant. Hence this
479    * routine for the convenience of subclasses: every [frequency]
480    * invocations, issue a co_yield.</p>
481    *
482    * @param moreExepected Should always be true unless this is being called
483    * at the end of endDocument() handling.
484    * */

485   protected void count_and_yield(boolean moreExpected) throws SAXException JavaDoc
486   {
487     if(!moreExpected) eventcounter=0;
488     
489     if(--eventcounter<=0)
490       {
491         co_yield(true);
492         eventcounter=frequency;
493       }
494   }
495
496   /**
497    * co_entry_pause is called in startDocument() before anything else
498    * happens. It causes the filter to wait for a "go ahead" request
499    * from the controller before delivering any events. Note that
500    * the very first thing the controller tells us may be "I don't
501    * need events after all"!
502    */

503   private void co_entry_pause() throws SAXException JavaDoc
504   {
505     if(fCoroutineManager==null)
506     {
507       // Nobody called init()? Do it now...
508
init(null,-1,-1);
509     }
510
511     try
512     {
513       Object JavaDoc arg=fCoroutineManager.co_entry_pause(fSourceCoroutineID);
514       if(arg==Boolean.FALSE)
515         co_yield(false);
516     }
517     catch(NoSuchMethodException JavaDoc e)
518     {
519       // Coroutine system says we haven't registered. That's an
520
// application coding error, and is unrecoverable.
521
if(DEBUG) e.printStackTrace();
522       throw new SAXException JavaDoc(e);
523     }
524   }
525
526   /**
527    * Co_Yield handles coroutine interactions while a parse is in progress.
528    *
529    * When moreRemains==true, we are pausing after delivering events, to
530    * ask if more are needed. We will resume the controller thread with
531    * co_resume(Boolean.TRUE, ...)
532    * When control is passed back it may indicate
533    * Boolean.TRUE indication to continue delivering events
534    * Boolean.FALSE indication to discontinue events and shut down.
535    *
536    * When moreRemains==false, we shut down immediately without asking the
537    * controller's permission. Normally this means end of document has been
538    * reached.
539    *
540    * Shutting down a IncrementalSAXSource_Filter requires terminating the incoming
541    * SAX event stream. If we are in control of that stream (if it came
542    * from an XMLReader passed to our startReader() method), we can do so
543    * very quickly by throwing a reserved exception to it. If the stream is
544    * coming from another source, we can't do that because its caller may
545    * not be prepared for this "normal abnormal exit", and instead we put
546    * ourselves in a "spin" mode where events are discarded.
547    */

548   private void co_yield(boolean moreRemains) throws SAXException JavaDoc
549   {
550     // Horrendous kluge to run filter to completion. See below.
551
if(fNoMoreEvents)
552       return;
553
554     try // Coroutine manager might throw no-such.
555
{
556       Object JavaDoc arg=Boolean.FALSE;
557       if(moreRemains)
558       {
559         // Yield control, resume parsing when done
560
arg = fCoroutineManager.co_resume(Boolean.TRUE, fSourceCoroutineID,
561                                           fControllerCoroutineID);
562         
563       }
564
565       // If we're at end of document or were told to stop early
566
if(arg==Boolean.FALSE)
567       {
568         fNoMoreEvents=true;
569         
570         if(fXMLReader!=null) // Running under startParseThread()
571
throw new StopException(); // We'll co_exit from there.
572

573         // Yield control. We do NOT expect anyone to ever ask us again.
574
fCoroutineManager.co_exit_to(Boolean.FALSE, fSourceCoroutineID,
575                                      fControllerCoroutineID);
576       }
577     }
578     catch(NoSuchMethodException JavaDoc e)
579     {
580       // Shouldn't happen unless we've miscoded our coroutine logic
581
// "Shut down the garbage smashers on the detention level!"
582
fNoMoreEvents=true;
583       fCoroutineManager.co_exit(fSourceCoroutineID);
584       throw new SAXException JavaDoc(e);
585     }
586   }
587
588   //
589
// Convenience: Run an XMLReader in a thread
590
//
591

592   /** Launch a thread that will run an XMLReader's parse() operation within
593    * a thread, feeding events to this IncrementalSAXSource_Filter. Mostly a convenience
594    * routine, but has the advantage that -- since we invoked parse() --
595    * we can halt parsing quickly via a StopException rather than waiting
596    * for the SAX stream to end by itself.
597    *
598    * @throws SAXException is parse thread is already in progress
599    * or parsing can not be started.
600    * */

601   public void startParse(InputSource JavaDoc source) throws SAXException JavaDoc
602   {
603     if(fNoMoreEvents)
604       throw new SAXException JavaDoc(XMLMessages.createXMLMessage(XMLErrorResources.ER_INCRSAXSRCFILTER_NOT_RESTARTABLE, null)); //"IncrmentalSAXSource_Filter not currently restartable.");
605
if(fXMLReader==null)
606       throw new SAXException JavaDoc(XMLMessages.createXMLMessage(XMLErrorResources.ER_XMLRDR_NOT_BEFORE_STARTPARSE, null)); //"XMLReader not before startParse request");
607

608     fXMLReaderInputSource=source;
609     
610     // Xalan thread pooling...
611
// org.apache.xalan.transformer.TransformerImpl.runTransformThread(this);
612
ThreadControllerWrapper.runThread(this, -1);
613   }
614   
615   /* Thread logic to support startParseThread()
616    */

617   public void run()
618   {
619     // Guard against direct invocation of start().
620
if(fXMLReader==null) return;
621
622     if(DEBUG)System.out.println("IncrementalSAXSource_Filter parse thread launched");
623
624     // Initially assume we'll run successfully.
625
Object JavaDoc arg=Boolean.FALSE;
626
627     // For the duration of this operation, all coroutine handshaking
628
// will occur in the co_yield method. That's the nice thing about
629
// coroutines; they give us a way to hand off control from the
630
// middle of a synchronous method.
631
try
632     {
633       fXMLReader.parse(fXMLReaderInputSource);
634     }
635     catch(IOException JavaDoc ex)
636     {
637       arg=ex;
638     }
639     catch(StopException ex)
640     {
641       // Expected and harmless
642
if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
643     }
644     catch (SAXException JavaDoc ex)
645     {
646       Exception JavaDoc inner=ex.getException();
647       if(inner instanceof StopException){
648         // Expected and harmless
649
if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
650       }
651       else
652       {
653         // Unexpected malfunction
654
if(DEBUG)
655         {
656           System.out.println("Active IncrementalSAXSource_Filter UNEXPECTED SAX exception: "+inner);
657           inner.printStackTrace();
658         }
659         arg=ex;
660       }
661     } // end parse
662

663     // Mark as no longer running in thread.
664
fXMLReader=null;
665
666     try
667     {
668       // Mark as done and yield control to the controller coroutine
669
fNoMoreEvents=true;
670       fCoroutineManager.co_exit_to(arg, fSourceCoroutineID,
671                                    fControllerCoroutineID);
672     }
673     catch(java.lang.NoSuchMethodException JavaDoc e)
674     {
675       // Shouldn't happen unless we've miscoded our coroutine logic
676
// "CPO, shut down the garbage smashers on the detention level!"
677
e.printStackTrace(System.err);
678       fCoroutineManager.co_exit(fSourceCoroutineID);
679     }
680   }
681
682   /** Used to quickly terminate parse when running under a
683       startParse() thread. Only its type is important. */

684   class StopException extends RuntimeException JavaDoc
685   {
686   }
687
688   /** deliverMoreNodes() is a simple API which tells the coroutine
689    * parser that we need more nodes. This is intended to be called
690    * from one of our partner routines, and serves to encapsulate the
691    * details of how incremental parsing has been achieved.
692    *
693    * @param parsemore If true, tells the incremental filter to generate
694    * another chunk of output. If false, tells the filter that we're
695    * satisfied and it can terminate parsing of this document.
696    *
697    * @return Boolean.TRUE if there may be more events available by invoking
698    * deliverMoreNodes() again. Boolean.FALSE if parsing has run to completion (or been
699    * terminated by deliverMoreNodes(false). Or an exception object if something
700    * malfunctioned. %REVIEW% We _could_ actually throw the exception, but
701    * that would require runinng deliverMoreNodes() in a try/catch... and for many
702    * applications, exception will be simply be treated as "not TRUE" in
703    * any case.
704    * */

705   public Object JavaDoc deliverMoreNodes(boolean parsemore)
706   {
707     // If parsing is already done, we can immediately say so
708
if(fNoMoreEvents)
709       return Boolean.FALSE;
710
711     try
712     {
713       Object JavaDoc result =
714         fCoroutineManager.co_resume(parsemore?Boolean.TRUE:Boolean.FALSE,
715                                     fControllerCoroutineID, fSourceCoroutineID);
716       if(result==Boolean.FALSE)
717         fCoroutineManager.co_exit(fControllerCoroutineID);
718
719       return result;
720     }
721       
722     // SHOULD NEVER OCCUR, since the coroutine number and coroutine manager
723
// are those previously established for this IncrementalSAXSource_Filter...
724
// So I'm just going to return it as a parsing exception, for now.
725
catch(NoSuchMethodException JavaDoc e)
726       {
727         return e;
728       }
729   }
730   
731
732   //================================================================
733
/** Simple unit test. Attempt coroutine parsing of document indicated
734    * by first argument (as a URI), report progress.
735    */

736     /*
737   public static void main(String args[])
738   {
739     System.out.println("Starting...");
740
741     org.xml.sax.XMLReader theSAXParser=
742       new org.apache.xerces.parsers.SAXParser();
743   
744
745     for(int arg=0;arg<args.length;++arg)
746     {
747       // The filter is not currently designed to be restartable
748       // after a parse has ended. Generate a new one each time.
749       IncrementalSAXSource_Filter filter=
750         new IncrementalSAXSource_Filter();
751       // Use a serializer as our sample output
752       org.apache.xml.serialize.XMLSerializer trace;
753       trace=new org.apache.xml.serialize.XMLSerializer(System.out,null);
754       filter.setContentHandler(trace);
755       filter.setLexicalHandler(trace);
756
757       try
758       {
759         InputSource source = new InputSource(args[arg]);
760         Object result=null;
761         boolean more=true;
762
763         // init not issued; we _should_ automagically Do The Right Thing
764
765         // Bind parser, kick off parsing in a thread
766         filter.setXMLReader(theSAXParser);
767         filter.startParse(source);
768       
769         for(result = filter.deliverMoreNodes(more);
770             (result instanceof Boolean && ((Boolean)result)==Boolean.TRUE);
771             result = filter.deliverMoreNodes(more))
772         {
773           System.out.println("\nSome parsing successful, trying more.\n");
774           
775           // Special test: Terminate parsing early.
776           if(arg+1<args.length && "!".equals(args[arg+1]))
777           {
778             ++arg;
779             more=false;
780           }
781           
782         }
783       
784         if (result instanceof Boolean && ((Boolean)result)==Boolean.FALSE)
785         {
786           System.out.println("\nFilter ended (EOF or on request).\n");
787         }
788         else if (result == null) {
789           System.out.println("\nUNEXPECTED: Filter says shut down prematurely.\n");
790         }
791         else if (result instanceof Exception) {
792           System.out.println("\nFilter threw exception:");
793           ((Exception)result).printStackTrace();
794         }
795       
796       }
797       catch(SAXException e)
798       {
799         e.printStackTrace();
800       }
801     } // end for
802   }
803     */

804 } // class IncrementalSAXSource_Filter
805
Popular Tags