/* * 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 emptyDocumentFragment
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