org.apache.xml.dtm.ref
Class IncrementalSAXSource_Filter

java.lang.Object
  extended by org.apache.xml.dtm.ref.IncrementalSAXSource_Filter
All Implemented Interfaces:
java.lang.Runnable, IncrementalSAXSource, org.xml.sax.ContentHandler, org.xml.sax.DTDHandler, org.xml.sax.ErrorHandler, org.xml.sax.ext.LexicalHandler

public class IncrementalSAXSource_Filter
extends java.lang.Object
implements IncrementalSAXSource, org.xml.sax.ContentHandler, org.xml.sax.DTDHandler, org.xml.sax.ext.LexicalHandler, org.xml.sax.ErrorHandler, java.lang.Runnable

IncrementalSAXSource_Filter implements IncrementalSAXSource, using a standard SAX2 event source as its input and parcelling out those events gradually in reponse to deliverMoreNodes() requests. Output from the filter will be passed along to a SAX handler registered as our listener, but those callbacks will pass through a counting stage which periodically yields control back to the controller coroutine.

%REVIEW%: This filter is not currenly intended to be reusable for parsing additional streams/documents. We may want to consider making it resettable at some point in the future. But it's a small object, so that'd be mostly a convenience issue; the cost of allocating each time is trivial compared to the cost of processing any nontrival stream.

For a brief usage example, see the unit-test main() method.

This is a simplification of the old CoroutineSAXParser, focusing specifically on filtering. The resulting controller protocol is _far_ simpler and less error-prone; the only controller operation is deliverMoreNodes(), and the only requirement is that deliverMoreNodes(false) be called if you want to discard the rest of the stream and the previous deliverMoreNodes() didn't return false.


Nested Class Summary
(package private) static class IncrementalSAXSource_Filter.StopException
          Used to quickly terminate parse when running under a startParse() thread.
 
Field Summary
private  org.xml.sax.ContentHandler clientContentHandler
           
private  org.xml.sax.DTDHandler clientDTDHandler
           
private  org.xml.sax.ErrorHandler clientErrorHandler
           
private  org.xml.sax.ext.LexicalHandler clientLexicalHandler
           
(package private)  boolean DEBUG
           
private  int eventcounter
           
private  int fControllerCoroutineID
           
private  CoroutineManager fCoroutineManager
           
private  boolean fNoMoreEvents
           
private  int frequency
           
private  int fSourceCoroutineID
           
private  org.xml.sax.XMLReader fXMLReader
           
private  org.xml.sax.InputSource fXMLReaderInputSource
           
 
Constructor Summary
IncrementalSAXSource_Filter()
           
IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
          Create a IncrementalSAXSource_Filter which is not yet bound to a specific SAX event source.
 
Method Summary
 void characters(char[] ch, int start, int length)
           
private  void co_entry_pause()
          co_entry_pause is called in startDocument() before anything else happens.
private  void co_yield(boolean moreRemains)
          Co_Yield handles coroutine interactions while a parse is in progress.
 void comment(char[] ch, int start, int length)
           
protected  void count_and_yield(boolean moreExpected)
          In the SAX delegation code, I've inlined the count-down in the hope of encouraging compilers to deliver better performance.
static IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co, int controllerCoroutineID)
           
 java.lang.Object deliverMoreNodes(boolean parsemore)
          deliverMoreNodes() is a simple API which tells the coroutine parser that we need more nodes.
 void endCDATA()
           
 void endDocument()
           
 void endDTD()
           
 void endElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName)
           
 void endEntity(java.lang.String name)
           
 void endPrefixMapping(java.lang.String prefix)
           
 void error(org.xml.sax.SAXParseException exception)
           
 void fatalError(org.xml.sax.SAXParseException exception)
           
 int getControllerCoroutineID()
           
 CoroutineManager getCoroutineManager()
           
 int getSourceCoroutineID()
           
 void ignorableWhitespace(char[] ch, int start, int length)
           
 void init(CoroutineManager co, int controllerCoroutineID, int sourceCoroutineID)
           
 void notationDecl(java.lang.String a, java.lang.String b, java.lang.String c)
           
 void processingInstruction(java.lang.String target, java.lang.String data)
           
 void run()
           
 void setContentHandler(org.xml.sax.ContentHandler handler)
          Register a SAX-style content handler for us to output to
 void setDocumentLocator(org.xml.sax.Locator locator)
           
 void setDTDHandler(org.xml.sax.DTDHandler handler)
          Register a SAX-style DTD handler for us to output to
 void setErrHandler(org.xml.sax.ErrorHandler handler)
           
 void setLexicalHandler(org.xml.sax.ext.LexicalHandler handler)
          Register a SAX-style lexical handler for us to output to
 void setReturnFrequency(int events)
           
 void setXMLReader(org.xml.sax.XMLReader eventsource)
          Bind our input streams to an XMLReader.
 void skippedEntity(java.lang.String name)
           
 void startCDATA()
           
 void startDocument()
           
 void startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
           
 void startElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName, org.xml.sax.Attributes atts)
           
 void startEntity(java.lang.String name)
           
 void startParse(org.xml.sax.InputSource source)
          Launch a thread that will run an XMLReader's parse() operation within a thread, feeding events to this IncrementalSAXSource_Filter.
 void startPrefixMapping(java.lang.String prefix, java.lang.String uri)
           
 void unparsedEntityDecl(java.lang.String a, java.lang.String b, java.lang.String c, java.lang.String d)
           
 void warning(org.xml.sax.SAXParseException exception)
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DEBUG

boolean DEBUG

fCoroutineManager

private CoroutineManager fCoroutineManager

fControllerCoroutineID

private int fControllerCoroutineID

fSourceCoroutineID

private int fSourceCoroutineID

clientContentHandler

private org.xml.sax.ContentHandler clientContentHandler

clientLexicalHandler

private org.xml.sax.ext.LexicalHandler clientLexicalHandler

clientDTDHandler

private org.xml.sax.DTDHandler clientDTDHandler

clientErrorHandler

private org.xml.sax.ErrorHandler clientErrorHandler

eventcounter

private int eventcounter

frequency

private int frequency

fNoMoreEvents

private boolean fNoMoreEvents

fXMLReader

private org.xml.sax.XMLReader fXMLReader

fXMLReaderInputSource

private org.xml.sax.InputSource fXMLReaderInputSource
Constructor Detail

IncrementalSAXSource_Filter

public IncrementalSAXSource_Filter()

IncrementalSAXSource_Filter

public IncrementalSAXSource_Filter(CoroutineManager co,
                                   int controllerCoroutineID)
Create a IncrementalSAXSource_Filter which is not yet bound to a specific SAX event source.

Method Detail

createIncrementalSAXSource

public static IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co,
                                                              int controllerCoroutineID)

init

public void init(CoroutineManager co,
                 int controllerCoroutineID,
                 int sourceCoroutineID)

setXMLReader

public void setXMLReader(org.xml.sax.XMLReader eventsource)
Bind our input streams to an XMLReader. Just a convenience routine; obviously you can explicitly register this as a listener with the same effect.


setContentHandler

public void setContentHandler(org.xml.sax.ContentHandler handler)
Description copied from interface: IncrementalSAXSource
Register a SAX-style content handler for us to output to

Specified by:
setContentHandler in interface IncrementalSAXSource

setDTDHandler

public void setDTDHandler(org.xml.sax.DTDHandler handler)
Description copied from interface: IncrementalSAXSource
Register a SAX-style DTD handler for us to output to

Specified by:
setDTDHandler in interface IncrementalSAXSource

setLexicalHandler

public void setLexicalHandler(org.xml.sax.ext.LexicalHandler handler)
Description copied from interface: IncrementalSAXSource
Register a SAX-style lexical handler for us to output to

Specified by:
setLexicalHandler in interface IncrementalSAXSource

setErrHandler

public void setErrHandler(org.xml.sax.ErrorHandler handler)

setReturnFrequency

public void setReturnFrequency(int events)

characters

public void characters(char[] ch,
                       int start,
                       int length)
                throws org.xml.sax.SAXException
Specified by:
characters in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

endDocument

public void endDocument()
                 throws org.xml.sax.SAXException
Specified by:
endDocument in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

endElement

public void endElement(java.lang.String namespaceURI,
                       java.lang.String localName,
                       java.lang.String qName)
                throws org.xml.sax.SAXException
Specified by:
endElement in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

endPrefixMapping

public void endPrefixMapping(java.lang.String prefix)
                      throws org.xml.sax.SAXException
Specified by:
endPrefixMapping in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

ignorableWhitespace

public void ignorableWhitespace(char[] ch,
                                int start,
                                int length)
                         throws org.xml.sax.SAXException
Specified by:
ignorableWhitespace in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

processingInstruction

public void processingInstruction(java.lang.String target,
                                  java.lang.String data)
                           throws org.xml.sax.SAXException
Specified by:
processingInstruction in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

setDocumentLocator

public void setDocumentLocator(org.xml.sax.Locator locator)
Specified by:
setDocumentLocator in interface org.xml.sax.ContentHandler

skippedEntity

public void skippedEntity(java.lang.String name)
                   throws org.xml.sax.SAXException
Specified by:
skippedEntity in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

startDocument

public void startDocument()
                   throws org.xml.sax.SAXException
Specified by:
startDocument in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

startElement

public void startElement(java.lang.String namespaceURI,
                         java.lang.String localName,
                         java.lang.String qName,
                         org.xml.sax.Attributes atts)
                  throws org.xml.sax.SAXException
Specified by:
startElement in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

startPrefixMapping

public void startPrefixMapping(java.lang.String prefix,
                               java.lang.String uri)
                        throws org.xml.sax.SAXException
Specified by:
startPrefixMapping in interface org.xml.sax.ContentHandler
Throws:
org.xml.sax.SAXException

comment

public void comment(char[] ch,
                    int start,
                    int length)
             throws org.xml.sax.SAXException
Specified by:
comment in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

endCDATA

public void endCDATA()
              throws org.xml.sax.SAXException
Specified by:
endCDATA in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

endDTD

public void endDTD()
            throws org.xml.sax.SAXException
Specified by:
endDTD in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

endEntity

public void endEntity(java.lang.String name)
               throws org.xml.sax.SAXException
Specified by:
endEntity in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

startCDATA

public void startCDATA()
                throws org.xml.sax.SAXException
Specified by:
startCDATA in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

startDTD

public void startDTD(java.lang.String name,
                     java.lang.String publicId,
                     java.lang.String systemId)
              throws org.xml.sax.SAXException
Specified by:
startDTD in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

startEntity

public void startEntity(java.lang.String name)
                 throws org.xml.sax.SAXException
Specified by:
startEntity in interface org.xml.sax.ext.LexicalHandler
Throws:
org.xml.sax.SAXException

notationDecl

public void notationDecl(java.lang.String a,
                         java.lang.String b,
                         java.lang.String c)
                  throws org.xml.sax.SAXException
Specified by:
notationDecl in interface org.xml.sax.DTDHandler
Throws:
org.xml.sax.SAXException

unparsedEntityDecl

public void unparsedEntityDecl(java.lang.String a,
                               java.lang.String b,
                               java.lang.String c,
                               java.lang.String d)
                        throws org.xml.sax.SAXException
Specified by:
unparsedEntityDecl in interface org.xml.sax.DTDHandler
Throws:
org.xml.sax.SAXException

error

public void error(org.xml.sax.SAXParseException exception)
           throws org.xml.sax.SAXException
Specified by:
error in interface org.xml.sax.ErrorHandler
Throws:
org.xml.sax.SAXException

fatalError

public void fatalError(org.xml.sax.SAXParseException exception)
                throws org.xml.sax.SAXException
Specified by:
fatalError in interface org.xml.sax.ErrorHandler
Throws:
org.xml.sax.SAXException

warning

public void warning(org.xml.sax.SAXParseException exception)
             throws org.xml.sax.SAXException
Specified by:
warning in interface org.xml.sax.ErrorHandler
Throws:
org.xml.sax.SAXException

getSourceCoroutineID

public int getSourceCoroutineID()

getControllerCoroutineID

public int getControllerCoroutineID()

getCoroutineManager

public CoroutineManager getCoroutineManager()
Returns:
the CoroutineManager this CoroutineFilter object is bound to. If you're using the do...() methods, applications should only need to talk to the CoroutineManager once, to obtain the application's Coroutine ID.

count_and_yield

protected void count_and_yield(boolean moreExpected)
                        throws org.xml.sax.SAXException

In the SAX delegation code, I've inlined the count-down in the hope of encouraging compilers to deliver better performance. However, if we subclass (eg to directly connect the output to a DTM builder), that would require calling super in order to run that logic... which seems inelegant. Hence this routine for the convenience of subclasses: every [frequency] invocations, issue a co_yield.

Parameters:
moreExepected - Should always be true unless this is being called at the end of endDocument() handling.
Throws:
org.xml.sax.SAXException

co_entry_pause

private void co_entry_pause()
                     throws org.xml.sax.SAXException
co_entry_pause is called in startDocument() before anything else happens. It causes the filter to wait for a "go ahead" request from the controller before delivering any events. Note that the very first thing the controller tells us may be "I don't need events after all"!

Throws:
org.xml.sax.SAXException

co_yield

private void co_yield(boolean moreRemains)
               throws org.xml.sax.SAXException
Co_Yield handles coroutine interactions while a parse is in progress. When moreRemains==true, we are pausing after delivering events, to ask if more are needed. We will resume the controller thread with co_resume(Boolean.TRUE, ...) When control is passed back it may indicate Boolean.TRUE indication to continue delivering events Boolean.FALSE indication to discontinue events and shut down. When moreRemains==false, we shut down immediately without asking the controller's permission. Normally this means end of document has been reached. Shutting down a IncrementalSAXSource_Filter requires terminating the incoming SAX event stream. If we are in control of that stream (if it came from an XMLReader passed to our startReader() method), we can do so very quickly by throwing a reserved exception to it. If the stream is coming from another source, we can't do that because its caller may not be prepared for this "normal abnormal exit", and instead we put ourselves in a "spin" mode where events are discarded.

Throws:
org.xml.sax.SAXException

startParse

public void startParse(org.xml.sax.InputSource source)
                throws org.xml.sax.SAXException
Launch a thread that will run an XMLReader's parse() operation within a thread, feeding events to this IncrementalSAXSource_Filter. Mostly a convenience routine, but has the advantage that -- since we invoked parse() -- we can halt parsing quickly via a StopException rather than waiting for the SAX stream to end by itself.

Specified by:
startParse in interface IncrementalSAXSource
Throws:
org.xml.sax.SAXException - is parse thread is already in progress or parsing can not be started.

run

public void run()
Specified by:
run in interface java.lang.Runnable

deliverMoreNodes

public java.lang.Object deliverMoreNodes(boolean parsemore)
deliverMoreNodes() is a simple API which tells the coroutine parser that we need more nodes. This is intended to be called from one of our partner routines, and serves to encapsulate the details of how incremental parsing has been achieved.

Specified by:
deliverMoreNodes in interface IncrementalSAXSource
Parameters:
parsemore - If true, tells the incremental filter to generate another chunk of output. If false, tells the filter that we're satisfied and it can terminate parsing of this document.
Returns:
Boolean.TRUE if there may be more events available by invoking deliverMoreNodes() again. Boolean.FALSE if parsing has run to completion (or been terminated by deliverMoreNodes(false). Or an exception object if something malfunctioned. %REVIEW% We _could_ actually throw the exception, but that would require runinng deliverMoreNodes() in a try/catch... and for many applications, exception will be simply be treated as "not TRUE" in any case.