All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xpath.XPathContext Maven / Gradle / Ivy

There is a newer version: 1.14.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: XPathContext.java 524809 2007-04-02 15:51:51Z zongaro $
 */
package org.apache.xpath;

import java.lang.reflect.Method;
import java.util.Stack;
import java.util.Vector;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;

import org.apache.xalan.extensions.ExpressionContext;
import org.apache.xalan.res.XSLMessages;
import org.apache.xml.dtm.Axis;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMFilter;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.DTMManager;
import org.apache.xml.dtm.DTMWSFilter;
import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
import org.apache.xml.utils.IntStack;
import org.apache.xml.utils.NodeVector;
import org.apache.xml.utils.ObjectStack;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.SAXSourceLocator;
import org.apache.xml.utils.XMLString;
import org.apache.xpath.axes.SubContextList;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.DTMXRTreeFrag;
import org.apache.xpath.objects.XString;
import org.apache.xpath.res.XPATHErrorResources;

import org.xml.sax.XMLReader;

/**
 * Default class for the runtime execution context for XPath.
 * 
 * 

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. * * %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 DocumentFragment object. * @return A new DocumentFragment handle. */ 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 DTMIterator based on an XPath * UnionExpr. * * @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 DTMIterator. */ public DTMIterator createDTMIterator(Object xpathCompiler, int pos) { return m_dtmManager.createDTMIterator(xpathCompiler, pos); } // /** * Create a new DTMIterator based on an XPath * UnionExpr. * * @param xpathString Must be a valid string expressing a * UnionExpr. * * @param presolver An object that can resolve prefixes to namespace URLs. * * @return The newly created DTMIterator. */ public DTMIterator createDTMIterator(String xpathString, PrefixResolver presolver) { return m_dtmManager.createDTMIterator(xpathString, presolver); } // /** * Create a new DTMIterator 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 NodeFilter for the set of possible * SHOW_ values.These flags can be combined using * OR. * @param filter The NodeFilter to be used with this * TreeWalker, or null to indicate no filter. * @param entityReferenceExpansion The value of this flag determines * whether entity reference nodes are expanded. * * @return The newly created NodeIterator. */ public DTMIterator createDTMIterator(int whatToShow, DTMFilter filter, boolean entityReferenceExpansion) { return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion); } /** * Create a new DTMIterator that holds exactly one node. * * @param node The node handle that the DTMIterator will iterate to. * * @return The newly created DTMIterator. */ 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 * true. */ public XPathContext() { this(true); } /** * Create an XPathContext instance. * @param recursiveVarContext A boolean 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 true. * @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 boolean 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 current node list, * also refered to here as a context node list. */ 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 current node list, * also refered to here as a context node list. * @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 current node 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 current node. */ public final int getCurrentNode() { return m_currentNodes.peek(); } /** * Set the current context node and expression node. * * @param cn the current node. * @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 current node. * @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 current node. */ 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 current node list * as defined by the XSLT spec. * * @return the current node list. * @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. * * 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. * * 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() { 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy