/** Reset for new run. */ public void reset() { releaseDTMXRTreeFrags(); // 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_rtfdtm_stack = null; // drop our references too m_which_rtfdtm = -1; if (m_global_rtfdtm != null) m_dtmManager.release(m_global_rtfdtm, true); m_global_rtfdtm = null; m_dtmManager = DTMManager.newInstance(org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); m_saxLocations.removeAllElements(); m_axesIteratorStack.removeAllElements(); m_contextNodeLists.removeAllElements(); m_currentExpressionNodes.removeAllElements(); m_currentNodes.removeAllElements(); m_iteratorRoots.RemoveAllNoClear(); m_predicatePos.removeAllElements(); m_predicateRoots.RemoveAllNoClear(); m_prefixResolvers.removeAllElements(); m_prefixResolvers.push(null); m_currentNodes.push(DTM.NULL); m_currentExpressionNodes.push(DTM.NULL); m_saxLocations.push(null); }
/** * Default class for the runtime execution context for XPath. * * <p>This class extends DTMManager but does not directly implement it. * * @xsl.usage advanced */ public class XPathContext extends DTMManager // implements ExpressionContext { IntStack m_last_pushed_rtfdtm = new IntStack(); /** * 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. * * <p>%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; /** * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is required since we're * never going to pop these. */ private SAX2RTFDTM m_global_rtfdtm = null; /** * HashMap of cached the DTMXRTreeFrag objects, which are identified by DTM IDs. The object are * just wrappers for DTMs which are used in XRTreeFrag. */ private HashMap m_DTMXRTreeFrags = null; /** state of the secure processing feature. */ private boolean m_isSecureProcessing = false; /** * Though XPathContext context extends the DTMManager, it really is a proxy for this object, which * is the real DTMManager. */ protected DTMManager m_dtmManager = DTMManager.newInstance(org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); /** * Return the DTMManager object. Though XPathContext context extends the DTMManager, it really is * a proxy for the real DTMManager. If a caller needs to make a lot of calls to the DTMManager, it * is faster if it gets the real one from this function. */ public DTMManager getDTMManager() { return m_dtmManager; } /** Set the state of the secure processing feature */ public void setSecureProcessing(boolean flag) { m_isSecureProcessing = flag; } /** Return the state of the secure processing feature */ public boolean isSecureProcessing() { return m_isSecureProcessing; } /** * Get an instance of a DTM, loaded with the content from the specified source. If the unique flag * is true, a new instance will 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). * * @param source the specification of the source object, which may be null, in which case it is * assumed that node construction will take by some other means. * @param unique true if the returned DTM must be unique, probably because it is going to be * mutated. * @param wsfilter Enables filtering of whitespace nodes, and may be null. * @param incremental true if the construction should try and be incremental. * @param doIndexing true if the caller considers it worth it to use indexing schemes. * @return a non-null DTM reference. */ public DTM getDTM( javax.xml.transform.Source source, boolean unique, DTMWSFilter wsfilter, boolean incremental, boolean doIndexing) { return m_dtmManager.getDTM(source, unique, wsfilter, incremental, doIndexing); } /** * Get an instance of a DTM that "owns" a node handle. * * @param nodeHandle the nodeHandle. * @return a non-null DTM reference. */ public DTM getDTM(int nodeHandle) { return m_dtmManager.getDTM(nodeHandle); } /** * Given a W3C DOM node, try and return a DTM handle. Note: calling this may be non-optimal. * * @param node Non-null reference to a DOM node. * @return a valid DTM handle. */ public int getDTMHandleFromNode(org.w3c.dom.Node node) { return m_dtmManager.getDTMHandleFromNode(node); } // // /** %TBD% Doc */ public int getDTMIdentity(DTM dtm) { return m_dtmManager.getDTMIdentity(dtm); } // /** * Creates an empty <code>DocumentFragment</code> object. * * @return A new <code>DocumentFragment handle</code>. */ public DTM createDocumentFragment() { return m_dtmManager.createDocumentFragment(); } // /** * Release a DTM either to a lru pool, or completely remove reference. DTMs without system IDs are * always hard deleted. State: experimental. * * @param dtm The DTM to be released. * @param shouldHardDelete True if the DTM should be removed no matter what. * @return true if the DTM was removed, false if it was put back in a lru pool. */ 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 != null && m_rtfdtm_stack.contains(dtm)) { return false; } return m_dtmManager.release(dtm, shouldHardDelete); } /** * Create a new <code>DTMIterator</code> based on an XPath <a * href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or a <a * href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. * * @param xpathCompiler ??? Somehow we need to pass in a subpart of the expression. I hate to do * this with strings, since the larger expression has already been parsed. * @param pos The position in the expression. * @return The newly created <code>DTMIterator</code>. */ public DTMIterator createDTMIterator(Object xpathCompiler, int pos) { return m_dtmManager.createDTMIterator(xpathCompiler, pos); } // /** * Create a new <code>DTMIterator</code> based on an XPath <a * href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or a <a * href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. * * @param xpathString Must be a valid string expressing a <a * href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or a <a * href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. * @param presolver An object that can resolve prefixes to namespace URLs. * @return The newly created <code>DTMIterator</code>. */ public DTMIterator createDTMIterator(String xpathString, PrefixResolver presolver) { return m_dtmManager.createDTMIterator(xpathString, presolver); } // /** * Create a new <code>DTMIterator</code> based only on a whatToShow and a DTMFilter. The traversal * semantics are defined as the descendant access. * * @param whatToShow This flag specifies which node types may appear in the logical view of the * tree presented by the iterator. See the description of <code>NodeFilter</code> for the set * of possible <code>SHOW_</code> values.These flags can be combined using <code>OR</code>. * @param filter The <code>NodeFilter</code> to be used with this <code>TreeWalker</code>, or * <code>null</code> to indicate no filter. * @param entityReferenceExpansion The value of this flag determines whether entity reference * nodes are expanded. * @return The newly created <code>NodeIterator</code>. */ public DTMIterator createDTMIterator( int whatToShow, DTMFilter filter, boolean entityReferenceExpansion) { return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion); } /** * Create a new <code>DTMIterator</code> that holds exactly one node. * * @param node The node handle that the DTMIterator will iterate to. * @return The newly created <code>DTMIterator</code>. */ public DTMIterator createDTMIterator(int node) { // DescendantIterator iter = new DescendantIterator(); DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(Axis.SELF); iter.setRoot(node, this); return iter; // return m_dtmManager.createDTMIterator(node); } /** * Create an XPathContext instance. This is equivalent to calling the {@link * #XPathContext(boolean)} constructor with the value <code>true</code>. */ public XPathContext() { this(true); } /** * Create an XPathContext instance. * * @param recursiveVarContext A <code>boolean</code> value indicating whether the XPath context * needs to support pushing of scopes for variable resolution */ public XPathContext(boolean recursiveVarContext) { m_prefixResolvers.push(null); m_currentNodes.push(DTM.NULL); m_currentExpressionNodes.push(DTM.NULL); m_saxLocations.push(null); m_variableStacks = recursiveVarContext ? new VariableStack() : new VariableStack(1); } /** * Create an XPathContext instance. This is equivalent to calling the constructor {@link * #XPathContext(java.lang.Object,boolean)} with the value of the second parameter set to <code> * true</code>. * * @param owner Value that can be retrieved via the getOwnerObject() method. * @see #getOwnerObject */ public XPathContext(Object owner) { this(owner, true); } /** * Create an XPathContext instance. * * @param owner Value that can be retrieved via the getOwnerObject() method. * @see #getOwnerObject * @param recursiveVarContext A <code>boolean</code> value indicating whether the XPath context * needs to support pushing of scopes for variable resolution */ public XPathContext(Object owner, boolean recursiveVarContext) { this(recursiveVarContext); m_owner = owner; try { m_ownerGetErrorListener = m_owner.getClass().getMethod("getErrorListener", new Class[] {}); } catch (NoSuchMethodException nsme) { } } /** Reset for new run. */ public void reset() { releaseDTMXRTreeFrags(); // 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_rtfdtm_stack = null; // drop our references too m_which_rtfdtm = -1; if (m_global_rtfdtm != null) m_dtmManager.release(m_global_rtfdtm, true); m_global_rtfdtm = null; m_dtmManager = DTMManager.newInstance(org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); m_saxLocations.removeAllElements(); m_axesIteratorStack.removeAllElements(); m_contextNodeLists.removeAllElements(); m_currentExpressionNodes.removeAllElements(); m_currentNodes.removeAllElements(); m_iteratorRoots.RemoveAllNoClear(); m_predicatePos.removeAllElements(); m_predicateRoots.RemoveAllNoClear(); m_prefixResolvers.removeAllElements(); m_prefixResolvers.push(null); m_currentNodes.push(DTM.NULL); m_currentExpressionNodes.push(DTM.NULL); m_saxLocations.push(null); } /** The current stylesheet locator. */ ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT); /** * Set the current locater in the stylesheet. * * @param location The location within the stylesheet. */ public void setSAXLocator(SourceLocator location) { m_saxLocations.setTop(location); } /** * Set the current locater in the stylesheet. * * @param location The location within the stylesheet. */ public void pushSAXLocator(SourceLocator location) { m_saxLocations.push(location); } /** Push a slot on the locations stack so that setSAXLocator can be repeatedly called. */ public void pushSAXLocatorNull() { m_saxLocations.push(null); } /** Pop the current locater. */ public void popSAXLocator() { m_saxLocations.pop(); } /** * Get the current locater in the stylesheet. * * @return The location within the stylesheet, or null if not known. */ public SourceLocator getSAXLocator() { return (SourceLocator) m_saxLocations.peek(); } /** * The owner context of this XPathContext. In the case of XSLT, this will be a Transformer object. */ private Object m_owner; /** * The owner context of this XPathContext. In the case of XSLT, this will be a Transformer object. */ private Method m_ownerGetErrorListener; /** * Get the "owner" context of this context, which should be, in the case of XSLT, the Transformer * object. This is needed so that XSLT functions can get the Transformer. * * @return The owner object passed into the constructor, or null. */ public Object getOwnerObject() { return m_owner; } // ================ VarStack =================== /** * The stack of Variable stacks. A VariableStack will be pushed onto this stack for each template * invocation. */ private VariableStack m_variableStacks; /** * Get the variable stack, which is in charge of variables and parameters. * * @return the variable stack, which should not be null. */ public final VariableStack getVarStack() { return m_variableStacks; } /** * Get the variable stack, which is in charge of variables and parameters. * * @param varStack non-null reference to the variable stack. */ public final void setVarStack(VariableStack varStack) { m_variableStacks = varStack; } // ================ SourceTreeManager =================== /** The source tree manager, which associates Source objects to source tree nodes. */ private SourceTreeManager m_sourceTreeManager = new SourceTreeManager(); /** * Get the SourceTreeManager associated with this execution context. * * @return the SourceTreeManager associated with this execution context. */ public final SourceTreeManager getSourceTreeManager() { return m_sourceTreeManager; } /** * Set the SourceTreeManager associated with this execution context. * * @param mgr the SourceTreeManager to be associated with this execution context. */ public void setSourceTreeManager(SourceTreeManager mgr) { m_sourceTreeManager = mgr; } // ================================================= /** The ErrorListener where errors and warnings are to be reported. */ private ErrorListener m_errorListener; /** * A default ErrorListener in case our m_errorListener was not specified and our owner either does * not have an ErrorListener or has a null one. */ private ErrorListener m_defaultErrorListener; /** * Get the ErrorListener where errors and warnings are to be reported. * * @return A non-null ErrorListener reference. */ public final ErrorListener getErrorListener() { if (null != m_errorListener) return m_errorListener; ErrorListener retval = null; try { if (null != m_ownerGetErrorListener) retval = (ErrorListener) m_ownerGetErrorListener.invoke(m_owner, new Object[] {}); } catch (Exception e) { } if (null == retval) { if (null == m_defaultErrorListener) m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler(); retval = m_defaultErrorListener; } return retval; } /** * Set the ErrorListener where errors and warnings are to be reported. * * @param listener A non-null ErrorListener reference. */ public void setErrorListener(ErrorListener listener) throws IllegalArgumentException { if (listener == null) throw new IllegalArgumentException( XSLMessages.createXPATHMessage( XPATHErrorResources.ER_NULL_ERROR_HANDLER, null)); // "Null error handler"); m_errorListener = listener; } // ================================================= /** * The TrAX URI Resolver for resolving URIs from the document(...) function to source tree nodes. */ private URIResolver m_uriResolver; /** * Get the URIResolver associated with this execution context. * * @return a URI resolver, which may be null. */ public final URIResolver getURIResolver() { return m_uriResolver; } /** * Set the URIResolver associated with this execution context. * * @param resolver the URIResolver to be associated with this execution context, may be null to * clear an already set resolver. */ public void setURIResolver(URIResolver resolver) { m_uriResolver = resolver; } // ================================================= /** The reader of the primary source tree. */ public XMLReader m_primaryReader; /** * Get primary XMLReader associated with this execution context. * * @return The reader of the primary source tree. */ public final XMLReader getPrimaryReader() { return m_primaryReader; } /** * Set primary XMLReader associated with this execution context. * * @param reader The reader of the primary source tree. */ public void setPrimaryReader(XMLReader reader) { m_primaryReader = reader; } // ================================================= /** Misnamed string manager for XPath messages. */ // private static XSLMessages m_XSLMessages = new XSLMessages(); /** * Tell the user of an assertion error, and probably throw an exception. * * @param b If false, a TransformerException will be thrown. * @param msg The assertion message, which should be informative. * @throws javax.xml.transform.TransformerException if b is false. */ private void assertion(boolean b, String msg) throws javax.xml.transform.TransformerException { if (!b) { ErrorListener errorHandler = getErrorListener(); if (errorHandler != null) { errorHandler.fatalError( new TransformerException( XSLMessages.createMessage( XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, new Object[] {msg}), (SAXSourceLocator) this.getSAXLocator())); } } } // ========================================================== // SECTION: Execution context state tracking // ========================================================== /** The current context node list. */ private Stack m_contextNodeLists = new Stack(); public Stack getContextNodeListsStack() { return m_contextNodeLists; } public void setContextNodeListsStack(Stack s) { m_contextNodeLists = s; } /** * Get the current context node list. * * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, * also refered to here as a <term>context node list</term>. */ public final DTMIterator getContextNodeList() { if (m_contextNodeLists.size() > 0) return (DTMIterator) m_contextNodeLists.peek(); else return null; } /** * Set the current context node list. * * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, * also refered to here as a <term>context node list</term>. * @xsl.usage internal */ public final void pushContextNodeList(DTMIterator nl) { m_contextNodeLists.push(nl); } /** * Pop the current context node list. * * @xsl.usage internal */ public final void popContextNodeList() { if (m_contextNodeLists.isEmpty()) System.err.println("Warning: popContextNodeList when stack is empty!"); else m_contextNodeLists.pop(); } /** The ammount to use for stacks that record information during the recursive execution. */ public static final int RECURSIONLIMIT = (1024 * 4); /** * The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects. Not * to be confused with the current node list. %REVIEW% Note that there are no bounds check and * resize for this stack, so if it is blown, it's all over. */ private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT); // private NodeVector m_currentNodes = new NodeVector(); public IntStack getCurrentNodeStack() { return m_currentNodes; } public void setCurrentNodeStack(IntStack nv) { m_currentNodes = nv; } /** * Get the current context node. * * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. */ public final int getCurrentNode() { return m_currentNodes.peek(); } /** * Set the current context node and expression node. * * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. * @param en the sub-expression context node. */ public final void pushCurrentNodeAndExpression(int cn, int en) { m_currentNodes.push(cn); m_currentExpressionNodes.push(cn); } /** Set the current context node. */ public final void popCurrentNodeAndExpression() { m_currentNodes.quickPop(1); m_currentExpressionNodes.quickPop(1); } /** * Push the current context node, expression node, and prefix resolver. * * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. * @param en the sub-expression context node. * @param nc the namespace context (prefix resolver. */ public final void pushExpressionState(int cn, int en, PrefixResolver nc) { m_currentNodes.push(cn); m_currentExpressionNodes.push(cn); m_prefixResolvers.push(nc); } /** Pop the current context node, expression node, and prefix resolver. */ public final void popExpressionState() { m_currentNodes.quickPop(1); m_currentExpressionNodes.quickPop(1); m_prefixResolvers.pop(); } /** * Set the current context node. * * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. */ public final void pushCurrentNode(int n) { m_currentNodes.push(n); } /** Pop the current context node. */ public final void popCurrentNode() { m_currentNodes.quickPop(1); } /** Set the current predicate root. */ public final void pushPredicateRoot(int n) { m_predicateRoots.push(n); } /** Pop the current predicate root. */ public final void popPredicateRoot() { m_predicateRoots.popQuick(); } /** Get the current predicate root. */ public final int getPredicateRoot() { return m_predicateRoots.peepOrNull(); } /** Set the current location path iterator root. */ public final void pushIteratorRoot(int n) { m_iteratorRoots.push(n); } /** Pop the current location path iterator root. */ public final void popIteratorRoot() { m_iteratorRoots.popQuick(); } /** Get the current location path iterator root. */ public final int getIteratorRoot() { return m_iteratorRoots.peepOrNull(); } /** A stack of the current sub-expression nodes. */ private NodeVector m_iteratorRoots = new NodeVector(); /** A stack of the current sub-expression nodes. */ private NodeVector m_predicateRoots = new NodeVector(); /** A stack of the current sub-expression nodes. */ private IntStack m_currentExpressionNodes = new IntStack(RECURSIONLIMIT); public IntStack getCurrentExpressionNodeStack() { return m_currentExpressionNodes; } public void setCurrentExpressionNodeStack(IntStack nv) { m_currentExpressionNodes = nv; } private IntStack m_predicatePos = new IntStack(); public final int getPredicatePos() { return m_predicatePos.peek(); } public final void pushPredicatePos(int n) { m_predicatePos.push(n); } public final void popPredicatePos() { m_predicatePos.pop(); } /** * Get the current node that is the expression's context (i.e. for current() support). * * @return The current sub-expression node. */ public final int getCurrentExpressionNode() { return m_currentExpressionNodes.peek(); } /** * Set the current node that is the expression's context (i.e. for current() support). * * @param n The sub-expression node to be current. */ public final void pushCurrentExpressionNode(int n) { m_currentExpressionNodes.push(n); } /** Pop the current node that is the expression's context (i.e. for current() support). */ public final void popCurrentExpressionNode() { m_currentExpressionNodes.quickPop(1); } private ObjectStack m_prefixResolvers = new ObjectStack(RECURSIONLIMIT); /** * Get the current namespace context for the xpath. * * @return the current prefix resolver for resolving prefixes to namespace URLs. */ public final PrefixResolver getNamespaceContext() { return (PrefixResolver) m_prefixResolvers.peek(); } /** * Get the current namespace context for the xpath. * * @param pr the prefix resolver to be used for resolving prefixes to namespace URLs. */ public final void setNamespaceContext(PrefixResolver pr) { m_prefixResolvers.setTop(pr); } /** * Push a current namespace context for the xpath. * * @param pr the prefix resolver to be used for resolving prefixes to namespace URLs. */ public final void pushNamespaceContext(PrefixResolver pr) { m_prefixResolvers.push(pr); } /** * Just increment the namespace contest stack, so that setNamespaceContext can be used on the * slot. */ public final void pushNamespaceContextNull() { m_prefixResolvers.push(null); } /** Pop the current namespace context for the xpath. */ public final void popNamespaceContext() { m_prefixResolvers.pop(); } // ========================================================== // SECTION: Current TreeWalker contexts (for internal use) // ========================================================== /** Stack of AxesIterators. */ private Stack m_axesIteratorStack = new Stack(); public Stack getAxesIteratorStackStacks() { return m_axesIteratorStack; } public void setAxesIteratorStackStacks(Stack s) { m_axesIteratorStack = s; } /** * Push a TreeWalker on the stack. * * @param iter A sub-context AxesWalker. * @xsl.usage internal */ public final void pushSubContextList(SubContextList iter) { m_axesIteratorStack.push(iter); } /** * Pop the last pushed axes iterator. * * @xsl.usage internal */ public final void popSubContextList() { m_axesIteratorStack.pop(); } /** * Get the current axes iterator, or return null if none. * * @return the sub-context node list. * @xsl.usage internal */ public SubContextList getSubContextList() { return m_axesIteratorStack.isEmpty() ? null : (SubContextList) m_axesIteratorStack.peek(); } /** * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a> as * defined by the XSLT spec. * * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>. * @xsl.usage internal */ public org.apache.xpath.axes.SubContextList getCurrentNodeList() { return m_axesIteratorStack.isEmpty() ? null : (SubContextList) m_axesIteratorStack.elementAt(0); } // ========================================================== // SECTION: Implementation of ExpressionContext interface // ========================================================== /** * Get the current context node. * * @return The current context node. */ public final int getContextNode() { return this.getCurrentNode(); } /** * Get the current context node list. * * @return An iterator for the current context list, as defined in XSLT. */ public final DTMIterator getContextNodes() { try { DTMIterator cnl = getContextNodeList(); if (null != cnl) return cnl.cloneWithReset(); else return null; // for now... this might ought to be an empty iterator. } catch (CloneNotSupportedException cnse) { return null; // error reporting? } } XPathExpressionContext expressionContext = new XPathExpressionContext(); /** * The the expression context for extensions for this context. * * @return An object that implements the ExpressionContext. */ public ExpressionContext getExpressionContext() { return expressionContext; } public class XPathExpressionContext implements ExpressionContext { /** * Return the XPathContext associated with this XPathExpressionContext. Extensions should use * this judiciously and only when special processing requirements cannot be met another way. * Consider requesting an enhancement to the ExpressionContext interface to avoid having to call * this method. * * @return the XPathContext associated with this XPathExpressionContext. */ public XPathContext getXPathContext() { return XPathContext.this; } /** * Return the DTMManager object. Though XPathContext context extends the DTMManager, it really * is a proxy for the real DTMManager. If a caller needs to make a lot of calls to the * DTMManager, it is faster if it gets the real one from this function. */ public DTMManager getDTMManager() { return m_dtmManager; } /** * Get the current context node. * * @return The current context node. */ public org.w3c.dom.Node getContextNode() { int context = getCurrentNode(); return getDTM(context).getNode(context); } /** * Get the current context node list. * * @return An iterator for the current context list, as defined in XSLT. */ public org.w3c.dom.traversal.NodeIterator getContextNodes() { return new org.apache.xml.dtm.ref.DTMNodeIterator(getContextNodeList()); } /** * Get the error listener. * * @return The registered error listener. */ public ErrorListener getErrorListener() { return XPathContext.this.getErrorListener(); } /** * Get the value of a node as a number. * * @param n Node to be converted to a number. May be null. * @return value of n as a number. */ public double toNumber(org.w3c.dom.Node n) { // %REVIEW% You can't get much uglier than this... int nodeHandle = getDTMHandleFromNode(n); DTM dtm = getDTM(nodeHandle); XString xobj = (XString) dtm.getStringValue(nodeHandle); return xobj.num(); } /** * Get the value of a node as a string. * * @param n Node to be converted to a string. May be null. * @return value of n as a string, or an empty string if n is null. */ public String toString(org.w3c.dom.Node n) { // %REVIEW% You can't get much uglier than this... int nodeHandle = getDTMHandleFromNode(n); DTM dtm = getDTM(nodeHandle); XMLString strVal = dtm.getStringValue(nodeHandle); return strVal.toString(); } /** * Get a variable based on it's qualified name. * * @param qname The qualified name of the variable. * @return The evaluated value of the variable. * @throws javax.xml.transform.TransformerException */ public final XObject getVariableOrParam(org.apache.xml.utils.QName qname) throws javax.xml.transform.TransformerException { return m_variableStacks.getVariableOrParam(XPathContext.this, qname); } } /** * Get a DTM to be used as a container for a global 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. * * <p>Note: The distinction between this method and getRTFDTM() is that the latter allocates space * from the dynamic variable stack (m_rtfdtm_stack), which may be pruned away again as the * templates which defined those variables are exited. Global variables may be bound late (see * XUnresolvedVariable), and never want to be discarded, hence we need to allocate them separately * and don't actually need a stack to track them. * * @return a non-null DTM reference. */ public DTM getGlobalRTFDTM() { // 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 it doesn't exist, or if the one already existing is in the middle of // being constructed, we need to obtain a new DTM to write into. I'm not sure // the latter will ever arise, but I'd rather be just a bit paranoid.. if (m_global_rtfdtm == null || m_global_rtfdtm.isTreeIncomplete()) { m_global_rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null, true, null, false, false); } return m_global_rtfdtm; } /** * Get a DTM to be used as a container for a dynamic 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.addElement(rtfdtm); ++m_which_rtfdtm; } else if (m_which_rtfdtm < 0) { rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack.elementAt(++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. This is not as uncommon a case // as we might wish, unfortunately, as some folks insist on coding XSLT // as if it were a procedural language... 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.addElement(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() { m_last_pushed_rtfdtm.push(m_which_rtfdtm); 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. * * <p>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. * * <p>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() { int previous = m_last_pushed_rtfdtm.pop(); if (null == m_rtfdtm_stack) return; if (m_which_rtfdtm == previous) { if (previous >= 0) // guard against none-active { boolean isEmpty = ((SAX2RTFDTM) (m_rtfdtm_stack.elementAt(previous))).popRewindMark(); } } else while (m_which_rtfdtm != previous) { // Empty each DTM before popping, so it's ready for reuse // _DON'T_ pop the previous, since it's still open (which is why we // stacked up more of these) and did not receive a mark. boolean isEmpty = ((SAX2RTFDTM) (m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark(); --m_which_rtfdtm; } } /** * Gets DTMXRTreeFrag object if one has already been created. Creates new DTMXRTreeFrag object and * adds to m_DTMXRTreeFrags HashMap, otherwise. * * @param dtmIdentity * @return DTMXRTreeFrag */ public DTMXRTreeFrag getDTMXRTreeFrag(int dtmIdentity) { if (m_DTMXRTreeFrags == null) { m_DTMXRTreeFrags = new HashMap(); } if (m_DTMXRTreeFrags.containsKey(new Integer(dtmIdentity))) { return (DTMXRTreeFrag) m_DTMXRTreeFrags.get(new Integer(dtmIdentity)); } else { final DTMXRTreeFrag frag = new DTMXRTreeFrag(dtmIdentity, this); m_DTMXRTreeFrags.put(new Integer(dtmIdentity), frag); return frag; } } /** Cleans DTMXRTreeFrag objects by removing references to DTM and XPathContext objects. */ private final void releaseDTMXRTreeFrags() { if (m_DTMXRTreeFrags == null) { return; } final Iterator iter = (m_DTMXRTreeFrags.values()).iterator(); while (iter.hasNext()) { DTMXRTreeFrag frag = (DTMXRTreeFrag) iter.next(); frag.destruct(); iter.remove(); } m_DTMXRTreeFrags = null; } }