FileDocCategorySizeDatePackage
IncrementalSAXSource_Filter.javaAPI DocJava SE 5 API27015Fri Aug 26 14:56:00 BST 2005com.sun.org.apache.xml.internal.dtm.ref

IncrementalSAXSource_Filter

public class IncrementalSAXSource_Filter extends Object implements IncrementalSAXSource, ErrorHandler, LexicalHandler, Runnable, DTDHandler, ContentHandler

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.

Fields Summary
boolean
DEBUG
private CoroutineManager
fCoroutineManager
private int
fControllerCoroutineID
private int
fSourceCoroutineID
private ContentHandler
clientContentHandler
private LexicalHandler
clientLexicalHandler
private DTDHandler
clientDTDHandler
private ErrorHandler
clientErrorHandler
private int
eventcounter
private int
frequency
private boolean
fNoMoreEvents
private XMLReader
fXMLReader
private InputSource
fXMLReaderInputSource
Constructors Summary
public IncrementalSAXSource_Filter()


  //
  // Constructors
  //

    
    this.init( new CoroutineManager(), -1, -1);
  
public IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
Create a IncrementalSAXSource_Filter which is not yet bound to a specific SAX event source.

    this.init( co, controllerCoroutineID, -1 );
  
Methods Summary
public voidcharacters(char[] ch, int start, int length)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.characters(ch,start,length);
  
private voidco_entry_pause()
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"!

    if(fCoroutineManager==null)
    {
      // Nobody called init()? Do it now...
      init(null,-1,-1);
    }

    try
    {
      Object arg=fCoroutineManager.co_entry_pause(fSourceCoroutineID);
      if(arg==Boolean.FALSE)
        co_yield(false);
    }
    catch(NoSuchMethodException e)
    {
      // Coroutine system says we haven't registered. That's an
      // application coding error, and is unrecoverable.
      if(DEBUG) e.printStackTrace();
      throw new SAXException(e);
    }
  
private voidco_yield(boolean moreRemains)
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.

    // Horrendous kluge to run filter to completion. See below.
    if(fNoMoreEvents)
      return;

    try // Coroutine manager might throw no-such.
    {
      Object arg=Boolean.FALSE;
      if(moreRemains)
      {
        // Yield control, resume parsing when done
        arg = fCoroutineManager.co_resume(Boolean.TRUE, fSourceCoroutineID,
                                          fControllerCoroutineID);
        
      }

      // If we're at end of document or were told to stop early
      if(arg==Boolean.FALSE)
      {
        fNoMoreEvents=true;
        
        if(fXMLReader!=null)    // Running under startParseThread()
          throw new StopException(); // We'll co_exit from there.
        
        // Yield control. We do NOT expect anyone to ever ask us again.
        fCoroutineManager.co_exit_to(Boolean.FALSE, fSourceCoroutineID,
                                     fControllerCoroutineID);
      }
    }
    catch(NoSuchMethodException e)
    {
      // Shouldn't happen unless we've miscoded our coroutine logic
      // "Shut down the garbage smashers on the detention level!"
      fNoMoreEvents=true;
      fCoroutineManager.co_exit(fSourceCoroutineID);
      throw new SAXException(e);
    }
  
public voidcomment(char[] ch, int start, int length)

    if(null!=clientLexicalHandler)
      clientLexicalHandler.comment(ch,start,length);
  
protected voidcount_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. 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.

param
moreExepected Should always be true unless this is being called at the end of endDocument() handling.

    if(!moreExpected) eventcounter=0;
    
    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
  
public static com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSourcecreateIncrementalSAXSource(com.sun.org.apache.xml.internal.dtm.ref.CoroutineManager co, int controllerCoroutineID)

    return new IncrementalSAXSource_Filter(co, controllerCoroutineID);
  
public java.lang.ObjectdeliverMoreNodes(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.

param
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.
return
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.

    // If parsing is already done, we can immediately say so
    if(fNoMoreEvents)
      return Boolean.FALSE;

    try 
    {
      Object result =
        fCoroutineManager.co_resume(parsemore?Boolean.TRUE:Boolean.FALSE,
                                    fControllerCoroutineID, fSourceCoroutineID);
      if(result==Boolean.FALSE)
        fCoroutineManager.co_exit(fControllerCoroutineID);

      return result;
    }
      
    // SHOULD NEVER OCCUR, since the coroutine number and coroutine manager
    // are those previously established for this IncrementalSAXSource_Filter...
    // So I'm just going to return it as a parsing exception, for now.
    catch(NoSuchMethodException e)
      {
        return e;
      }
  
public voidendCDATA()

    if(null!=clientLexicalHandler)
      clientLexicalHandler.endCDATA();
  
public voidendDTD()

    if(null!=clientLexicalHandler)
      clientLexicalHandler.endDTD();
  
public voidendDocument()

    // EXCEPTION: In this case we need to run the event BEFORE we yield.
    if(clientContentHandler!=null)
      clientContentHandler.endDocument();

    eventcounter=0;     
    co_yield(false);
  
public voidendElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.endElement(namespaceURI,localName,qName);
  
public voidendEntity(java.lang.String name)

    if(null!=clientLexicalHandler)
      clientLexicalHandler.endEntity(name);
  
public voidendPrefixMapping(java.lang.String prefix)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.endPrefixMapping(prefix);
  
public voiderror(org.xml.sax.SAXParseException exception)

    if(null!=clientErrorHandler)
      clientErrorHandler.error(exception);
  
public voidfatalError(org.xml.sax.SAXParseException exception)

    // EXCEPTION: In this case we need to run the event BEFORE we yield --
    // just as with endDocument, this terminates the event stream.
    if(null!=clientErrorHandler)
      clientErrorHandler.error(exception);

    eventcounter=0;     
    co_yield(false);

  
public intgetControllerCoroutineID()

    return fControllerCoroutineID;
  
public com.sun.org.apache.xml.internal.dtm.ref.CoroutineManagergetCoroutineManager()

return
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.

    return fCoroutineManager;
  
public intgetSourceCoroutineID()

    return fSourceCoroutineID;
  
public voidignorableWhitespace(char[] ch, int start, int length)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.ignorableWhitespace(ch,start,length);
  
public voidinit(com.sun.org.apache.xml.internal.dtm.ref.CoroutineManager co, int controllerCoroutineID, int sourceCoroutineID)

    if(co==null)
      co = new CoroutineManager();
    fCoroutineManager = co;
    fControllerCoroutineID = co.co_joinCoroutineSet(controllerCoroutineID);
    fSourceCoroutineID = co.co_joinCoroutineSet(sourceCoroutineID);
    if (fControllerCoroutineID == -1 || fSourceCoroutineID == -1)
      throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COJOINROUTINESET_FAILED, null)); //"co_joinCoroutineSet() failed");

    fNoMoreEvents=false;
    eventcounter=frequency;
  
public voidnotationDecl(java.lang.String a, java.lang.String b, java.lang.String c)

  	if(null!=clientDTDHandler)
	  	clientDTDHandler.notationDecl(a,b,c);
  
public voidprocessingInstruction(java.lang.String target, java.lang.String data)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.processingInstruction(target,data);
  
public voidrun()

    // Guard against direct invocation of start().
    if(fXMLReader==null) return;

    if(DEBUG)System.out.println("IncrementalSAXSource_Filter parse thread launched");

    // Initially assume we'll run successfully.
    Object arg=Boolean.FALSE;

    // For the duration of this operation, all coroutine handshaking
    // will occur in the co_yield method. That's the nice thing about
    // coroutines; they give us a way to hand off control from the
    // middle of a synchronous method.
    try
    {
      fXMLReader.parse(fXMLReaderInputSource);
    }
    catch(IOException ex)
    {
      arg=ex;
    }
    catch(StopException ex)
    {
      // Expected and harmless
      if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
    }
    catch (SAXException ex)
    {
      Exception inner=ex.getException();
      if(inner instanceof StopException){
        // Expected and harmless
        if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
      }
      else
      {
        // Unexpected malfunction
        if(DEBUG)
        {
          System.out.println("Active IncrementalSAXSource_Filter UNEXPECTED SAX exception: "+inner);
          inner.printStackTrace();
        }                
        arg=ex;
      }
    } // end parse
    
    // Mark as no longer running in thread.
    fXMLReader=null;

    try
    {
      // Mark as done and yield control to the controller coroutine
      fNoMoreEvents=true;
      fCoroutineManager.co_exit_to(arg, fSourceCoroutineID,
                                   fControllerCoroutineID);
    }
    catch(java.lang.NoSuchMethodException e)
    {
      // Shouldn't happen unless we've miscoded our coroutine logic
      // "CPO, shut down the garbage smashers on the detention level!"
      e.printStackTrace(System.err);
      fCoroutineManager.co_exit(fSourceCoroutineID);
    }
  
public voidsetContentHandler(org.xml.sax.ContentHandler handler)

    clientContentHandler=handler;
  
public voidsetDTDHandler(org.xml.sax.DTDHandler handler)

    clientDTDHandler=handler;
  
public voidsetDocumentLocator(org.xml.sax.Locator locator)

    if(--eventcounter<=0)
      {
        // This can cause a hang.  -sb
        // co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.setDocumentLocator(locator);
  
public voidsetErrHandler(org.xml.sax.ErrorHandler handler)

    clientErrorHandler=handler;
  
public voidsetLexicalHandler(org.xml.sax.ext.LexicalHandler handler)

    clientLexicalHandler=handler;
  
public voidsetReturnFrequency(int events)

    if(events<1) events=1;
    frequency=eventcounter=events;
  
public voidsetXMLReader(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.

    fXMLReader=eventsource;
    eventsource.setContentHandler(this);
    eventsource.setDTDHandler(this);
    eventsource.setErrorHandler(this); // to report fatal errors in filtering mode

    // Not supported by all SAX2 filters:
    try 
    {
      eventsource.
        setProperty("http://xml.org/sax/properties/lexical-handler",
                    this);
    }
    catch(SAXNotRecognizedException e)
    {
      // Nothing we can do about it
    }
    catch(SAXNotSupportedException e)
    {
      // Nothing we can do about it
    }

    // Should we also bind as other varieties of handler?
    // (DTDHandler and so on)
  
public voidskippedEntity(java.lang.String name)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.skippedEntity(name);
  
public voidstartCDATA()

    if(null!=clientLexicalHandler)
      clientLexicalHandler.startCDATA();
  
public voidstartDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)

    if(null!=clientLexicalHandler)
      clientLexicalHandler. startDTD(name, publicId, systemId);
  
public voidstartDocument()

    co_entry_pause();

    // Otherwise, begin normal event delivery
    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.startDocument();
  
public voidstartElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName, org.xml.sax.Attributes atts)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.startElement(namespaceURI, localName, qName, atts);
  
public voidstartEntity(java.lang.String name)

    if(null!=clientLexicalHandler)
      clientLexicalHandler.startEntity(name);
  
public voidstartParse(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. 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.

throws
SAXException is parse thread is already in progress or parsing can not be started.

    if(fNoMoreEvents)
      throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_INCRSAXSRCFILTER_NOT_RESTARTABLE, null)); //"IncrmentalSAXSource_Filter not currently restartable.");
    if(fXMLReader==null)
      throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_XMLRDR_NOT_BEFORE_STARTPARSE, null)); //"XMLReader not before startParse request");

    fXMLReaderInputSource=source;
    
    // Xalan thread pooling...
    // com.sun.org.apache.xalan.internal.transformer.TransformerImpl.runTransformThread(this);
    ThreadControllerWrapper.runThread(this, -1);
  
public voidstartPrefixMapping(java.lang.String prefix, java.lang.String uri)

    if(--eventcounter<=0)
      {
        co_yield(true);
        eventcounter=frequency;
      }
    if(clientContentHandler!=null)
      clientContentHandler.startPrefixMapping(prefix,uri);
  
public voidunparsedEntityDecl(java.lang.String a, java.lang.String b, java.lang.String c, java.lang.String d)

  	if(null!=clientDTDHandler)
	  	clientDTDHandler.unparsedEntityDecl(a,b,c,d);
  
public voidwarning(org.xml.sax.SAXParseException exception)

    if(null!=clientErrorHandler)
      clientErrorHandler.error(exception);