001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: XPathContext.java 524809 2007-04-02 15:51:51Z zongaro $
020 */
021 package org.apache.xpath;
022
023 import java.lang.reflect.Method;
024 import java.util.Stack;
025 import java.util.Vector;
026 import java.util.HashMap;
027 import java.util.Iterator;
028
029 import javax.xml.transform.ErrorListener;
030 import javax.xml.transform.SourceLocator;
031 import javax.xml.transform.TransformerException;
032 import javax.xml.transform.URIResolver;
033
034 import org.apache.xalan.extensions.ExpressionContext;
035 import org.apache.xalan.res.XSLMessages;
036 import org.apache.xml.dtm.Axis;
037 import org.apache.xml.dtm.DTM;
038 import org.apache.xml.dtm.DTMFilter;
039 import org.apache.xml.dtm.DTMIterator;
040 import org.apache.xml.dtm.DTMManager;
041 import org.apache.xml.dtm.DTMWSFilter;
042 import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
043 import org.apache.xml.utils.IntStack;
044 import org.apache.xml.utils.NodeVector;
045 import org.apache.xml.utils.ObjectStack;
046 import org.apache.xml.utils.PrefixResolver;
047 import org.apache.xml.utils.SAXSourceLocator;
048 import org.apache.xml.utils.XMLString;
049 import org.apache.xpath.axes.SubContextList;
050 import org.apache.xpath.objects.XObject;
051 import org.apache.xpath.objects.DTMXRTreeFrag;
052 import org.apache.xpath.objects.XString;
053 import org.apache.xpath.res.XPATHErrorResources;
054
055 import org.xml.sax.XMLReader;
056
057 /**
058 * Default class for the runtime execution context for XPath.
059 *
060 * <p>This class extends DTMManager but does not directly implement it.</p>
061 * @xsl.usage advanced
062 */
063 public class XPathContext extends DTMManager // implements ExpressionContext
064 {
065 IntStack m_last_pushed_rtfdtm=new IntStack();
066 /**
067 * Stack of cached "reusable" DTMs for Result Tree Fragments.
068 * This is a kluge to handle the problem of starting an RTF before
069 * the old one is complete.
070 *
071 * %REVIEW% I'm using a Vector rather than Stack so we can reuse
072 * the DTMs if the problem occurs multiple times. I'm not sure that's
073 * really a net win versus discarding the DTM and starting a new one...
074 * but the retained RTF DTM will have been tail-pruned so should be small.
075 */
076 private Vector m_rtfdtm_stack=null;
077 /** Index of currently active RTF DTM in m_rtfdtm_stack */
078 private int m_which_rtfdtm=-1;
079
080 /**
081 * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is
082 * required since we're never going to pop these.
083 */
084 private SAX2RTFDTM m_global_rtfdtm=null;
085
086 /**
087 * HashMap of cached the DTMXRTreeFrag objects, which are identified by DTM IDs.
088 * The object are just wrappers for DTMs which are used in XRTreeFrag.
089 */
090 private HashMap m_DTMXRTreeFrags = null;
091
092 /**
093 * state of the secure processing feature.
094 */
095 private boolean m_isSecureProcessing = false;
096
097 /**
098 * Though XPathContext context extends
099 * the DTMManager, it really is a proxy for this object, which
100 * is the real DTMManager.
101 */
102 protected DTMManager m_dtmManager = DTMManager.newInstance(
103 org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
104
105 /**
106 * Return the DTMManager object. Though XPathContext context extends
107 * the DTMManager, it really is a proxy for the real DTMManager. If a
108 * caller needs to make a lot of calls to the DTMManager, it is faster
109 * if it gets the real one from this function.
110 */
111 public DTMManager getDTMManager()
112 {
113 return m_dtmManager;
114 }
115
116 /**
117 * Set the state of the secure processing feature
118 */
119 public void setSecureProcessing(boolean flag)
120 {
121 m_isSecureProcessing = flag;
122 }
123
124 /**
125 * Return the state of the secure processing feature
126 */
127 public boolean isSecureProcessing()
128 {
129 return m_isSecureProcessing;
130 }
131
132 /**
133 * Get an instance of a DTM, loaded with the content from the
134 * specified source. If the unique flag is true, a new instance will
135 * always be returned. Otherwise it is up to the DTMManager to return a
136 * new instance or an instance that it already created and may be being used
137 * by someone else.
138 * (I think more parameters will need to be added for error handling, and entity
139 * resolution).
140 *
141 * @param source the specification of the source object, which may be null,
142 * in which case it is assumed that node construction will take
143 * by some other means.
144 * @param unique true if the returned DTM must be unique, probably because it
145 * is going to be mutated.
146 * @param wsfilter Enables filtering of whitespace nodes, and may be null.
147 * @param incremental true if the construction should try and be incremental.
148 * @param doIndexing true if the caller considers it worth it to use
149 * indexing schemes.
150 *
151 * @return a non-null DTM reference.
152 */
153 public DTM getDTM(javax.xml.transform.Source source, boolean unique,
154 DTMWSFilter wsfilter,
155 boolean incremental,
156 boolean doIndexing)
157 {
158 return m_dtmManager.getDTM(source, unique, wsfilter,
159 incremental, doIndexing);
160 }
161
162 /**
163 * Get an instance of a DTM that "owns" a node handle.
164 *
165 * @param nodeHandle the nodeHandle.
166 *
167 * @return a non-null DTM reference.
168 */
169 public DTM getDTM(int nodeHandle)
170 {
171 return m_dtmManager.getDTM(nodeHandle);
172 }
173
174 /**
175 * Given a W3C DOM node, try and return a DTM handle.
176 * Note: calling this may be non-optimal.
177 *
178 * @param node Non-null reference to a DOM node.
179 *
180 * @return a valid DTM handle.
181 */
182 public int getDTMHandleFromNode(org.w3c.dom.Node node)
183 {
184 return m_dtmManager.getDTMHandleFromNode(node);
185 }
186 //
187 //
188 /**
189 * %TBD% Doc
190 */
191 public int getDTMIdentity(DTM dtm)
192 {
193 return m_dtmManager.getDTMIdentity(dtm);
194 }
195 //
196 /**
197 * Creates an empty <code>DocumentFragment</code> object.
198 * @return A new <code>DocumentFragment handle</code>.
199 */
200 public DTM createDocumentFragment()
201 {
202 return m_dtmManager.createDocumentFragment();
203 }
204 //
205 /**
206 * Release a DTM either to a lru pool, or completely remove reference.
207 * DTMs without system IDs are always hard deleted.
208 * State: experimental.
209 *
210 * @param dtm The DTM to be released.
211 * @param shouldHardDelete True if the DTM should be removed no matter what.
212 * @return true if the DTM was removed, false if it was put back in a lru pool.
213 */
214 public boolean release(DTM dtm, boolean shouldHardDelete)
215 {
216 // %REVIEW% If it's a DTM which may contain multiple Result Tree
217 // Fragments, we can't discard it unless we know not only that it
218 // is empty, but that the XPathContext itself is going away. So do
219 // _not_ accept the request. (May want to do it as part of
220 // reset(), though.)
221 if(m_rtfdtm_stack!=null && m_rtfdtm_stack.contains(dtm))
222 {
223 return false;
224 }
225
226 return m_dtmManager.release(dtm, shouldHardDelete);
227 }
228
229 /**
230 * Create a new <code>DTMIterator</code> based on an XPath
231 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
232 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
233 *
234 * @param xpathCompiler ??? Somehow we need to pass in a subpart of the
235 * expression. I hate to do this with strings, since the larger expression
236 * has already been parsed.
237 *
238 * @param pos The position in the expression.
239 * @return The newly created <code>DTMIterator</code>.
240 */
241 public DTMIterator createDTMIterator(Object xpathCompiler, int pos)
242 {
243 return m_dtmManager.createDTMIterator(xpathCompiler, pos);
244 }
245 //
246 /**
247 * Create a new <code>DTMIterator</code> based on an XPath
248 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
249 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
250 *
251 * @param xpathString Must be a valid string expressing a
252 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
253 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
254 *
255 * @param presolver An object that can resolve prefixes to namespace URLs.
256 *
257 * @return The newly created <code>DTMIterator</code>.
258 */
259 public DTMIterator createDTMIterator(String xpathString,
260 PrefixResolver presolver)
261 {
262 return m_dtmManager.createDTMIterator(xpathString, presolver);
263 }
264 //
265 /**
266 * Create a new <code>DTMIterator</code> based only on a whatToShow and
267 * a DTMFilter. The traversal semantics are defined as the descendant
268 * access.
269 *
270 * @param whatToShow This flag specifies which node types may appear in
271 * the logical view of the tree presented by the iterator. See the
272 * description of <code>NodeFilter</code> for the set of possible
273 * <code>SHOW_</code> values.These flags can be combined using
274 * <code>OR</code>.
275 * @param filter The <code>NodeFilter</code> to be used with this
276 * <code>TreeWalker</code>, or <code>null</code> to indicate no filter.
277 * @param entityReferenceExpansion The value of this flag determines
278 * whether entity reference nodes are expanded.
279 *
280 * @return The newly created <code>NodeIterator</code>.
281 */
282 public DTMIterator createDTMIterator(int whatToShow,
283 DTMFilter filter, boolean entityReferenceExpansion)
284 {
285 return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion);
286 }
287
288 /**
289 * Create a new <code>DTMIterator</code> that holds exactly one node.
290 *
291 * @param node The node handle that the DTMIterator will iterate to.
292 *
293 * @return The newly created <code>DTMIterator</code>.
294 */
295 public DTMIterator createDTMIterator(int node)
296 {
297 // DescendantIterator iter = new DescendantIterator();
298 DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(Axis.SELF);
299 iter.setRoot(node, this);
300 return iter;
301 // return m_dtmManager.createDTMIterator(node);
302 }
303
304 /**
305 * Create an XPathContext instance. This is equivalent to calling
306 * the {@link #XPathContext(boolean)} constructor with the value
307 * <code>true</code>.
308 */
309 public XPathContext() {
310 this(true);
311 }
312
313 /**
314 * Create an XPathContext instance.
315 * @param recursiveVarContext A <code>boolean</code> value indicating whether
316 * the XPath context needs to support pushing of scopes for
317 * variable resolution
318 */
319 public XPathContext(boolean recursiveVarContext) {
320 m_prefixResolvers.push(null);
321 m_currentNodes.push(DTM.NULL);
322 m_currentExpressionNodes.push(DTM.NULL);
323 m_saxLocations.push(null);
324 m_variableStacks = recursiveVarContext ? new VariableStack()
325 : new VariableStack(1);
326 }
327
328 /**
329 * Create an XPathContext instance. This is equivalent to calling the
330 * constructor {@link #XPathContext(java.lang.Object,boolean)} with the
331 * value of the second parameter set to <code>true</code>.
332 * @param owner Value that can be retrieved via the getOwnerObject() method.
333 * @see #getOwnerObject
334 */
335 public XPathContext(Object owner)
336 {
337 this(owner, true);
338 }
339
340 /**
341 * Create an XPathContext instance.
342 * @param owner Value that can be retrieved via the getOwnerObject() method.
343 * @see #getOwnerObject
344 * @param recursiveVarContext A <code>boolean</code> value indicating whether
345 * the XPath context needs to support pushing of scopes for
346 * variable resolution
347 */
348 public XPathContext(Object owner, boolean recursiveVarContext) {
349 this(recursiveVarContext);
350 m_owner = owner;
351 try {
352 m_ownerGetErrorListener = m_owner.getClass().getMethod("getErrorListener", new Class[] {});
353 }
354 catch (NoSuchMethodException nsme) {}
355 }
356
357 /**
358 * Reset for new run.
359 */
360 public void reset()
361 {
362 releaseDTMXRTreeFrags();
363 // These couldn't be disposed of earlier (see comments in release()); zap them now.
364 if(m_rtfdtm_stack!=null)
365 for (java.util.Enumeration e = m_rtfdtm_stack.elements() ; e.hasMoreElements() ;)
366 m_dtmManager.release((DTM)e.nextElement(), true);
367
368 m_rtfdtm_stack=null; // drop our references too
369 m_which_rtfdtm=-1;
370
371 if(m_global_rtfdtm!=null)
372 m_dtmManager.release(m_global_rtfdtm,true);
373 m_global_rtfdtm=null;
374
375
376 m_dtmManager = DTMManager.newInstance(
377 org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
378
379 m_saxLocations.removeAllElements();
380 m_axesIteratorStack.removeAllElements();
381 m_contextNodeLists.removeAllElements();
382 m_currentExpressionNodes.removeAllElements();
383 m_currentNodes.removeAllElements();
384 m_iteratorRoots.RemoveAllNoClear();
385 m_predicatePos.removeAllElements();
386 m_predicateRoots.RemoveAllNoClear();
387 m_prefixResolvers.removeAllElements();
388
389 m_prefixResolvers.push(null);
390 m_currentNodes.push(DTM.NULL);
391 m_currentExpressionNodes.push(DTM.NULL);
392 m_saxLocations.push(null);
393 }
394
395 /** The current stylesheet locator. */
396 ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT);
397
398 /**
399 * Set the current locater in the stylesheet.
400 *
401 * @param location The location within the stylesheet.
402 */
403 public void setSAXLocator(SourceLocator location)
404 {
405 m_saxLocations.setTop(location);
406 }
407
408 /**
409 * Set the current locater in the stylesheet.
410 *
411 * @param location The location within the stylesheet.
412 */
413 public void pushSAXLocator(SourceLocator location)
414 {
415 m_saxLocations.push(location);
416 }
417
418 /**
419 * Push a slot on the locations stack so that setSAXLocator can be
420 * repeatedly called.
421 *
422 */
423 public void pushSAXLocatorNull()
424 {
425 m_saxLocations.push(null);
426 }
427
428
429 /**
430 * Pop the current locater.
431 */
432 public void popSAXLocator()
433 {
434 m_saxLocations.pop();
435 }
436
437 /**
438 * Get the current locater in the stylesheet.
439 *
440 * @return The location within the stylesheet, or null if not known.
441 */
442 public SourceLocator getSAXLocator()
443 {
444 return (SourceLocator) m_saxLocations.peek();
445 }
446
447 /** The owner context of this XPathContext. In the case of XSLT, this will be a
448 * Transformer object.
449 */
450 private Object m_owner;
451
452 /** The owner context of this XPathContext. In the case of XSLT, this will be a
453 * Transformer object.
454 */
455 private Method m_ownerGetErrorListener;
456
457 /**
458 * Get the "owner" context of this context, which should be,
459 * in the case of XSLT, the Transformer object. This is needed
460 * so that XSLT functions can get the Transformer.
461 * @return The owner object passed into the constructor, or null.
462 */
463 public Object getOwnerObject()
464 {
465 return m_owner;
466 }
467
468 // ================ VarStack ===================
469
470 /**
471 * The stack of Variable stacks. A VariableStack will be
472 * pushed onto this stack for each template invocation.
473 */
474 private VariableStack m_variableStacks;
475
476 /**
477 * Get the variable stack, which is in charge of variables and
478 * parameters.
479 *
480 * @return the variable stack, which should not be null.
481 */
482 public final VariableStack getVarStack()
483 {
484 return m_variableStacks;
485 }
486
487 /**
488 * Get the variable stack, which is in charge of variables and
489 * parameters.
490 *
491 * @param varStack non-null reference to the variable stack.
492 */
493 public final void setVarStack(VariableStack varStack)
494 {
495 m_variableStacks = varStack;
496 }
497
498 // ================ SourceTreeManager ===================
499
500 /** The source tree manager, which associates Source objects to source
501 * tree nodes. */
502 private SourceTreeManager m_sourceTreeManager = new SourceTreeManager();
503
504 /**
505 * Get the SourceTreeManager associated with this execution context.
506 *
507 * @return the SourceTreeManager associated with this execution context.
508 */
509 public final SourceTreeManager getSourceTreeManager()
510 {
511 return m_sourceTreeManager;
512 }
513
514 /**
515 * Set the SourceTreeManager associated with this execution context.
516 *
517 * @param mgr the SourceTreeManager to be associated with this
518 * execution context.
519 */
520 public void setSourceTreeManager(SourceTreeManager mgr)
521 {
522 m_sourceTreeManager = mgr;
523 }
524
525 // =================================================
526
527 /** The ErrorListener where errors and warnings are to be reported. */
528 private ErrorListener m_errorListener;
529
530 /** A default ErrorListener in case our m_errorListener was not specified and our
531 * owner either does not have an ErrorListener or has a null one.
532 */
533 private ErrorListener m_defaultErrorListener;
534
535 /**
536 * Get the ErrorListener where errors and warnings are to be reported.
537 *
538 * @return A non-null ErrorListener reference.
539 */
540 public final ErrorListener getErrorListener()
541 {
542
543 if (null != m_errorListener)
544 return m_errorListener;
545
546 ErrorListener retval = null;
547
548 try {
549 if (null != m_ownerGetErrorListener)
550 retval = (ErrorListener) m_ownerGetErrorListener.invoke(m_owner, new Object[] {});
551 }
552 catch (Exception e) {}
553
554 if (null == retval)
555 {
556 if (null == m_defaultErrorListener)
557 m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler();
558 retval = m_defaultErrorListener;
559 }
560
561 return retval;
562 }
563
564 /**
565 * Set the ErrorListener where errors and warnings are to be reported.
566 *
567 * @param listener A non-null ErrorListener reference.
568 */
569 public void setErrorListener(ErrorListener listener) throws IllegalArgumentException
570 {
571 if (listener == null)
572 throw new IllegalArgumentException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
573 m_errorListener = listener;
574 }
575
576
577 // =================================================
578
579 /** The TrAX URI Resolver for resolving URIs from the document(...)
580 * function to source tree nodes. */
581 private URIResolver m_uriResolver;
582
583 /**
584 * Get the URIResolver associated with this execution context.
585 *
586 * @return a URI resolver, which may be null.
587 */
588 public final URIResolver getURIResolver()
589 {
590 return m_uriResolver;
591 }
592
593 /**
594 * Set the URIResolver associated with this execution context.
595 *
596 * @param resolver the URIResolver to be associated with this
597 * execution context, may be null to clear an already set resolver.
598 */
599 public void setURIResolver(URIResolver resolver)
600 {
601 m_uriResolver = resolver;
602 }
603
604 // =================================================
605
606 /** The reader of the primary source tree. */
607 public XMLReader m_primaryReader;
608
609 /**
610 * Get primary XMLReader associated with this execution context.
611 *
612 * @return The reader of the primary source tree.
613 */
614 public final XMLReader getPrimaryReader()
615 {
616 return m_primaryReader;
617 }
618
619 /**
620 * Set primary XMLReader associated with this execution context.
621 *
622 * @param reader The reader of the primary source tree.
623 */
624 public void setPrimaryReader(XMLReader reader)
625 {
626 m_primaryReader = reader;
627 }
628
629 // =================================================
630
631
632 /** Misnamed string manager for XPath messages. */
633 // private static XSLMessages m_XSLMessages = new XSLMessages();
634
635 /**
636 * Tell the user of an assertion error, and probably throw an
637 * exception.
638 *
639 * @param b If false, a TransformerException will be thrown.
640 * @param msg The assertion message, which should be informative.
641 *
642 * @throws javax.xml.transform.TransformerException if b is false.
643 */
644 private void assertion(boolean b, String msg) throws javax.xml.transform.TransformerException
645 {
646 if (!b)
647 {
648 ErrorListener errorHandler = getErrorListener();
649
650 if (errorHandler != null)
651 {
652 errorHandler.fatalError(
653 new TransformerException(
654 XSLMessages.createMessage(
655 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
656 new Object[]{ msg }), (SAXSourceLocator)this.getSAXLocator()));
657 }
658 }
659 }
660
661 //==========================================================
662 // SECTION: Execution context state tracking
663 //==========================================================
664
665 /**
666 * The current context node list.
667 */
668 private Stack m_contextNodeLists = new Stack();
669
670 public Stack getContextNodeListsStack() { return m_contextNodeLists; }
671 public void setContextNodeListsStack(Stack s) { m_contextNodeLists = s; }
672
673 /**
674 * Get the current context node list.
675 *
676 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
677 * also refered to here as a <term>context node list</term>.
678 */
679 public final DTMIterator getContextNodeList()
680 {
681
682 if (m_contextNodeLists.size() > 0)
683 return (DTMIterator) m_contextNodeLists.peek();
684 else
685 return null;
686 }
687
688 /**
689 * Set the current context node list.
690 *
691 * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
692 * also refered to here as a <term>context node list</term>.
693 * @xsl.usage internal
694 */
695 public final void pushContextNodeList(DTMIterator nl)
696 {
697 m_contextNodeLists.push(nl);
698 }
699
700 /**
701 * Pop the current context node list.
702 * @xsl.usage internal
703 */
704 public final void popContextNodeList()
705 {
706 if(m_contextNodeLists.isEmpty())
707 System.err.println("Warning: popContextNodeList when stack is empty!");
708 else
709 m_contextNodeLists.pop();
710 }
711
712 /**
713 * The ammount to use for stacks that record information during the
714 * recursive execution.
715 */
716 public static final int RECURSIONLIMIT = (1024*4);
717
718 /** The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects.
719 * Not to be confused with the current node list. %REVIEW% Note that there
720 * are no bounds check and resize for this stack, so if it is blown, it's all
721 * over. */
722 private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT);
723
724 // private NodeVector m_currentNodes = new NodeVector();
725
726 public IntStack getCurrentNodeStack() {return m_currentNodes; }
727 public void setCurrentNodeStack(IntStack nv) { m_currentNodes = nv; }
728
729 /**
730 * Get the current context node.
731 *
732 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
733 */
734 public final int getCurrentNode()
735 {
736 return m_currentNodes.peek();
737 }
738
739 /**
740 * Set the current context node and expression node.
741 *
742 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
743 * @param en the sub-expression context node.
744 */
745 public final void pushCurrentNodeAndExpression(int cn, int en)
746 {
747 m_currentNodes.push(cn);
748 m_currentExpressionNodes.push(cn);
749 }
750
751 /**
752 * Set the current context node.
753 */
754 public final void popCurrentNodeAndExpression()
755 {
756 m_currentNodes.quickPop(1);
757 m_currentExpressionNodes.quickPop(1);
758 }
759
760 /**
761 * Push the current context node, expression node, and prefix resolver.
762 *
763 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
764 * @param en the sub-expression context node.
765 * @param nc the namespace context (prefix resolver.
766 */
767 public final void pushExpressionState(int cn, int en, PrefixResolver nc)
768 {
769 m_currentNodes.push(cn);
770 m_currentExpressionNodes.push(cn);
771 m_prefixResolvers.push(nc);
772 }
773
774 /**
775 * Pop the current context node, expression node, and prefix resolver.
776 */
777 public final void popExpressionState()
778 {
779 m_currentNodes.quickPop(1);
780 m_currentExpressionNodes.quickPop(1);
781 m_prefixResolvers.pop();
782 }
783
784
785
786 /**
787 * Set the current context node.
788 *
789 * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
790 */
791 public final void pushCurrentNode(int n)
792 {
793 m_currentNodes.push(n);
794 }
795
796 /**
797 * Pop the current context node.
798 */
799 public final void popCurrentNode()
800 {
801 m_currentNodes.quickPop(1);
802 }
803
804 /**
805 * Set the current predicate root.
806 */
807 public final void pushPredicateRoot(int n)
808 {
809 m_predicateRoots.push(n);
810 }
811
812 /**
813 * Pop the current predicate root.
814 */
815 public final void popPredicateRoot()
816 {
817 m_predicateRoots.popQuick();
818 }
819
820 /**
821 * Get the current predicate root.
822 */
823 public final int getPredicateRoot()
824 {
825 return m_predicateRoots.peepOrNull();
826 }
827
828 /**
829 * Set the current location path iterator root.
830 */
831 public final void pushIteratorRoot(int n)
832 {
833 m_iteratorRoots.push(n);
834 }
835
836 /**
837 * Pop the current location path iterator root.
838 */
839 public final void popIteratorRoot()
840 {
841 m_iteratorRoots.popQuick();
842 }
843
844 /**
845 * Get the current location path iterator root.
846 */
847 public final int getIteratorRoot()
848 {
849 return m_iteratorRoots.peepOrNull();
850 }
851
852 /** A stack of the current sub-expression nodes. */
853 private NodeVector m_iteratorRoots = new NodeVector();
854
855 /** A stack of the current sub-expression nodes. */
856 private NodeVector m_predicateRoots = new NodeVector();
857
858 /** A stack of the current sub-expression nodes. */
859 private IntStack m_currentExpressionNodes = new IntStack(RECURSIONLIMIT);
860
861
862 public IntStack getCurrentExpressionNodeStack() { return m_currentExpressionNodes; }
863 public void setCurrentExpressionNodeStack(IntStack nv) { m_currentExpressionNodes = nv; }
864
865 private IntStack m_predicatePos = new IntStack();
866
867 public final int getPredicatePos()
868 {
869 return m_predicatePos.peek();
870 }
871
872 public final void pushPredicatePos(int n)
873 {
874 m_predicatePos.push(n);
875 }
876
877 public final void popPredicatePos()
878 {
879 m_predicatePos.pop();
880 }
881
882 /**
883 * Get the current node that is the expression's context (i.e. for current() support).
884 *
885 * @return The current sub-expression node.
886 */
887 public final int getCurrentExpressionNode()
888 {
889 return m_currentExpressionNodes.peek();
890 }
891
892 /**
893 * Set the current node that is the expression's context (i.e. for current() support).
894 *
895 * @param n The sub-expression node to be current.
896 */
897 public final void pushCurrentExpressionNode(int n)
898 {
899 m_currentExpressionNodes.push(n);
900 }
901
902 /**
903 * Pop the current node that is the expression's context
904 * (i.e. for current() support).
905 */
906 public final void popCurrentExpressionNode()
907 {
908 m_currentExpressionNodes.quickPop(1);
909 }
910
911 private ObjectStack m_prefixResolvers
912 = new ObjectStack(RECURSIONLIMIT);
913
914 /**
915 * Get the current namespace context for the xpath.
916 *
917 * @return the current prefix resolver for resolving prefixes to
918 * namespace URLs.
919 */
920 public final PrefixResolver getNamespaceContext()
921 {
922 return (PrefixResolver) m_prefixResolvers.peek();
923 }
924
925 /**
926 * Get the current namespace context for the xpath.
927 *
928 * @param pr the prefix resolver to be used for resolving prefixes to
929 * namespace URLs.
930 */
931 public final void setNamespaceContext(PrefixResolver pr)
932 {
933 m_prefixResolvers.setTop(pr);
934 }
935
936 /**
937 * Push a current namespace context for the xpath.
938 *
939 * @param pr the prefix resolver to be used for resolving prefixes to
940 * namespace URLs.
941 */
942 public final void pushNamespaceContext(PrefixResolver pr)
943 {
944 m_prefixResolvers.push(pr);
945 }
946
947 /**
948 * Just increment the namespace contest stack, so that setNamespaceContext
949 * can be used on the slot.
950 */
951 public final void pushNamespaceContextNull()
952 {
953 m_prefixResolvers.push(null);
954 }
955
956 /**
957 * Pop the current namespace context for the xpath.
958 */
959 public final void popNamespaceContext()
960 {
961 m_prefixResolvers.pop();
962 }
963
964 //==========================================================
965 // SECTION: Current TreeWalker contexts (for internal use)
966 //==========================================================
967
968 /**
969 * Stack of AxesIterators.
970 */
971 private Stack m_axesIteratorStack = new Stack();
972
973 public Stack getAxesIteratorStackStacks() { return m_axesIteratorStack; }
974 public void setAxesIteratorStackStacks(Stack s) { m_axesIteratorStack = s; }
975
976 /**
977 * Push a TreeWalker on the stack.
978 *
979 * @param iter A sub-context AxesWalker.
980 * @xsl.usage internal
981 */
982 public final void pushSubContextList(SubContextList iter)
983 {
984 m_axesIteratorStack.push(iter);
985 }
986
987 /**
988 * Pop the last pushed axes iterator.
989 * @xsl.usage internal
990 */
991 public final void popSubContextList()
992 {
993 m_axesIteratorStack.pop();
994 }
995
996 /**
997 * Get the current axes iterator, or return null if none.
998 *
999 * @return the sub-context node list.
1000 * @xsl.usage internal
1001 */
1002 public SubContextList getSubContextList()
1003 {
1004 return m_axesIteratorStack.isEmpty()
1005 ? null : (SubContextList) m_axesIteratorStack.peek();
1006 }
1007
1008 /**
1009 * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>
1010 * as defined by the XSLT spec.
1011 *
1012 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>.
1013 * @xsl.usage internal
1014 */
1015
1016 public org.apache.xpath.axes.SubContextList getCurrentNodeList()
1017 {
1018 return m_axesIteratorStack.isEmpty()
1019 ? null : (SubContextList) m_axesIteratorStack.elementAt(0);
1020 }
1021 //==========================================================
1022 // SECTION: Implementation of ExpressionContext interface
1023 //==========================================================
1024
1025 /**
1026 * Get the current context node.
1027 * @return The current context node.
1028 */
1029 public final int getContextNode()
1030 {
1031 return this.getCurrentNode();
1032 }
1033
1034 /**
1035 * Get the current context node list.
1036 * @return An iterator for the current context list, as
1037 * defined in XSLT.
1038 */
1039 public final DTMIterator getContextNodes()
1040 {
1041
1042 try
1043 {
1044 DTMIterator cnl = getContextNodeList();
1045
1046 if (null != cnl)
1047 return cnl.cloneWithReset();
1048 else
1049 return null; // for now... this might ought to be an empty iterator.
1050 }
1051 catch (CloneNotSupportedException cnse)
1052 {
1053 return null; // error reporting?
1054 }
1055 }
1056
1057 XPathExpressionContext expressionContext = new XPathExpressionContext();
1058
1059 /**
1060 * The the expression context for extensions for this context.
1061 *
1062 * @return An object that implements the ExpressionContext.
1063 */
1064 public ExpressionContext getExpressionContext()
1065 {
1066 return expressionContext;
1067 }
1068
1069 public class XPathExpressionContext implements ExpressionContext
1070 {
1071 /**
1072 * Return the XPathContext associated with this XPathExpressionContext.
1073 * Extensions should use this judiciously and only when special processing
1074 * requirements cannot be met another way. Consider requesting an enhancement
1075 * to the ExpressionContext interface to avoid having to call this method.
1076 * @return the XPathContext associated with this XPathExpressionContext.
1077 */
1078 public XPathContext getXPathContext()
1079 {
1080 return XPathContext.this;
1081 }
1082
1083 /**
1084 * Return the DTMManager object. Though XPathContext context extends
1085 * the DTMManager, it really is a proxy for the real DTMManager. If a
1086 * caller needs to make a lot of calls to the DTMManager, it is faster
1087 * if it gets the real one from this function.
1088 */
1089 public DTMManager getDTMManager()
1090 {
1091 return m_dtmManager;
1092 }
1093
1094 /**
1095 * Get the current context node.
1096 * @return The current context node.
1097 */
1098 public org.w3c.dom.Node getContextNode()
1099 {
1100 int context = getCurrentNode();
1101
1102 return getDTM(context).getNode(context);
1103 }
1104
1105 /**
1106 * Get the current context node list.
1107 * @return An iterator for the current context list, as
1108 * defined in XSLT.
1109 */
1110 public org.w3c.dom.traversal.NodeIterator getContextNodes()
1111 {
1112 return new org.apache.xml.dtm.ref.DTMNodeIterator(getContextNodeList());
1113 }
1114
1115 /**
1116 * Get the error listener.
1117 * @return The registered error listener.
1118 */
1119 public ErrorListener getErrorListener()
1120 {
1121 return XPathContext.this.getErrorListener();
1122 }
1123
1124 /**
1125 * Get the value of a node as a number.
1126 * @param n Node to be converted to a number. May be null.
1127 * @return value of n as a number.
1128 */
1129 public double toNumber(org.w3c.dom.Node n)
1130 {
1131 // %REVIEW% You can't get much uglier than this...
1132 int nodeHandle = getDTMHandleFromNode(n);
1133 DTM dtm = getDTM(nodeHandle);
1134 XString xobj = (XString)dtm.getStringValue(nodeHandle);
1135 return xobj.num();
1136 }
1137
1138 /**
1139 * Get the value of a node as a string.
1140 * @param n Node to be converted to a string. May be null.
1141 * @return value of n as a string, or an empty string if n is null.
1142 */
1143 public String toString(org.w3c.dom.Node n)
1144 {
1145 // %REVIEW% You can't get much uglier than this...
1146 int nodeHandle = getDTMHandleFromNode(n);
1147 DTM dtm = getDTM(nodeHandle);
1148 XMLString strVal = dtm.getStringValue(nodeHandle);
1149 return strVal.toString();
1150 }
1151
1152 /**
1153 * Get a variable based on it's qualified name.
1154 * @param qname The qualified name of the variable.
1155 * @return The evaluated value of the variable.
1156 * @throws javax.xml.transform.TransformerException
1157 */
1158
1159 public final XObject getVariableOrParam(org.apache.xml.utils.QName qname)
1160 throws javax.xml.transform.TransformerException
1161 {
1162 return m_variableStacks.getVariableOrParam(XPathContext.this, qname);
1163 }
1164
1165 }
1166
1167 /**
1168 * Get a DTM to be used as a container for a global Result Tree
1169 * Fragment. This will always be an instance of (derived from? equivalent to?)
1170 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX
1171 * output to it. It may be a single DTM containing for multiple fragments,
1172 * if the implementation supports that.
1173 *
1174 * Note: The distinction between this method and getRTFDTM() is that the latter
1175 * allocates space from the dynamic variable stack (m_rtfdtm_stack), which may
1176 * be pruned away again as the templates which defined those variables are exited.
1177 * Global variables may be bound late (see XUnresolvedVariable), and never want to
1178 * be discarded, hence we need to allocate them separately and don't actually need
1179 * a stack to track them.
1180 *
1181 * @return a non-null DTM reference.
1182 */
1183 public DTM getGlobalRTFDTM()
1184 {
1185 // We probably should _NOT_ be applying whitespace filtering at this stage!
1186 //
1187 // Some magic has been applied in DTMManagerDefault to recognize this set of options
1188 // and generate an instance of DTM which can contain multiple documents
1189 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
1190 // I didn't want to change the manager API at this time, or expose
1191 // too many dependencies on its internals. (Ideally, I'd like to move
1192 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
1193 // specify the subclass here.)
1194
1195 // If it doesn't exist, or if the one already existing is in the middle of
1196 // being constructed, we need to obtain a new DTM to write into. I'm not sure
1197 // the latter will ever arise, but I'd rather be just a bit paranoid..
1198 if( m_global_rtfdtm==null || m_global_rtfdtm.isTreeIncomplete() )
1199 {
1200 m_global_rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
1201 }
1202 return m_global_rtfdtm;
1203 }
1204
1205
1206
1207
1208 /**
1209 * Get a DTM to be used as a container for a dynamic Result Tree
1210 * Fragment. This will always be an instance of (derived from? equivalent to?)
1211 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX
1212 * output to it. It may be a single DTM containing for multiple fragments,
1213 * if the implementation supports that.
1214 *
1215 * @return a non-null DTM reference.
1216 */
1217 public DTM getRTFDTM()
1218 {
1219 SAX2RTFDTM rtfdtm;
1220
1221 // We probably should _NOT_ be applying whitespace filtering at this stage!
1222 //
1223 // Some magic has been applied in DTMManagerDefault to recognize this set of options
1224 // and generate an instance of DTM which can contain multiple documents
1225 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
1226 // I didn't want to change the manager API at this time, or expose
1227 // too many dependencies on its internals. (Ideally, I'd like to move
1228 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
1229 // specify the subclass here.)
1230
1231 if(m_rtfdtm_stack==null)
1232 {
1233 m_rtfdtm_stack=new Vector();
1234 rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
1235 m_rtfdtm_stack.addElement(rtfdtm);
1236 ++m_which_rtfdtm;
1237 }
1238 else if(m_which_rtfdtm<0)
1239 {
1240 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(++m_which_rtfdtm);
1241 }
1242 else
1243 {
1244 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
1245
1246 // It might already be under construction -- the classic example would be
1247 // an xsl:variable which uses xsl:call-template as part of its value. To
1248 // handle this recursion, we have to start a new RTF DTM, pushing the old
1249 // one onto a stack so we can return to it. This is not as uncommon a case
1250 // as we might wish, unfortunately, as some folks insist on coding XSLT
1251 // as if it were a procedural language...
1252 if(rtfdtm.isTreeIncomplete())
1253 {
1254 if(++m_which_rtfdtm < m_rtfdtm_stack.size())
1255 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
1256 else
1257 {
1258 rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
1259 m_rtfdtm_stack.addElement(rtfdtm);
1260 }
1261 }
1262 }
1263
1264 return rtfdtm;
1265 }
1266
1267 /** Push the RTFDTM's context mark, to allows discarding RTFs added after this
1268 * point. (If it doesn't exist we don't push, since we might still be able to
1269 * get away with not creating it. That requires that excessive pops be harmless.)
1270 * */
1271 public void pushRTFContext()
1272 {
1273 m_last_pushed_rtfdtm.push(m_which_rtfdtm);
1274 if(null!=m_rtfdtm_stack)
1275 ((SAX2RTFDTM)(getRTFDTM())).pushRewindMark();
1276 }
1277
1278 /** Pop the RTFDTM's context mark. This discards any RTFs added after the last
1279 * mark was set.
1280 *
1281 * If there is no RTF DTM, there's nothing to pop so this
1282 * becomes a no-op. If pushes were issued before this was called, we count on
1283 * the fact that popRewindMark is defined such that overpopping just resets
1284 * to empty.
1285 *
1286 * Complicating factor: We need to handle the case of popping back to a previous
1287 * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose.
1288 * Basically: If pop says this DTM is now empty, then return to the previous
1289 * if one exists, in whatever state we left it in. UGLY, but hopefully the
1290 * situation which forces us to consider this will arise exceedingly rarely.
1291 * */
1292 public void popRTFContext()
1293 {
1294 int previous=m_last_pushed_rtfdtm.pop();
1295 if(null==m_rtfdtm_stack)
1296 return;
1297
1298 if(m_which_rtfdtm==previous)
1299 {
1300 if(previous>=0) // guard against none-active
1301 {
1302 boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(previous))).popRewindMark();
1303 }
1304 }
1305 else while(m_which_rtfdtm!=previous)
1306 {
1307 // Empty each DTM before popping, so it's ready for reuse
1308 // _DON'T_ pop the previous, since it's still open (which is why we
1309 // stacked up more of these) and did not receive a mark.
1310 boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark();
1311 --m_which_rtfdtm;
1312 }
1313 }
1314
1315 /**
1316 * Gets DTMXRTreeFrag object if one has already been created.
1317 * Creates new DTMXRTreeFrag object and adds to m_DTMXRTreeFrags HashMap,
1318 * otherwise.
1319 * @param dtmIdentity
1320 * @return DTMXRTreeFrag
1321 */
1322 public DTMXRTreeFrag getDTMXRTreeFrag(int dtmIdentity){
1323 if(m_DTMXRTreeFrags == null){
1324 m_DTMXRTreeFrags = new HashMap();
1325 }
1326
1327 if(m_DTMXRTreeFrags.containsKey(new Integer(dtmIdentity))){
1328 return (DTMXRTreeFrag)m_DTMXRTreeFrags.get(new Integer(dtmIdentity));
1329 }else{
1330 final DTMXRTreeFrag frag = new DTMXRTreeFrag(dtmIdentity,this);
1331 m_DTMXRTreeFrags.put(new Integer(dtmIdentity),frag);
1332 return frag ;
1333 }
1334 }
1335
1336 /**
1337 * Cleans DTMXRTreeFrag objects by removing references
1338 * to DTM and XPathContext objects.
1339 */
1340 private final void releaseDTMXRTreeFrags(){
1341 if(m_DTMXRTreeFrags == null){
1342 return;
1343 }
1344 final Iterator iter = (m_DTMXRTreeFrags.values()).iterator();
1345 while(iter.hasNext()){
1346 DTMXRTreeFrag frag = (DTMXRTreeFrag)iter.next();
1347 frag.destruct();
1348 iter.remove();
1349 }
1350 m_DTMXRTreeFrags = null;
1351 }
1352 }