0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: XPathContext.java,v 1.55 2005/06/07 15:35:52 mkwan Exp $
0018: */
0019: package org.apache.xpath;
0020:
0021: import java.lang.reflect.Method;
0022: import java.util.Stack;
0023: import java.util.Vector;
0024: import java.util.HashMap;
0025: import java.util.Iterator;
0026:
0027: import javax.xml.transform.ErrorListener;
0028: import javax.xml.transform.SourceLocator;
0029: import javax.xml.transform.TransformerException;
0030: import javax.xml.transform.URIResolver;
0031:
0032: import org.apache.xalan.extensions.ExpressionContext;
0033: import org.apache.xalan.res.XSLMessages;
0034: import org.apache.xml.dtm.Axis;
0035: import org.apache.xml.dtm.DTM;
0036: import org.apache.xml.dtm.DTMFilter;
0037: import org.apache.xml.dtm.DTMIterator;
0038: import org.apache.xml.dtm.DTMManager;
0039: import org.apache.xml.dtm.DTMWSFilter;
0040: import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
0041: import org.apache.xml.utils.IntStack;
0042: import org.apache.xml.utils.NodeVector;
0043: import org.apache.xml.utils.ObjectStack;
0044: import org.apache.xml.utils.PrefixResolver;
0045: import org.apache.xml.utils.SAXSourceLocator;
0046: import org.apache.xml.utils.XMLString;
0047: import org.apache.xpath.axes.SubContextList;
0048: import org.apache.xpath.objects.XObject;
0049: import org.apache.xpath.objects.DTMXRTreeFrag;
0050: import org.apache.xpath.objects.XString;
0051: import org.apache.xpath.res.XPATHErrorResources;
0052:
0053: import org.xml.sax.XMLReader;
0054:
0055: /**
0056: * Default class for the runtime execution context for XPath.
0057: *
0058: * <p>This class extends DTMManager but does not directly implement it.</p>
0059: * @xsl.usage advanced
0060: */
0061: public class XPathContext extends DTMManager // implements ExpressionContext
0062: {
0063: IntStack m_last_pushed_rtfdtm = new IntStack();
0064: /**
0065: * Stack of cached "reusable" DTMs for Result Tree Fragments.
0066: * This is a kluge to handle the problem of starting an RTF before
0067: * the old one is complete.
0068: *
0069: * %REVIEW% I'm using a Vector rather than Stack so we can reuse
0070: * the DTMs if the problem occurs multiple times. I'm not sure that's
0071: * really a net win versus discarding the DTM and starting a new one...
0072: * but the retained RTF DTM will have been tail-pruned so should be small.
0073: */
0074: private Vector m_rtfdtm_stack = null;
0075: /** Index of currently active RTF DTM in m_rtfdtm_stack */
0076: private int m_which_rtfdtm = -1;
0077:
0078: /**
0079: * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is
0080: * required since we're never going to pop these.
0081: */
0082: private SAX2RTFDTM m_global_rtfdtm = null;
0083:
0084: /**
0085: * HashMap of cached the DTMXRTreeFrag objects, which are identified by DTM IDs.
0086: * The object are just wrappers for DTMs which are used in XRTreeFrag.
0087: */
0088: private HashMap m_DTMXRTreeFrags = null;
0089:
0090: /**
0091: * state of the secure processing feature.
0092: */
0093: private boolean m_isSecureProcessing = false;
0094:
0095: /**
0096: * Though XPathContext context extends
0097: * the DTMManager, it really is a proxy for this object, which
0098: * is the real DTMManager.
0099: */
0100: protected DTMManager m_dtmManager = DTMManager
0101: .newInstance(org.apache.xpath.objects.XMLStringFactoryImpl
0102: .getFactory());
0103:
0104: /**
0105: * Return the DTMManager object. Though XPathContext context extends
0106: * the DTMManager, it really is a proxy for the real DTMManager. If a
0107: * caller needs to make a lot of calls to the DTMManager, it is faster
0108: * if it gets the real one from this function.
0109: */
0110: public DTMManager getDTMManager() {
0111: return m_dtmManager;
0112: }
0113:
0114: /**
0115: * Set the state of the secure processing feature
0116: */
0117: public void setSecureProcessing(boolean flag) {
0118: m_isSecureProcessing = flag;
0119: }
0120:
0121: /**
0122: * Return the state of the secure processing feature
0123: */
0124: public boolean isSecureProcessing() {
0125: return m_isSecureProcessing;
0126: }
0127:
0128: /**
0129: * Get an instance of a DTM, loaded with the content from the
0130: * specified source. If the unique flag is true, a new instance will
0131: * always be returned. Otherwise it is up to the DTMManager to return a
0132: * new instance or an instance that it already created and may be being used
0133: * by someone else.
0134: * (I think more parameters will need to be added for error handling, and entity
0135: * resolution).
0136: *
0137: * @param source the specification of the source object, which may be null,
0138: * in which case it is assumed that node construction will take
0139: * by some other means.
0140: * @param unique true if the returned DTM must be unique, probably because it
0141: * is going to be mutated.
0142: * @param wsfilter Enables filtering of whitespace nodes, and may be null.
0143: * @param incremental true if the construction should try and be incremental.
0144: * @param doIndexing true if the caller considers it worth it to use
0145: * indexing schemes.
0146: *
0147: * @return a non-null DTM reference.
0148: */
0149: public DTM getDTM(javax.xml.transform.Source source,
0150: boolean unique, DTMWSFilter wsfilter, boolean incremental,
0151: boolean doIndexing) {
0152: return m_dtmManager.getDTM(source, unique, wsfilter,
0153: incremental, doIndexing);
0154: }
0155:
0156: /**
0157: * Get an instance of a DTM that "owns" a node handle.
0158: *
0159: * @param nodeHandle the nodeHandle.
0160: *
0161: * @return a non-null DTM reference.
0162: */
0163: public DTM getDTM(int nodeHandle) {
0164: return m_dtmManager.getDTM(nodeHandle);
0165: }
0166:
0167: /**
0168: * Given a W3C DOM node, try and return a DTM handle.
0169: * Note: calling this may be non-optimal.
0170: *
0171: * @param node Non-null reference to a DOM node.
0172: *
0173: * @return a valid DTM handle.
0174: */
0175: public int getDTMHandleFromNode(org.w3c.dom.Node node) {
0176: return m_dtmManager.getDTMHandleFromNode(node);
0177: }
0178:
0179: //
0180: //
0181: /**
0182: * %TBD% Doc
0183: */
0184: public int getDTMIdentity(DTM dtm) {
0185: return m_dtmManager.getDTMIdentity(dtm);
0186: }
0187:
0188: //
0189: /**
0190: * Creates an empty <code>DocumentFragment</code> object.
0191: * @return A new <code>DocumentFragment handle</code>.
0192: */
0193: public DTM createDocumentFragment() {
0194: return m_dtmManager.createDocumentFragment();
0195: }
0196:
0197: //
0198: /**
0199: * Release a DTM either to a lru pool, or completely remove reference.
0200: * DTMs without system IDs are always hard deleted.
0201: * State: experimental.
0202: *
0203: * @param dtm The DTM to be released.
0204: * @param shouldHardDelete True if the DTM should be removed no matter what.
0205: * @return true if the DTM was removed, false if it was put back in a lru pool.
0206: */
0207: public boolean release(DTM dtm, boolean shouldHardDelete) {
0208: // %REVIEW% If it's a DTM which may contain multiple Result Tree
0209: // Fragments, we can't discard it unless we know not only that it
0210: // is empty, but that the XPathContext itself is going away. So do
0211: // _not_ accept the request. (May want to do it as part of
0212: // reset(), though.)
0213: if (m_rtfdtm_stack != null && m_rtfdtm_stack.contains(dtm)) {
0214: return false;
0215: }
0216:
0217: return m_dtmManager.release(dtm, shouldHardDelete);
0218: }
0219:
0220: /**
0221: * Create a new <code>DTMIterator</code> based on an XPath
0222: * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
0223: * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
0224: *
0225: * @param xpathCompiler ??? Somehow we need to pass in a subpart of the
0226: * expression. I hate to do this with strings, since the larger expression
0227: * has already been parsed.
0228: *
0229: * @param pos The position in the expression.
0230: * @return The newly created <code>DTMIterator</code>.
0231: */
0232: public DTMIterator createDTMIterator(Object xpathCompiler, int pos) {
0233: return m_dtmManager.createDTMIterator(xpathCompiler, pos);
0234: }
0235:
0236: //
0237: /**
0238: * Create a new <code>DTMIterator</code> based on an XPath
0239: * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
0240: * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
0241: *
0242: * @param xpathString Must be a valid string expressing a
0243: * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
0244: * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
0245: *
0246: * @param presolver An object that can resolve prefixes to namespace URLs.
0247: *
0248: * @return The newly created <code>DTMIterator</code>.
0249: */
0250: public DTMIterator createDTMIterator(String xpathString,
0251: PrefixResolver presolver) {
0252: return m_dtmManager.createDTMIterator(xpathString, presolver);
0253: }
0254:
0255: //
0256: /**
0257: * Create a new <code>DTMIterator</code> based only on a whatToShow and
0258: * a DTMFilter. The traversal semantics are defined as the descendant
0259: * access.
0260: *
0261: * @param whatToShow This flag specifies which node types may appear in
0262: * the logical view of the tree presented by the iterator. See the
0263: * description of <code>NodeFilter</code> for the set of possible
0264: * <code>SHOW_</code> values.These flags can be combined using
0265: * <code>OR</code>.
0266: * @param filter The <code>NodeFilter</code> to be used with this
0267: * <code>TreeWalker</code>, or <code>null</code> to indicate no filter.
0268: * @param entityReferenceExpansion The value of this flag determines
0269: * whether entity reference nodes are expanded.
0270: *
0271: * @return The newly created <code>NodeIterator</code>.
0272: */
0273: public DTMIterator createDTMIterator(int whatToShow,
0274: DTMFilter filter, boolean entityReferenceExpansion) {
0275: return m_dtmManager.createDTMIterator(whatToShow, filter,
0276: entityReferenceExpansion);
0277: }
0278:
0279: /**
0280: * Create a new <code>DTMIterator</code> that holds exactly one node.
0281: *
0282: * @param node The node handle that the DTMIterator will iterate to.
0283: *
0284: * @return The newly created <code>DTMIterator</code>.
0285: */
0286: public DTMIterator createDTMIterator(int node) {
0287: // DescendantIterator iter = new DescendantIterator();
0288: DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(
0289: Axis.SELF);
0290: iter.setRoot(node, this );
0291: return iter;
0292: // return m_dtmManager.createDTMIterator(node);
0293: }
0294:
0295: /**
0296: * Create an XPathContext instance.
0297: */
0298: public XPathContext() {
0299: m_prefixResolvers.push(null);
0300: m_currentNodes.push(DTM.NULL);
0301: m_currentExpressionNodes.push(DTM.NULL);
0302: m_saxLocations.push(null);
0303: }
0304:
0305: /**
0306: * Create an XPathContext instance.
0307: * @param owner Value that can be retrieved via the getOwnerObject() method.
0308: * @see #getOwnerObject
0309: */
0310: public XPathContext(Object owner) {
0311: m_owner = owner;
0312: try {
0313: m_ownerGetErrorListener = m_owner.getClass().getMethod(
0314: "getErrorListener", new Class[] {});
0315: } catch (NoSuchMethodException nsme) {
0316: }
0317: m_prefixResolvers.push(null);
0318: m_currentNodes.push(DTM.NULL);
0319: m_currentExpressionNodes.push(DTM.NULL);
0320: m_saxLocations.push(null);
0321: }
0322:
0323: /**
0324: * Reset for new run.
0325: */
0326: public void reset() {
0327: releaseDTMXRTreeFrags();
0328: // These couldn't be disposed of earlier (see comments in release()); zap them now.
0329: if (m_rtfdtm_stack != null)
0330: for (java.util.Enumeration e = m_rtfdtm_stack.elements(); e
0331: .hasMoreElements();)
0332: m_dtmManager.release((DTM) e.nextElement(), true);
0333:
0334: m_rtfdtm_stack = null; // drop our references too
0335: m_which_rtfdtm = -1;
0336:
0337: if (m_global_rtfdtm != null)
0338: m_dtmManager.release(m_global_rtfdtm, true);
0339: m_global_rtfdtm = null;
0340:
0341: m_dtmManager = DTMManager
0342: .newInstance(org.apache.xpath.objects.XMLStringFactoryImpl
0343: .getFactory());
0344:
0345: m_saxLocations.removeAllElements();
0346: m_axesIteratorStack.removeAllElements();
0347: m_contextNodeLists.removeAllElements();
0348: m_currentExpressionNodes.removeAllElements();
0349: m_currentNodes.removeAllElements();
0350: m_iteratorRoots.RemoveAllNoClear();
0351: m_predicatePos.removeAllElements();
0352: m_predicateRoots.RemoveAllNoClear();
0353: m_prefixResolvers.removeAllElements();
0354:
0355: m_prefixResolvers.push(null);
0356: m_currentNodes.push(DTM.NULL);
0357: m_currentExpressionNodes.push(DTM.NULL);
0358: m_saxLocations.push(null);
0359: }
0360:
0361: /** The current stylesheet locator. */
0362: ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT);
0363:
0364: /**
0365: * Set the current locater in the stylesheet.
0366: *
0367: * @param location The location within the stylesheet.
0368: */
0369: public void setSAXLocator(SourceLocator location) {
0370: m_saxLocations.setTop(location);
0371: }
0372:
0373: /**
0374: * Set the current locater in the stylesheet.
0375: *
0376: * @param location The location within the stylesheet.
0377: */
0378: public void pushSAXLocator(SourceLocator location) {
0379: m_saxLocations.push(location);
0380: }
0381:
0382: /**
0383: * Push a slot on the locations stack so that setSAXLocator can be
0384: * repeatedly called.
0385: *
0386: */
0387: public void pushSAXLocatorNull() {
0388: m_saxLocations.push(null);
0389: }
0390:
0391: /**
0392: * Pop the current locater.
0393: */
0394: public void popSAXLocator() {
0395: m_saxLocations.pop();
0396: }
0397:
0398: /**
0399: * Get the current locater in the stylesheet.
0400: *
0401: * @return The location within the stylesheet, or null if not known.
0402: */
0403: public SourceLocator getSAXLocator() {
0404: return (SourceLocator) m_saxLocations.peek();
0405: }
0406:
0407: /** The owner context of this XPathContext. In the case of XSLT, this will be a
0408: * Transformer object.
0409: */
0410: private Object m_owner;
0411:
0412: /** The owner context of this XPathContext. In the case of XSLT, this will be a
0413: * Transformer object.
0414: */
0415: private Method m_ownerGetErrorListener;
0416:
0417: /**
0418: * Get the "owner" context of this context, which should be,
0419: * in the case of XSLT, the Transformer object. This is needed
0420: * so that XSLT functions can get the Transformer.
0421: * @return The owner object passed into the constructor, or null.
0422: */
0423: public Object getOwnerObject() {
0424: return m_owner;
0425: }
0426:
0427: // ================ VarStack ===================
0428:
0429: /**
0430: * The stack of Variable stacks. A VariableStack will be
0431: * pushed onto this stack for each template invocation.
0432: */
0433: private VariableStack m_variableStacks = new VariableStack();
0434:
0435: /**
0436: * Get the variable stack, which is in charge of variables and
0437: * parameters.
0438: *
0439: * @return the variable stack, which should not be null.
0440: */
0441: public final VariableStack getVarStack() {
0442: return m_variableStacks;
0443: }
0444:
0445: /**
0446: * Get the variable stack, which is in charge of variables and
0447: * parameters.
0448: *
0449: * @param varStack non-null reference to the variable stack.
0450: */
0451: public final void setVarStack(VariableStack varStack) {
0452: m_variableStacks = varStack;
0453: }
0454:
0455: // ================ SourceTreeManager ===================
0456:
0457: /** The source tree manager, which associates Source objects to source
0458: * tree nodes. */
0459: private SourceTreeManager m_sourceTreeManager = new SourceTreeManager();
0460:
0461: /**
0462: * Get the SourceTreeManager associated with this execution context.
0463: *
0464: * @return the SourceTreeManager associated with this execution context.
0465: */
0466: public final SourceTreeManager getSourceTreeManager() {
0467: return m_sourceTreeManager;
0468: }
0469:
0470: /**
0471: * Set the SourceTreeManager associated with this execution context.
0472: *
0473: * @param mgr the SourceTreeManager to be associated with this
0474: * execution context.
0475: */
0476: public void setSourceTreeManager(SourceTreeManager mgr) {
0477: m_sourceTreeManager = mgr;
0478: }
0479:
0480: // =================================================
0481:
0482: /** The ErrorListener where errors and warnings are to be reported. */
0483: private ErrorListener m_errorListener;
0484:
0485: /** A default ErrorListener in case our m_errorListener was not specified and our
0486: * owner either does not have an ErrorListener or has a null one.
0487: */
0488: private ErrorListener m_defaultErrorListener;
0489:
0490: /**
0491: * Get the ErrorListener where errors and warnings are to be reported.
0492: *
0493: * @return A non-null ErrorListener reference.
0494: */
0495: public final ErrorListener getErrorListener() {
0496:
0497: if (null != m_errorListener)
0498: return m_errorListener;
0499:
0500: ErrorListener retval = null;
0501:
0502: try {
0503: if (null != m_ownerGetErrorListener)
0504: retval = (ErrorListener) m_ownerGetErrorListener
0505: .invoke(m_owner, new Object[] {});
0506: } catch (Exception e) {
0507: }
0508:
0509: if (null == retval) {
0510: if (null == m_defaultErrorListener)
0511: m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler();
0512: retval = m_defaultErrorListener;
0513: }
0514:
0515: return retval;
0516: }
0517:
0518: /**
0519: * Set the ErrorListener where errors and warnings are to be reported.
0520: *
0521: * @param listener A non-null ErrorListener reference.
0522: */
0523: public void setErrorListener(ErrorListener listener)
0524: throws IllegalArgumentException {
0525: if (listener == null)
0526: throw new IllegalArgumentException(XSLMessages
0527: .createXPATHMessage(
0528: XPATHErrorResources.ER_NULL_ERROR_HANDLER,
0529: null)); //"Null error handler");
0530: m_errorListener = listener;
0531: }
0532:
0533: // =================================================
0534:
0535: /** The TrAX URI Resolver for resolving URIs from the document(...)
0536: * function to source tree nodes. */
0537: private URIResolver m_uriResolver;
0538:
0539: /**
0540: * Get the URIResolver associated with this execution context.
0541: *
0542: * @return a URI resolver, which may be null.
0543: */
0544: public final URIResolver getURIResolver() {
0545: return m_uriResolver;
0546: }
0547:
0548: /**
0549: * Set the URIResolver associated with this execution context.
0550: *
0551: * @param resolver the URIResolver to be associated with this
0552: * execution context, may be null to clear an already set resolver.
0553: */
0554: public void setURIResolver(URIResolver resolver) {
0555: m_uriResolver = resolver;
0556: }
0557:
0558: // =================================================
0559:
0560: /** The reader of the primary source tree. */
0561: public XMLReader m_primaryReader;
0562:
0563: /**
0564: * Get primary XMLReader associated with this execution context.
0565: *
0566: * @return The reader of the primary source tree.
0567: */
0568: public final XMLReader getPrimaryReader() {
0569: return m_primaryReader;
0570: }
0571:
0572: /**
0573: * Set primary XMLReader associated with this execution context.
0574: *
0575: * @param reader The reader of the primary source tree.
0576: */
0577: public void setPrimaryReader(XMLReader reader) {
0578: m_primaryReader = reader;
0579: }
0580:
0581: // =================================================
0582:
0583: /** Misnamed string manager for XPath messages. */
0584: // private static XSLMessages m_XSLMessages = new XSLMessages();
0585: /**
0586: * Tell the user of an assertion error, and probably throw an
0587: * exception.
0588: *
0589: * @param b If false, a TransformerException will be thrown.
0590: * @param msg The assertion message, which should be informative.
0591: *
0592: * @throws javax.xml.transform.TransformerException if b is false.
0593: */
0594: private void assertion(boolean b, String msg)
0595: throws javax.xml.transform.TransformerException {
0596: if (!b) {
0597: ErrorListener errorHandler = getErrorListener();
0598:
0599: if (errorHandler != null) {
0600: errorHandler
0601: .fatalError(new TransformerException(
0602: XSLMessages
0603: .createMessage(
0604: XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
0605: new Object[] { msg }),
0606: (SAXSourceLocator) this .getSAXLocator()));
0607: }
0608: }
0609: }
0610:
0611: //==========================================================
0612: // SECTION: Execution context state tracking
0613: //==========================================================
0614:
0615: /**
0616: * The current context node list.
0617: */
0618: private Stack m_contextNodeLists = new Stack();
0619:
0620: public Stack getContextNodeListsStack() {
0621: return m_contextNodeLists;
0622: }
0623:
0624: public void setContextNodeListsStack(Stack s) {
0625: m_contextNodeLists = s;
0626: }
0627:
0628: /**
0629: * Get the current context node list.
0630: *
0631: * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
0632: * also refered to here as a <term>context node list</term>.
0633: */
0634: public final DTMIterator getContextNodeList() {
0635:
0636: if (m_contextNodeLists.size() > 0)
0637: return (DTMIterator) m_contextNodeLists.peek();
0638: else
0639: return null;
0640: }
0641:
0642: /**
0643: * Set the current context node list.
0644: *
0645: * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
0646: * also refered to here as a <term>context node list</term>.
0647: * @xsl.usage internal
0648: */
0649: public final void pushContextNodeList(DTMIterator nl) {
0650: m_contextNodeLists.push(nl);
0651: }
0652:
0653: /**
0654: * Pop the current context node list.
0655: * @xsl.usage internal
0656: */
0657: public final void popContextNodeList() {
0658: if (m_contextNodeLists.isEmpty())
0659: System.err
0660: .println("Warning: popContextNodeList when stack is empty!");
0661: else
0662: m_contextNodeLists.pop();
0663: }
0664:
0665: /**
0666: * The ammount to use for stacks that record information during the
0667: * recursive execution.
0668: */
0669: public static final int RECURSIONLIMIT = (1024 * 4);
0670:
0671: /** The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects.
0672: * Not to be confused with the current node list. %REVIEW% Note that there
0673: * are no bounds check and resize for this stack, so if it is blown, it's all
0674: * over. */
0675: private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT);
0676:
0677: // private NodeVector m_currentNodes = new NodeVector();
0678:
0679: public IntStack getCurrentNodeStack() {
0680: return m_currentNodes;
0681: }
0682:
0683: public void setCurrentNodeStack(IntStack nv) {
0684: m_currentNodes = nv;
0685: }
0686:
0687: /**
0688: * Get the current context node.
0689: *
0690: * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
0691: */
0692: public final int getCurrentNode() {
0693: return m_currentNodes.peek();
0694: }
0695:
0696: /**
0697: * Set the current context node and expression node.
0698: *
0699: * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
0700: * @param en the sub-expression context node.
0701: */
0702: public final void pushCurrentNodeAndExpression(int cn, int en) {
0703: m_currentNodes.push(cn);
0704: m_currentExpressionNodes.push(cn);
0705: }
0706:
0707: /**
0708: * Set the current context node.
0709: */
0710: public final void popCurrentNodeAndExpression() {
0711: m_currentNodes.quickPop(1);
0712: m_currentExpressionNodes.quickPop(1);
0713: }
0714:
0715: /**
0716: * Push the current context node, expression node, and prefix resolver.
0717: *
0718: * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
0719: * @param en the sub-expression context node.
0720: * @param nc the namespace context (prefix resolver.
0721: */
0722: public final void pushExpressionState(int cn, int en,
0723: PrefixResolver nc) {
0724: m_currentNodes.push(cn);
0725: m_currentExpressionNodes.push(cn);
0726: m_prefixResolvers.push(nc);
0727: }
0728:
0729: /**
0730: * Pop the current context node, expression node, and prefix resolver.
0731: */
0732: public final void popExpressionState() {
0733: m_currentNodes.quickPop(1);
0734: m_currentExpressionNodes.quickPop(1);
0735: m_prefixResolvers.pop();
0736: }
0737:
0738: /**
0739: * Set the current context node.
0740: *
0741: * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
0742: */
0743: public final void pushCurrentNode(int n) {
0744: m_currentNodes.push(n);
0745: }
0746:
0747: /**
0748: * Pop the current context node.
0749: */
0750: public final void popCurrentNode() {
0751: m_currentNodes.quickPop(1);
0752: }
0753:
0754: /**
0755: * Set the current predicate root.
0756: */
0757: public final void pushPredicateRoot(int n) {
0758: m_predicateRoots.push(n);
0759: }
0760:
0761: /**
0762: * Pop the current predicate root.
0763: */
0764: public final void popPredicateRoot() {
0765: m_predicateRoots.popQuick();
0766: }
0767:
0768: /**
0769: * Get the current predicate root.
0770: */
0771: public final int getPredicateRoot() {
0772: return m_predicateRoots.peepOrNull();
0773: }
0774:
0775: /**
0776: * Set the current location path iterator root.
0777: */
0778: public final void pushIteratorRoot(int n) {
0779: m_iteratorRoots.push(n);
0780: }
0781:
0782: /**
0783: * Pop the current location path iterator root.
0784: */
0785: public final void popIteratorRoot() {
0786: m_iteratorRoots.popQuick();
0787: }
0788:
0789: /**
0790: * Get the current location path iterator root.
0791: */
0792: public final int getIteratorRoot() {
0793: return m_iteratorRoots.peepOrNull();
0794: }
0795:
0796: /** A stack of the current sub-expression nodes. */
0797: private NodeVector m_iteratorRoots = new NodeVector();
0798:
0799: /** A stack of the current sub-expression nodes. */
0800: private NodeVector m_predicateRoots = new NodeVector();
0801:
0802: /** A stack of the current sub-expression nodes. */
0803: private IntStack m_currentExpressionNodes = new IntStack(
0804: RECURSIONLIMIT);
0805:
0806: public IntStack getCurrentExpressionNodeStack() {
0807: return m_currentExpressionNodes;
0808: }
0809:
0810: public void setCurrentExpressionNodeStack(IntStack nv) {
0811: m_currentExpressionNodes = nv;
0812: }
0813:
0814: private IntStack m_predicatePos = new IntStack();
0815:
0816: public final int getPredicatePos() {
0817: return m_predicatePos.peek();
0818: }
0819:
0820: public final void pushPredicatePos(int n) {
0821: m_predicatePos.push(n);
0822: }
0823:
0824: public final void popPredicatePos() {
0825: m_predicatePos.pop();
0826: }
0827:
0828: /**
0829: * Get the current node that is the expression's context (i.e. for current() support).
0830: *
0831: * @return The current sub-expression node.
0832: */
0833: public final int getCurrentExpressionNode() {
0834: return m_currentExpressionNodes.peek();
0835: }
0836:
0837: /**
0838: * Set the current node that is the expression's context (i.e. for current() support).
0839: *
0840: * @param n The sub-expression node to be current.
0841: */
0842: public final void pushCurrentExpressionNode(int n) {
0843: m_currentExpressionNodes.push(n);
0844: }
0845:
0846: /**
0847: * Pop the current node that is the expression's context
0848: * (i.e. for current() support).
0849: */
0850: public final void popCurrentExpressionNode() {
0851: m_currentExpressionNodes.quickPop(1);
0852: }
0853:
0854: private ObjectStack m_prefixResolvers = new ObjectStack(
0855: RECURSIONLIMIT);
0856:
0857: /**
0858: * Get the current namespace context for the xpath.
0859: *
0860: * @return the current prefix resolver for resolving prefixes to
0861: * namespace URLs.
0862: */
0863: public final PrefixResolver getNamespaceContext() {
0864: return (PrefixResolver) m_prefixResolvers.peek();
0865: }
0866:
0867: /**
0868: * Get the current namespace context for the xpath.
0869: *
0870: * @param pr the prefix resolver to be used for resolving prefixes to
0871: * namespace URLs.
0872: */
0873: public final void setNamespaceContext(PrefixResolver pr) {
0874: m_prefixResolvers.setTop(pr);
0875: }
0876:
0877: /**
0878: * Push a current namespace context for the xpath.
0879: *
0880: * @param pr the prefix resolver to be used for resolving prefixes to
0881: * namespace URLs.
0882: */
0883: public final void pushNamespaceContext(PrefixResolver pr) {
0884: m_prefixResolvers.push(pr);
0885: }
0886:
0887: /**
0888: * Just increment the namespace contest stack, so that setNamespaceContext
0889: * can be used on the slot.
0890: */
0891: public final void pushNamespaceContextNull() {
0892: m_prefixResolvers.push(null);
0893: }
0894:
0895: /**
0896: * Pop the current namespace context for the xpath.
0897: */
0898: public final void popNamespaceContext() {
0899: m_prefixResolvers.pop();
0900: }
0901:
0902: //==========================================================
0903: // SECTION: Current TreeWalker contexts (for internal use)
0904: //==========================================================
0905:
0906: /**
0907: * Stack of AxesIterators.
0908: */
0909: private Stack m_axesIteratorStack = new Stack();
0910:
0911: public Stack getAxesIteratorStackStacks() {
0912: return m_axesIteratorStack;
0913: }
0914:
0915: public void setAxesIteratorStackStacks(Stack s) {
0916: m_axesIteratorStack = s;
0917: }
0918:
0919: /**
0920: * Push a TreeWalker on the stack.
0921: *
0922: * @param iter A sub-context AxesWalker.
0923: * @xsl.usage internal
0924: */
0925: public final void pushSubContextList(SubContextList iter) {
0926: m_axesIteratorStack.push(iter);
0927: }
0928:
0929: /**
0930: * Pop the last pushed axes iterator.
0931: * @xsl.usage internal
0932: */
0933: public final void popSubContextList() {
0934: m_axesIteratorStack.pop();
0935: }
0936:
0937: /**
0938: * Get the current axes iterator, or return null if none.
0939: *
0940: * @return the sub-context node list.
0941: * @xsl.usage internal
0942: */
0943: public SubContextList getSubContextList() {
0944: return m_axesIteratorStack.isEmpty() ? null
0945: : (SubContextList) m_axesIteratorStack.peek();
0946: }
0947:
0948: /**
0949: * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>
0950: * as defined by the XSLT spec.
0951: *
0952: * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>.
0953: * @xsl.usage internal
0954: */
0955:
0956: public org.apache.xpath.axes.SubContextList getCurrentNodeList() {
0957: return m_axesIteratorStack.isEmpty() ? null
0958: : (SubContextList) m_axesIteratorStack.elementAt(0);
0959: }
0960:
0961: //==========================================================
0962: // SECTION: Implementation of ExpressionContext interface
0963: //==========================================================
0964:
0965: /**
0966: * Get the current context node.
0967: * @return The current context node.
0968: */
0969: public final int getContextNode() {
0970: return this .getCurrentNode();
0971: }
0972:
0973: /**
0974: * Get the current context node list.
0975: * @return An iterator for the current context list, as
0976: * defined in XSLT.
0977: */
0978: public final DTMIterator getContextNodes() {
0979:
0980: try {
0981: DTMIterator cnl = getContextNodeList();
0982:
0983: if (null != cnl)
0984: return cnl.cloneWithReset();
0985: else
0986: return null; // for now... this might ought to be an empty iterator.
0987: } catch (CloneNotSupportedException cnse) {
0988: return null; // error reporting?
0989: }
0990: }
0991:
0992: XPathExpressionContext expressionContext = new XPathExpressionContext();
0993:
0994: /**
0995: * The the expression context for extensions for this context.
0996: *
0997: * @return An object that implements the ExpressionContext.
0998: */
0999: public ExpressionContext getExpressionContext() {
1000: return expressionContext;
1001: }
1002:
1003: public class XPathExpressionContext implements ExpressionContext {
1004: /**
1005: * Return the XPathContext associated with this XPathExpressionContext.
1006: * Extensions should use this judiciously and only when special processing
1007: * requirements cannot be met another way. Consider requesting an enhancement
1008: * to the ExpressionContext interface to avoid having to call this method.
1009: * @return the XPathContext associated with this XPathExpressionContext.
1010: */
1011: public XPathContext getXPathContext() {
1012: return XPathContext.this ;
1013: }
1014:
1015: /**
1016: * Return the DTMManager object. Though XPathContext context extends
1017: * the DTMManager, it really is a proxy for the real DTMManager. If a
1018: * caller needs to make a lot of calls to the DTMManager, it is faster
1019: * if it gets the real one from this function.
1020: */
1021: public DTMManager getDTMManager() {
1022: return m_dtmManager;
1023: }
1024:
1025: /**
1026: * Get the current context node.
1027: * @return The current context node.
1028: */
1029: public org.w3c.dom.Node getContextNode() {
1030: int context = getCurrentNode();
1031:
1032: return getDTM(context).getNode(context);
1033: }
1034:
1035: /**
1036: * Get the current context node list.
1037: * @return An iterator for the current context list, as
1038: * defined in XSLT.
1039: */
1040: public org.w3c.dom.traversal.NodeIterator getContextNodes() {
1041: return new org.apache.xml.dtm.ref.DTMNodeIterator(
1042: getContextNodeList());
1043: }
1044:
1045: /**
1046: * Get the error listener.
1047: * @return The registered error listener.
1048: */
1049: public ErrorListener getErrorListener() {
1050: return XPathContext.this .getErrorListener();
1051: }
1052:
1053: /**
1054: * Get the value of a node as a number.
1055: * @param n Node to be converted to a number. May be null.
1056: * @return value of n as a number.
1057: */
1058: public double toNumber(org.w3c.dom.Node n) {
1059: // %REVIEW% You can't get much uglier than this...
1060: int nodeHandle = getDTMHandleFromNode(n);
1061: DTM dtm = getDTM(nodeHandle);
1062: XString xobj = (XString) dtm.getStringValue(nodeHandle);
1063: return xobj.num();
1064: }
1065:
1066: /**
1067: * Get the value of a node as a string.
1068: * @param n Node to be converted to a string. May be null.
1069: * @return value of n as a string, or an empty string if n is null.
1070: */
1071: public String toString(org.w3c.dom.Node n) {
1072: // %REVIEW% You can't get much uglier than this...
1073: int nodeHandle = getDTMHandleFromNode(n);
1074: DTM dtm = getDTM(nodeHandle);
1075: XMLString strVal = dtm.getStringValue(nodeHandle);
1076: return strVal.toString();
1077: }
1078:
1079: /**
1080: * Get a variable based on it's qualified name.
1081: * @param qname The qualified name of the variable.
1082: * @return The evaluated value of the variable.
1083: * @throws javax.xml.transform.TransformerException
1084: */
1085:
1086: public final XObject getVariableOrParam(
1087: org.apache.xml.utils.QName qname)
1088: throws javax.xml.transform.TransformerException {
1089: return m_variableStacks.getVariableOrParam(
1090: XPathContext.this , qname);
1091: }
1092:
1093: }
1094:
1095: /**
1096: * Get a DTM to be used as a container for a global Result Tree
1097: * Fragment. This will always be an instance of (derived from? equivalent to?)
1098: * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX
1099: * output to it. It may be a single DTM containing for multiple fragments,
1100: * if the implementation supports that.
1101: *
1102: * Note: The distinction between this method and getRTFDTM() is that the latter
1103: * allocates space from the dynamic variable stack (m_rtfdtm_stack), which may
1104: * be pruned away again as the templates which defined those variables are exited.
1105: * Global variables may be bound late (see XUnresolvedVariable), and never want to
1106: * be discarded, hence we need to allocate them separately and don't actually need
1107: * a stack to track them.
1108: *
1109: * @return a non-null DTM reference.
1110: */
1111: public DTM getGlobalRTFDTM() {
1112: // We probably should _NOT_ be applying whitespace filtering at this stage!
1113: //
1114: // Some magic has been applied in DTMManagerDefault to recognize this set of options
1115: // and generate an instance of DTM which can contain multiple documents
1116: // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
1117: // I didn't want to change the manager API at this time, or expose
1118: // too many dependencies on its internals. (Ideally, I'd like to move
1119: // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
1120: // specify the subclass here.)
1121:
1122: // If it doesn't exist, or if the one already existing is in the middle of
1123: // being constructed, we need to obtain a new DTM to write into. I'm not sure
1124: // the latter will ever arise, but I'd rather be just a bit paranoid..
1125: if (m_global_rtfdtm == null
1126: || m_global_rtfdtm.isTreeIncomplete()) {
1127: m_global_rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null,
1128: true, null, false, false);
1129: }
1130: return m_global_rtfdtm;
1131: }
1132:
1133: /**
1134: * Get a DTM to be used as a container for a dynamic Result Tree
1135: * Fragment. This will always be an instance of (derived from? equivalent to?)
1136: * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX
1137: * output to it. It may be a single DTM containing for multiple fragments,
1138: * if the implementation supports that.
1139: *
1140: * @return a non-null DTM reference.
1141: */
1142: public DTM getRTFDTM() {
1143: SAX2RTFDTM rtfdtm;
1144:
1145: // We probably should _NOT_ be applying whitespace filtering at this stage!
1146: //
1147: // Some magic has been applied in DTMManagerDefault to recognize this set of options
1148: // and generate an instance of DTM which can contain multiple documents
1149: // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
1150: // I didn't want to change the manager API at this time, or expose
1151: // too many dependencies on its internals. (Ideally, I'd like to move
1152: // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
1153: // specify the subclass here.)
1154:
1155: if (m_rtfdtm_stack == null) {
1156: m_rtfdtm_stack = new Vector();
1157: rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null, true, null,
1158: false, false);
1159: m_rtfdtm_stack.addElement(rtfdtm);
1160: ++m_which_rtfdtm;
1161: } else if (m_which_rtfdtm < 0) {
1162: rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack
1163: .elementAt(++m_which_rtfdtm);
1164: } else {
1165: rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack
1166: .elementAt(m_which_rtfdtm);
1167:
1168: // It might already be under construction -- the classic example would be
1169: // an xsl:variable which uses xsl:call-template as part of its value. To
1170: // handle this recursion, we have to start a new RTF DTM, pushing the old
1171: // one onto a stack so we can return to it. This is not as uncommon a case
1172: // as we might wish, unfortunately, as some folks insist on coding XSLT
1173: // as if it were a procedural language...
1174: if (rtfdtm.isTreeIncomplete()) {
1175: if (++m_which_rtfdtm < m_rtfdtm_stack.size())
1176: rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack
1177: .elementAt(m_which_rtfdtm);
1178: else {
1179: rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null,
1180: true, null, false, false);
1181: m_rtfdtm_stack.addElement(rtfdtm);
1182: }
1183: }
1184: }
1185:
1186: return rtfdtm;
1187: }
1188:
1189: /** Push the RTFDTM's context mark, to allows discarding RTFs added after this
1190: * point. (If it doesn't exist we don't push, since we might still be able to
1191: * get away with not creating it. That requires that excessive pops be harmless.)
1192: * */
1193: public void pushRTFContext() {
1194: m_last_pushed_rtfdtm.push(m_which_rtfdtm);
1195: if (null != m_rtfdtm_stack)
1196: ((SAX2RTFDTM) (getRTFDTM())).pushRewindMark();
1197: }
1198:
1199: /** Pop the RTFDTM's context mark. This discards any RTFs added after the last
1200: * mark was set.
1201: *
1202: * If there is no RTF DTM, there's nothing to pop so this
1203: * becomes a no-op. If pushes were issued before this was called, we count on
1204: * the fact that popRewindMark is defined such that overpopping just resets
1205: * to empty.
1206: *
1207: * Complicating factor: We need to handle the case of popping back to a previous
1208: * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose.
1209: * Basically: If pop says this DTM is now empty, then return to the previous
1210: * if one exists, in whatever state we left it in. UGLY, but hopefully the
1211: * situation which forces us to consider this will arise exceedingly rarely.
1212: * */
1213: public void popRTFContext() {
1214: int previous = m_last_pushed_rtfdtm.pop();
1215: if (null == m_rtfdtm_stack)
1216: return;
1217:
1218: if (m_which_rtfdtm == previous) {
1219: if (previous >= 0) // guard against none-active
1220: {
1221: boolean isEmpty = ((SAX2RTFDTM) (m_rtfdtm_stack
1222: .elementAt(previous))).popRewindMark();
1223: }
1224: } else
1225: while (m_which_rtfdtm != previous) {
1226: // Empty each DTM before popping, so it's ready for reuse
1227: // _DON'T_ pop the previous, since it's still open (which is why we
1228: // stacked up more of these) and did not receive a mark.
1229: boolean isEmpty = ((SAX2RTFDTM) (m_rtfdtm_stack
1230: .elementAt(m_which_rtfdtm))).popRewindMark();
1231: --m_which_rtfdtm;
1232: }
1233: }
1234:
1235: /**
1236: * Gets DTMXRTreeFrag object if one has already been created.
1237: * Creates new DTMXRTreeFrag object and adds to m_DTMXRTreeFrags HashMap,
1238: * otherwise.
1239: * @param dtmIdentity
1240: * @return DTMXRTreeFrag
1241: */
1242: public DTMXRTreeFrag getDTMXRTreeFrag(int dtmIdentity) {
1243: if (m_DTMXRTreeFrags == null) {
1244: m_DTMXRTreeFrags = new HashMap();
1245: }
1246:
1247: if (m_DTMXRTreeFrags.containsKey(new Integer(dtmIdentity))) {
1248: return (DTMXRTreeFrag) m_DTMXRTreeFrags.get(new Integer(
1249: dtmIdentity));
1250: } else {
1251: final DTMXRTreeFrag frag = new DTMXRTreeFrag(dtmIdentity,
1252: this );
1253: m_DTMXRTreeFrags.put(new Integer(dtmIdentity), frag);
1254: return frag;
1255: }
1256: }
1257:
1258: /**
1259: * Cleans DTMXRTreeFrag objects by removing references
1260: * to DTM and XPathContext objects.
1261: */
1262: private final void releaseDTMXRTreeFrags() {
1263: if (m_DTMXRTreeFrags == null) {
1264: return;
1265: }
1266: final Iterator iter = (m_DTMXRTreeFrags.values()).iterator();
1267: while (iter.hasNext()) {
1268: DTMXRTreeFrag frag = (DTMXRTreeFrag) iter.next();
1269: frag.destruct();
1270: iter.remove();
1271: }
1272: m_DTMXRTreeFrags = null;
1273: }
1274: }
|