jkesselm    02/01/23 14:53:47

  Modified:    java/src/org/apache/xalan/templates ElemTemplate.java
               java/src/org/apache/xalan/transformer TransformerImpl.java
               java/src/org/apache/xml/dtm DTMManager.java
               java/src/org/apache/xml/dtm/ref DTMDefaultBase.java
                        DTMManagerDefault.java
               java/src/org/apache/xml/dtm/ref/sax2dtm SAX2DTM.java
               java/src/org/apache/xpath XPathContext.java
  Added:       java/src/org/apache/xml/dtm/ref/sax2dtm SAX2RTFDTM.java
  Log:
  These changes allow us to store multiple Result Tree Fragment (RTF)
  document trees (used when an XSLT variable contains a constructed
  set of nodes)  into a single DTM object rather than using a new DTM
  for each RTF. They also permit "tail-pruning" this shared DTM to
  reuse that space as the variables go out of scope.
  
  The result is a slight performance improvement, and a much more
  significant improvement in storage efficiency. Stylesheets which
  use RTFs heavily should now run in much less memory; in
  one testcase, our "working set" heap size (storage actually in
  use, not counting objects released but not yet GC'd) dropped
  from 12-15MB down to 3-6MB, and heap churn (how quickly
  storage was being allocated and discarded) also reduced
  substantially.
  
  The code changes needed to support this new scheme are
  surprisingly small. And I believe it may be possible to reduce them
  further, if we're willing to merge the SAX2RTFDTM subclass back
  into its SAX2DTM superclass.  I believe that could be done with
  very little adverse impact on other uses of SAX2DTM... but I felt it
  was safer to defer that decision for now.
  
  Revision  Changes    Path
  1.14      +6 -2      
xml-xalan/java/src/org/apache/xalan/templates/ElemTemplate.java
  
  Index: ElemTemplate.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/templates/ElemTemplate.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- ElemTemplate.java 12 Jun 2001 19:15:11 -0000      1.13
  +++ ElemTemplate.java 23 Jan 2002 22:53:46 -0000      1.14
  @@ -91,7 +91,6 @@
    */
   public class ElemTemplate extends ElemTemplateElement
   {
  -
     /** The public identifier for the current document event.
      *  @serial          */
     private String m_publicId;
  @@ -424,6 +423,9 @@
       if (TransformerImpl.S_DEBUG)
         transformer.getTraceManager().fireTraceEvent(this);
   
  +    XPathContext xctxt = transformer.getXPathContext();      
  +    xctxt.pushRTFContext();
  +
         // %REVIEW% commenting out of the code below.
   //    if (null != sourceNode)
   //    {
  @@ -437,7 +439,9 @@
   //
   //      //"sourceNode is null in handleApplyTemplatesInstruction!");
   //    }
  -  }
  +
  +    xctxt.popRTFContext();  
  +    }
   
     /**
      * This function is called during recomposition to
  
  
  
  1.117     +21 -5     
xml-xalan/java/src/org/apache/xalan/transformer/TransformerImpl.java
  
  Index: TransformerImpl.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/transformer/TransformerImpl.java,v
  retrieving revision 1.116
  retrieving revision 1.117
  diff -u -r1.116 -r1.117
  --- TransformerImpl.java      6 Nov 2001 16:50:48 -0000       1.116
  +++ TransformerImpl.java      23 Jan 2002 22:53:46 -0000      1.117
  @@ -1704,11 +1704,17 @@
     {
   
       XPathContext xctxt = m_xcontext;
  -    DTM dtmFrag = xctxt.getDTM(null, true, this, false, false);
  +    
  +    // Retrieve a DTM to contain the RTF. At this writing, this may be a
  +    // multi-document DTM (SAX2RTFDTM).
  +    DTM dtmFrag = xctxt.getRTFDTM();
       ContentHandler rtfHandler = dtmFrag.getContentHandler();
   
  -    // Create a ResultTreeFrag object.
  -    int resultFragment = resultFragment = dtmFrag.getDocument();
  +    // Obtain the ResultTreeFrag's root node.
  +    // NOTE: In SAX2RTFDTM, this value isn't available until after
  +    // the startDocument has been issued, so assignment has been moved
  +    // down a bit in the code.
  +    int resultFragment; // not yet reliably = dtmFrag.getDocument();
   
       // Save the current result tree handler.
       ResultTreeHandler savedRTreeHandler = this.m_resultTreeHandler;
  @@ -1721,7 +1727,12 @@
       try
       {
         rth.startDocument();
  -
  +      
  +      // startDocument is "bottlenecked" in RTH. We need it acted upon 
immediately,
  +      // to set the DTM's state as in-progress, so that if the 
xsl:variable's body causes
  +      // further RTF activity we can keep that from bashing this DTM.
  +      rth.flushPending(); 
  + 
         try
         {
   
  @@ -1730,7 +1741,12 @@
   
           // Make sure everything is flushed!
           rth.flushPending();
  -      }
  +        
  +        // Get the document ID. May not exist until the RTH has not only
  +        // recieved, but flushed, the startDocument... so waiting until
  +        // just before the end seems simplest/safest.
  +         resultFragment = dtmFrag.getDocument();      
  +       }
         finally
         {
           rth.endDocument();
  
  
  
  1.9       +3 -2      xml-xalan/java/src/org/apache/xml/dtm/DTMManager.java
  
  Index: DTMManager.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/java/src/org/apache/xml/dtm/DTMManager.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- DTMManager.java   26 Nov 2001 22:08:28 -0000      1.8
  +++ DTMManager.java   23 Jan 2002 22:53:47 -0000      1.9
  @@ -215,8 +215,9 @@
      * always be returned.  Otherwise it is up to the DTMManager to return a
      * new instance or an instance that it already created and may be being 
used
      * by someone else.
  -   * (I think more parameters will need to be added for error handling, and 
entity
  -   * resolution).
  +   * 
  +   * (More parameters may eventually need to be added for error handling
  +   * and entity resolution, and to better control selection of 
implementations.)
      *
      * @param source the specification of the source object, which may be null,
      *               in which case it is assumed that node construction will 
take
  
  
  
  1.25      +8 -7      
xml-xalan/java/src/org/apache/xml/dtm/ref/DTMDefaultBase.java
  
  Index: DTMDefaultBase.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xml/dtm/ref/DTMDefaultBase.java,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- DTMDefaultBase.java       18 Dec 2001 21:55:33 -0000      1.24
  +++ DTMDefaultBase.java       23 Jan 2002 22:53:47 -0000      1.25
  @@ -1138,15 +1138,13 @@
         }
       else
         {
  -        // Most recent?
  +        // Most recent. May be -1 (none) if DTM was pruned.
           // %OPT% Is there a lastElement() method? Should there be?
           int last=m_namespaceDeclSetElements.size()-1;
  -
  -        if(elementNodeIndex==
  -           m_namespaceDeclSetElements.elementAt(last))
  +             
  +        if(last>=0 && 
elementNodeIndex==m_namespaceDeclSetElements.elementAt(last))
             {
               
nsList=(SuballocatedIntVector)m_namespaceDeclSets.elementAt(last);
  -
             }
         }
       if(nsList==null)
  @@ -1391,8 +1389,11 @@
     }
   
     /**
  -   *  Given a node handle, find the owning document node.
  -   *
  +   * Find the Document node handle for the document currently under 
construction.
  +   * PLEASE NOTE that most people should use getOwnerDocument(nodeHandle) 
instead;
  +   * this version of the operation is primarily intended for use during 
negotiation
  +   * with the DTM Manager.
  +   * 
      *  @param nodeHandle the id of the node.
      *  @return int Node handle of document, which should always be valid.
      */
  
  
  
  1.33      +21 -5     
xml-xalan/java/src/org/apache/xml/dtm/ref/DTMManagerDefault.java
  
  Index: DTMManagerDefault.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xml/dtm/ref/DTMManagerDefault.java,v
  retrieving revision 1.32
  retrieving revision 1.33
  diff -u -r1.32 -r1.33
  --- DTMManagerDefault.java    10 Dec 2001 20:55:50 -0000      1.32
  +++ DTMManagerDefault.java    23 Jan 2002 22:53:47 -0000      1.33
  @@ -73,6 +73,7 @@
   import org.apache.xml.utils.SystemIDResolver;
   import org.apache.xml.dtm.ref.dom2dtm.DOM2DTM;
   import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
  +import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
   
   // W3C DOM
   import org.w3c.dom.Document;
  @@ -190,7 +191,7 @@
     }
   
     /**
  -   * Get the first free DTM ID available.
  +   * Get the first free DTM ID available. %OPT% Linear search is inefficient!
      */
     public int getFirstFreeDTMID()
     {
  @@ -229,8 +230,13 @@
      * always be returned.  Otherwise it is up to the DTMManager to return a
      * new instance or an instance that it already created and may be being 
used
      * by someone else.
  +   * 
  +   * A bit of magic in this implementation: If the source is null, unique is 
true,
  +   * and incremental and doIndexing are both false, we return an instance of
  +   * SAX2RTFDTM, which see.
  +   * 
      * (I think more parameters will need to be added for error handling, and 
entity
  -   * resolution).
  +   * resolution, and more explicit control of the RTF situation).
      *
      * @param source the specification of the source object.
      * @param unique true if the returned DTM must be unique, probably because 
it
  @@ -314,9 +320,19 @@
             }
           }
   
  -        // Create the basic SAX2DTM.
  -        SAX2DTM dtm = new SAX2DTM(this, source, documentID, whiteSpaceFilter,
  -                                  xstringFactory, doIndexing);
  +         SAX2DTM dtm;
  +             if(source==null && unique && !incremental && !doIndexing)
  +             {
  +               // Special case to support RTF construction into shared DTM.
  +               // It should actually still work for other uses,
  +               // but may be slightly deoptimized relative to the base
  +               // to allow it to deal with carrying multiple documents.
  +               dtm = new SAX2RTFDTM(this, source, documentID, 
whiteSpaceFilter,
  +                                    xstringFactory, doIndexing);
  +             }
  +             else // Create the basic SAX2DTM.
  +               dtm = new SAX2DTM(this, source, documentID, whiteSpaceFilter,
  +                                 xstringFactory, doIndexing);
   
           // Go ahead and add the DTM to the lookup table.  This needs to be
           // done before any parsing occurs.
  
  
  
  1.24      +35 -28    
xml-xalan/java/src/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM.java
  
  Index: SAX2DTM.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM.java,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- SAX2DTM.java      13 Dec 2001 21:35:40 -0000      1.23
  +++ SAX2DTM.java      23 Jan 2002 22:53:47 -0000      1.24
  @@ -111,28 +111,39 @@
      * %REVIEW% Should this have an option of being shared across DTMs?
      * Sequentially only; not threadsafe... Currently, I think not.
      *
  -   * %REVIEW%  Initial size should be pushed way down to reduce weight of 
RTFs,
  -   * pending reduction in number of RTFs.
  -   * However, I've got a bug in whitespace normalization to fix first.
  +   * %REVIEW% Initial size was pushed way down to reduce weight of RTFs.
  +   * pending reduction in number of RTF DTMs. Now that we're sharing a DTM
  +   * between RTFs, and tail-pruning... consider going back to the 
larger/faster.
  +   *
  +   * Made protected rather than private so SAX2RTFDTM can access it.
      */
     //private FastStringBuffer m_chars = new FastStringBuffer(13, 13);
  -  private FastStringBuffer m_chars = new FastStringBuffer(5, 13);
  +  protected FastStringBuffer m_chars = new FastStringBuffer(5, 13);
   
  -  /** This vector holds offset and length data. */
  +  /** This vector holds offset and length data.
  +   */
     protected SuballocatedIntVector m_data;
   
  -  /** The parent stack, needed only for construction. */
  -  transient private IntStack m_parents = new IntStack();
  +  /** The parent stack, needed only for construction.
  +   * Made protected rather than private so SAX2RTFDTM can access it.
  +   */
  +  transient protected IntStack m_parents = new IntStack();
   
  -  /** The current previous node, needed only for construction time. */
  -  transient private int m_previous = 0;
  +  /** The current previous node, needed only for construction time.
  +   * Made protected rather than private so SAX2RTFDTM can access it.
  +   */
  +  transient protected int m_previous = 0;
   
  -  /** Namespace support, only relevent at construction time. */
  -  transient private java.util.Vector m_prefixMappings =
  +  /** Namespace support, only relevent at construction time.
  +   * Made protected rather than private so SAX2RTFDTM can access it.
  +   */
  +  transient protected java.util.Vector m_prefixMappings =
       new java.util.Vector();
   
  -  /** Namespace support, only relevent at construction time. */
  -  transient private IntStack m_contextIndexes = new IntStack();
  +  /** Namespace support, only relevent at construction time.
  +   * Made protected rather than private so SAX2RTFDTM can access it.
  +   */
  +  transient protected IntStack m_contextIndexes = new IntStack();
   
     /** Type of next characters() event within text block in prgress. */
     transient private int m_textType = DTM.TEXT_NODE;
  @@ -155,8 +166,10 @@
     /** pool of string values that come as strings. */
     private DTMStringPool m_valuesOrPrefixes = new DTMStringPool();
   
  -  /** End document has been reached. */
  -  private boolean m_endDocumentOccured = false;
  +  /** End document has been reached.
  +   * Made protected rather than private so SAX2RTFDTM can access it.
  +   */
  +  protected boolean m_endDocumentOccured = false;
   
     /** Data or qualified name values, one array element for each node. */
     protected SuballocatedIntVector m_dataOrQName;
  @@ -1140,9 +1153,12 @@
      */
     public XMLString getStringValue(int nodeHandle)
     {
  -
       int identity = makeNodeIdentity(nodeHandle);
  -    int type = _type(identity);
  +    int type;
  +    if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint 
it
  +      type = DTM.NULL;
  +    else
  +      type= _type(identity);
   
       if (isTextType(type))
       {
  @@ -1488,11 +1504,6 @@
     /**
      * Receive notification of the beginning of the document.
      *
  -   * <p>By default, do nothing.  Application writers may override this
  -   * method in a subclass to take specific actions at the beginning
  -   * of a document (such as allocating the root node of a tree or
  -   * creating an output file).</p>
  -   *
      * @throws SAXException Any SAX exception, possibly
      *            wrapping another exception.
      * @see org.xml.sax.ContentHandler#startDocument
  @@ -1516,11 +1527,6 @@
     /**
      * Receive notification of the end of the document.
      *
  -   * <p>By default, do nothing.  Application writers may override this
  -   * method in a subclass to take specific actions at the end
  -   * of a document (such as finalising a tree or closing an output
  -   * file).</p>
  -   *
      * @throws SAXException Any SAX exception, possibly
      *            wrapping another exception.
      * @see org.xml.sax.ContentHandler#endDocument
  @@ -1699,7 +1705,8 @@
       int elemNode = addNode(DTM.ELEMENT_NODE, exName,
                              m_parents.peek(), m_previous, prefixIndex, true);
   
  -    indexNode(exName, elemNode);
  +    if(m_indexing)
  +      indexNode(exName, elemNode);
       
       m_parents.push(elemNode);
   
  
  
  
  1.1                  
xml-xalan/java/src/org/apache/xml/dtm/ref/sax2dtm/SAX2RTFDTM.java
  
  Index: SAX2RTFDTM.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.xml.dtm.ref.sax2dtm;
  
  import java.util.Hashtable;
  import java.util.Vector;
  import javax.xml.transform.Source;
  import javax.xml.transform.SourceLocator;
  import org.apache.xalan.transformer.XalanProperties;
  import org.apache.xalan.res.XSLTErrorResources;
  import org.apache.xalan.res.XSLMessages;
  
  import org.apache.xml.dtm.*;
  import org.apache.xml.dtm.ref.*;
  import org.apache.xml.utils.StringVector;
  import org.apache.xml.utils.IntVector;
  import org.apache.xml.utils.FastStringBuffer;
  import org.apache.xml.utils.IntStack;
  import org.apache.xml.utils.SuballocatedIntVector;
  import org.apache.xml.utils.SystemIDResolver;
  import org.apache.xml.utils.WrappedRuntimeException;
  import org.apache.xml.utils.XMLCharacterRecognizer;
  import org.apache.xml.utils.XMLString;
  import org.apache.xml.utils.XMLStringFactory;
  import org.xml.sax.*;
  import org.xml.sax.ext.*;
  
  /**
   * This is a subclass of SAX2DTM which has been modified to meet the needs of
   * Result Tree Frameworks (RTFs). The differences are:
   *
   * 1) Multiple XML trees may be appended to the single DTM. This means
   * that the root node of each document is _not_ node 0. Some code has
   * had to be deoptimized to support this mode of operation, and an
   * explicit mechanism for obtaining the Node Handle of the root node
   * has been provided.
   *
   * 2) A stack of these documents is maintained, allowing us to "tail-prune" 
the
   * most recently added trees off the end of the DTM as stylesheet elements 
   * (and thus variable contexts) are exited.
   *
   * PLEASE NOTE that this class may be _heavily_ dependent upon the
   * internals of the SAX2DTM superclass, and must be maintained in
   * parallel with that code.  Arguably, they should be conditionals
   * within a single class... but they have deen separated for
   * performance reasons. (In fact, one could even argue about which is
   * the superclass and which is the subclass; the current arrangement
   * is as much about preserving stability of existing code during
   * development as anything else.)
   * 
   * %REVIEW% In fact, since the differences are so minor, I think it
   * may be possible/practical to fold them back into the base
   * SAX2DTM. Consider that as a future code-size optimization.
   * */
  public class SAX2RTFDTM extends SAX2DTM
  {
    /** Set true to monitor SAX events and similar diagnostic info. */
    private static final boolean DEBUG = false;
    
    /** Most recently started Document, or null if the DTM is empty.  */
    private int m_currentDocumentNode=NULL;
    
    /** Tail-pruning mark: Number of nodes in use */
    IntStack mark_size=new IntStack();
    /** Tail-pruning mark: Number of data items in use */
    IntStack mark_data_size=new IntStack();
    /** Tail-pruning mark: Number of size-of-data fields in use */
    IntStack mark_char_size=new IntStack();
    /** Tail-pruning mark: Number of dataOrQName slots in use */
    IntStack mark_doq_size=new IntStack();
    /** Tail-pruning mark: Number of namespace declaration sets in use
     * %REVIEW% I don't think number of NS sets is ever different from number
     * of NS elements. We can probabably reduce these to a single stack and save
     * some storage.
     * */
    IntStack mark_nsdeclset_size=new IntStack();
    /** Tail-pruning mark: Number of naespace declaration elements in use
     * %REVIEW% I don't think number of NS sets is ever different from number
     * of NS elements. We can probabably reduce these to a single stack and save
     * some storage.
     */
    IntStack mark_nsdeclelem_size=new IntStack();
    
    public SAX2RTFDTM(DTMManager mgr, Source source, int dtmIdentity,
                   DTMWSFilter whiteSpaceFilter,
                   XMLStringFactory xstringfactory,
                   boolean doIndexing)
    {
      super(mgr, source, dtmIdentity, whiteSpaceFilter, 
            xstringfactory, doIndexing);
    }
    
    /**
     * Given a DTM, find the owning document node. In the case of
     * SAX2RTFDTM, which may contain multiple documents, this returns
     * the <b>most recently started</b> document, or null if the DTM is
     * empty or no document is currently under construction.
     *
     * %REVIEW% Should we continue to report the most recent after
     * construction has ended? I think not, given that it may have been
     * tail-pruned.
     *
     *  @param nodeHandle the id of the node.
     *  @return int Node handle of Document node, or null if this DTM does not
     *  contain an "active" document.
     * */
    public int getDocument()
    {
      return makeNodeHandle(m_currentDocumentNode);
    }
  
    /**
     * Given a node handle, find the owning document node.  This has the exact
     * same semantics as the DOM Document method of the same name, in that if
     * the nodeHandle is a document node, it will return NULL.
     *
     * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
     * binding layer. Included here as a convenience function and to
     * aid porting of DOM code to DTM.</p>
     *
     * @param nodeHandle the id of the node.
     * @return int Node handle of owning document, or -1 if the nodeHandle is
     *             a document.
     */
    public int getOwnerDocument(int nodeHandle)
    {
      for(int id=makeNodeIdentity(nodeHandle);
        id!=NULL;
        id=_parent(id))
        if(_type(id)==DTM.DOCUMENT_NODE)
        return id;
  
      return DTM.NULL;
    }
    
    /**
     * Receive notification of the beginning of a new RTF document.
     *
     * %REVIEW% Y'know, this isn't all that much of a deoptimization. We
     * might want to consider folding the start/endDocument changes back
     * into the main SAX2DTM so we don't have to expose so many fields
     * (even as Protected) and carry the additional code.
     *
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception. 
     * @see org.xml.sax.ContentHandler#startDocument
     * */
    public void startDocument() throws SAXException
    {
      // Re-initialize the tree append process
      m_endDocumentOccured = false;
      m_prefixMappings = new java.util.Vector();
      m_contextIndexes = new IntStack();
      m_parents = new IntStack();
      
      m_currentDocumentNode=m_size;
      super.startDocument();
    }
    
    /**
     * Receive notification of the end of the document.
     *
     * %REVIEW% Y'know, this isn't all that much of a deoptimization. We
     * might want to consider folding the start/endDocument changes back
     * into the main SAX2DTM so we don't have to expose so many fields
     * (even as Protected).
     *
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see org.xml.sax.ContentHandler#endDocument
     * */
    public void endDocument() throws SAXException
    {
      charactersFlush();
  
      m_nextsib.setElementAt(NULL,m_currentDocumentNode);
  
      if (m_firstch.elementAt(m_currentDocumentNode) == NOTPROCESSED)
        m_firstch.setElementAt(NULL,m_currentDocumentNode);
  
      if (DTM.NULL != m_previous)
        m_nextsib.setElementAt(DTM.NULL,m_previous);
  
      m_parents = null;
      m_prefixMappings = null;
      m_contextIndexes = null;
  
      m_currentDocumentNode= NULL; // no longer open
      m_endDocumentOccured = true;
    }
    
  
    /** "Tail-pruning" support for RTFs.
     * 
     * This function pushes information about the current size of the
     * DTM's data structures onto a stack, for use by popRewindMark()
     * (which see).
     * 
     * %REVIEW% I have no idea how to rewind m_elemIndexes. However,
     * RTFs will not be indexed, so I can simply panic if that case
     * arises. Hey, it works...
     * */
    public void pushRewindMark()
    {
      if(m_indexing || m_elemIndexes!=null) 
        throw new java.lang.NullPointerException("Coding error; Don't try to 
mark/rewind an indexed DTM");
  
      // Values from DTMDefaultBase
      // %REVIEW% Can the namespace stack sizes ever differ? If not, save space!
      mark_size.push(m_size);
      mark_nsdeclset_size.push( (m_namespaceDeclSets==null) ? 0 : 
m_namespaceDeclSets.size() );
      mark_nsdeclelem_size.push( (m_namespaceDeclSetElements==null) ? 0 : 
m_namespaceDeclSetElements.size() );
      
      // Values from SAX2DTM
      mark_data_size.push(m_data.size());
      mark_char_size.push(m_chars.size());
      mark_doq_size.push(m_dataOrQName.size()); 
    }
    
    /** "Tail-pruning" support for RTFs.
     * 
     * This function pops the information previously saved by
     * pushRewindMark (which see) and uses it to discard all nodes added
     * to the DTM after that time. We expect that this will allow us to
     * reuse storage more effectively.
     * 
     * This is _not_ intended to be called while a document is still being
     * constructed -- only between endDocument and the next startDocument
     * 
     * %REVIEW% WARNING: This is the first use of some of the truncation
     * methods.  If Xalan blows up after this is called, that's a likely
     * place to check.
     * 
     * %REVIEW% Our original design for DTMs permitted them to share
     * string pools.  If there any risk that this might be happening, we
     * can _not_ rewind and recover the string storage. One solution
     * might to assert that DTMs used for RTFs Must Not take advantage
     * of that feature, but this seems excessively fragile. Another, much
     * less attractive, would be to just let them leak... Nah.
     * 
     * @return true if and only if the pop completely emptied the
     * RTF. That response is used when determining how to unspool
     * RTF-started-while-RTF-open situations.
     * */
    public boolean popRewindMark()
    {
      boolean top=mark_size.empty();
      
      m_size=top ? 0 : mark_size.pop();
      m_exptype.setSize(m_size);
      m_firstch.setSize(m_size);
      m_nextsib.setSize(m_size);
      m_prevsib.setSize(m_size);
      m_parent.setSize(m_size);
  
      m_elemIndexes=null;
  
      int ds= top ? 0 : mark_nsdeclset_size.pop();
      if (m_namespaceDeclSets!=null)
        m_namespaceDeclSets.setSize(ds);
        
      int ds1= top ? 0 : mark_nsdeclelem_size.pop();
      if (m_namespaceDeclSetElements!=null)
        m_namespaceDeclSetElements.setSize(ds1);
    
      // Values from SAX2DTM
      m_data.setSize(top ? 0 : mark_data_size.pop());
      m_chars.setLength(top ? 0 : mark_char_size.pop());
      m_dataOrQName.setSize(top ? 0 : mark_doq_size.pop());
  
      // Return true iff DTM now empty
      return m_size==0;
    }
    
    /** @return true if a DTM tree is currently under construction.
     * */
    public boolean isTreeIncomplete()
    {
        return !m_endDocumentOccured;
        
    }
  }
  
  
  
  1.32      +123 -1    xml-xalan/java/src/org/apache/xpath/XPathContext.java
  
  Index: XPathContext.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/java/src/org/apache/xpath/XPathContext.java,v
  retrieving revision 1.31
  retrieving revision 1.32
  diff -u -r1.31 -r1.32
  --- XPathContext.java 11 Dec 2001 17:13:31 -0000      1.31
  +++ XPathContext.java 23 Jan 2002 22:53:47 -0000      1.32
  @@ -61,6 +61,7 @@
   import java.io.IOException;
   
   import java.util.Stack;
  +import java.util.Vector;
   
   import java.lang.reflect.Method;
   
  @@ -112,6 +113,9 @@
   
   import org.apache.xpath.axes.DescendantIterator;
   
  +// For RTF handling.
  +import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
  +
   /**
    * <meta name="usage" content="advanced"/>
    * Default class for the runtime execution context for XPath.
  @@ -121,6 +125,21 @@
   public class XPathContext extends DTMManager // implements ExpressionContext
   {
     /**
  +   * Stack of cached "reusable" DTMs for Result Tree Fragments.
  +   * This is a kluge to handle the problem of starting an RTF before
  +   * the old one is complete.
  +   * 
  +   * %REVIEW% I'm using a Vector rather than Stack so we can reuse
  +   * the DTMs if the problem occurs multiple times. I'm not sure that's
  +   * really a net win versus discarding the DTM and starting a new one...
  +   * but the retained RTF DTM will have been tail-pruned so should be small.
  +   */
  +  private Vector m_rtfdtm_stack=null;
  +  /** Index of currently active RTF DTM in m_rtfdtm_stack */
  +  private int m_which_rtfdtm=-1;
  +  
  +     
  +  /**
      * Though XPathContext context extends 
      * the DTMManager, it really is a proxy for this object, which 
      * is the real DTMManager.
  @@ -181,7 +200,7 @@
     {
       return m_dtmManager.getDTM(nodeHandle);
     }
  -//  
  +
     /**
      * Given a W3C DOM node, try and return a DTM handle.
      * Note: calling this may be non-optimal.
  @@ -224,6 +243,16 @@
      */
     public boolean release(DTM dtm, boolean shouldHardDelete)
     {
  +    // %REVIEW% If it's a DTM which may contain multiple Result Tree
  +    // Fragments, we can't discard it unless we know not only that it
  +    // is empty, but that the XPathContext itself is going away. So do
  +    // _not_ accept the request. (May want to do it as part of
  +    // reset(), though.)
  +    if(m_rtfdtm_stack.contains(dtm))
  +    {
  +      return false;
  +    }
  +     
       return m_dtmManager.release(dtm, shouldHardDelete);
     }
   
  @@ -336,6 +365,11 @@
      */
     public void reset()
     {
  +     // These couldn't be disposed of earlier (see comments in release()); 
zap them now.
  +     if(m_rtfdtm_stack!=null)
  +              for (java.util.Enumeration e = m_rtfdtm_stack.elements() ; 
e.hasMoreElements() ;) 
  +                     m_dtmManager.release((DTM)e.nextElement(), true);
  +     
       m_dtmManager = DTMManager.newInstance(
                      
org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
     }
  @@ -1149,4 +1183,92 @@
   
     }
   
  +  /**
  +   * Get a DTM to be used as a container for a Result Tree
  +   * Fragment. This will always be an instance of (derived from? equivalent 
to?) 
  +   * SAX2DTM, since each RTF is constructed by temporarily redirecting our 
SAX 
  +   * output to it. It may be a single DTM containing for multiple fragments, 
  +   * if the implementation supports that.
  +   * 
  +   * @return a non-null DTM reference.
  +   */
  +  public DTM getRTFDTM()
  +  {
  +     SAX2RTFDTM rtfdtm;
  +
  +     // We probably should _NOT_ be applying whitespace filtering at this 
stage!
  +     //
  +     // Some magic has been applied in DTMManagerDefault to recognize this 
set of options
  +     // and generate an instance of DTM which can contain multiple documents
  +     // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, 
but
  +     // I didn't want to change the manager API at this time, or expose 
  +     // too many dependencies on its internals. (Ideally, I'd like to move
  +     // isTreeIncomplete all the way up to DTM, so we wouldn't need to 
explicitly
  +     // specify the subclass here.)
  +
  +     if(m_rtfdtm_stack==null)
  +     {
  +             m_rtfdtm_stack=new Vector();
  +             
rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
  +             m_rtfdtm_stack.add(rtfdtm);
  +             ++m_which_rtfdtm;
  +     }
  +     else
  +     {
  +             rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
  +             
  +             // It might already be under construction -- the classic 
example would be
  +             // an xsl:variable which uses xsl:call-template as part of its 
value. To
  +             // handle this recursion, we have to start a new RTF DTM, 
pushing the old
  +             // one onto a stack so we can return to it. It is hoped that
  +             // this is an uncommon case!
  +             if(rtfdtm.isTreeIncomplete())
  +             {
  +                     if(++m_which_rtfdtm < m_rtfdtm_stack.size())
  +                             
rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
  +                     else
  +                     {
  +                             
rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
  +                             m_rtfdtm_stack.add(rtfdtm);             
  +                     }
  +             }
  +     }
  +    return rtfdtm;
  +  }
  +  
  +  /** Push the RTFDTM's context mark, to allows discarding RTFs added after 
this
  +   * point. (If it doesn't exist we don't push, since we might still be able 
to 
  +   * get away with not creating it. That requires that excessive pops be 
harmless.)
  +   * */
  +  public void pushRTFContext()
  +  {
  +     if(null!=m_rtfdtm_stack)
  +             ((SAX2RTFDTM)(getRTFDTM())).pushRewindMark();
  +  }
  +  
  +  /** Pop the RTFDTM's context mark. This discards any RTFs added after the 
last
  +   * mark was set. 
  +   * 
  +   * If there is no RTF DTM, there's nothing to pop so this
  +   * becomes a no-op. If pushes were issued before this was called, we count 
on
  +   * the fact that popRewindMark is defined such that overpopping just resets
  +   * to empty.
  +   * 
  +   * Complicating factor: We need to handle the case of popping back to a 
previous
  +   * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose.
  +   * Basically: If pop says this DTM is now empty, then return to the 
previous
  +   * if one exists, in whatever state we left it in. UGLY, but hopefully the
  +   * situation which forces us to consider this will arise exceedingly 
rarely.
  +   * */
  +  public void popRTFContext()
  +  {
  +     if(null==m_rtfdtm_stack)
  +             return;
  +             
  +     boolean 
isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark();
  +     if(isEmpty && m_which_rtfdtm>0)
  +     {
  +             --m_which_rtfdtm;
  +     }
  +  }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to