001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: PredicatedNodeTest.java,v 1.17 2005/01/23 01:02:11 mcnamara Exp $
018: */
019: package org.apache.xpath.axes;
020:
021: import org.apache.xml.dtm.DTM;
022: import org.apache.xml.dtm.DTMIterator;
023: import org.apache.xml.utils.PrefixResolver;
024: import org.apache.xpath.Expression;
025: import org.apache.xpath.ExpressionOwner;
026: import org.apache.xpath.XPathContext;
027: import org.apache.xpath.XPathVisitor;
028: import org.apache.xpath.compiler.Compiler;
029: import org.apache.xpath.objects.XObject;
030: import org.apache.xpath.patterns.NodeTest;
031:
032: public abstract class PredicatedNodeTest extends NodeTest implements
033: SubContextList {
034: static final long serialVersionUID = -6193530757296377351L;
035:
036: /**
037: * Construct an AxesWalker using a LocPathIterator.
038: *
039: * @param locPathIterator non-null reference to the parent iterator.
040: */
041: PredicatedNodeTest(LocPathIterator locPathIterator) {
042: m_lpi = locPathIterator;
043: }
044:
045: /**
046: * Construct an AxesWalker. The location path iterator will have to be set
047: * before use.
048: */
049: PredicatedNodeTest() {
050: }
051:
052: /**
053: * Read the object from a serialization stream.
054: *
055: * @param stream Input stream to read from
056: *
057: * @throws java.io.IOException
058: * @throws javax.xml.transform.TransformerException
059: */
060: private void readObject(java.io.ObjectInputStream stream)
061: throws java.io.IOException,
062: javax.xml.transform.TransformerException {
063: try {
064: stream.defaultReadObject();
065: m_predicateIndex = -1;
066: resetProximityPositions();
067: } catch (ClassNotFoundException cnfe) {
068: throw new javax.xml.transform.TransformerException(cnfe);
069: }
070: }
071:
072: /**
073: * Get a cloned PrdicatedNodeTest.
074: *
075: * @return A new PredicatedNodeTest that can be used without mutating this one.
076: *
077: * @throws CloneNotSupportedException
078: */
079: public Object clone() throws CloneNotSupportedException {
080: // Do not access the location path itterator during this operation!
081:
082: PredicatedNodeTest clone = (PredicatedNodeTest) super .clone();
083:
084: if ((null != this .m_proximityPositions)
085: && (this .m_proximityPositions == clone.m_proximityPositions)) {
086: clone.m_proximityPositions = new int[this .m_proximityPositions.length];
087:
088: System.arraycopy(this .m_proximityPositions, 0,
089: clone.m_proximityPositions, 0,
090: this .m_proximityPositions.length);
091: }
092:
093: if (clone.m_lpi == this )
094: clone.m_lpi = (LocPathIterator) clone;
095:
096: return clone;
097: }
098:
099: // Only for clones for findLastPos. See bug4638.
100: protected int m_predCount = -1;
101:
102: /**
103: * Get the number of predicates that this walker has.
104: *
105: * @return the number of predicates that this walker has.
106: */
107: public int getPredicateCount() {
108: if (-1 == m_predCount)
109: return (null == m_predicates) ? 0 : m_predicates.length;
110: else
111: return m_predCount;
112: }
113:
114: /**
115: * Set the number of predicates that this walker has. This does more
116: * that one would think, as it creates a new predicate array of the
117: * size of the count argument, and copies count predicates into the new
118: * one from the old, and then reassigns the predicates value. All this
119: * to keep from having to have a predicate count value.
120: *
121: * @param count The number of predicates, which must be equal or less
122: * than the existing count.
123: */
124: public void setPredicateCount(int count) {
125: if (count > 0) {
126: Expression[] newPredicates = new Expression[count];
127: for (int i = 0; i < count; i++) {
128: newPredicates[i] = m_predicates[i];
129: }
130: m_predicates = newPredicates;
131: } else
132: m_predicates = null;
133:
134: }
135:
136: /**
137: * Init predicate info.
138: *
139: * @param compiler The Compiler object that has information about this
140: * walker in the op map.
141: * @param opPos The op code position of this location step.
142: *
143: * @throws javax.xml.transform.TransformerException
144: */
145: protected void initPredicateInfo(Compiler compiler, int opPos)
146: throws javax.xml.transform.TransformerException {
147:
148: int pos = compiler.getFirstPredicateOpPos(opPos);
149:
150: if (pos > 0) {
151: m_predicates = compiler.getCompiledPredicates(pos);
152: if (null != m_predicates) {
153: for (int i = 0; i < m_predicates.length; i++) {
154: m_predicates[i].exprSetParent(this );
155: }
156: }
157: }
158: }
159:
160: /**
161: * Get a predicate expression at the given index.
162: *
163: *
164: * @param index Index of the predicate.
165: *
166: * @return A predicate expression.
167: */
168: public Expression getPredicate(int index) {
169: return m_predicates[index];
170: }
171:
172: /**
173: * Get the current sub-context position.
174: *
175: * @return The node position of this walker in the sub-context node list.
176: */
177: public int getProximityPosition() {
178:
179: // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
180: return getProximityPosition(m_predicateIndex);
181: }
182:
183: /**
184: * Get the current sub-context position.
185: *
186: * @param xctxt The XPath runtime context.
187: *
188: * @return The node position of this walker in the sub-context node list.
189: */
190: public int getProximityPosition(XPathContext xctxt) {
191: return getProximityPosition();
192: }
193:
194: /**
195: * Get the index of the last node that can be itterated to.
196: *
197: *
198: * @param xctxt XPath runtime context.
199: *
200: * @return the index of the last node that can be itterated to.
201: */
202: public abstract int getLastPos(XPathContext xctxt);
203:
204: /**
205: * Get the current sub-context position.
206: *
207: * @param predicateIndex The index of the predicate where the proximity
208: * should be taken from.
209: *
210: * @return The node position of this walker in the sub-context node list.
211: */
212: protected int getProximityPosition(int predicateIndex) {
213: return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex]
214: : 0;
215: }
216:
217: /**
218: * Reset the proximity positions counts.
219: */
220: public void resetProximityPositions() {
221: int nPredicates = getPredicateCount();
222: if (nPredicates > 0) {
223: if (null == m_proximityPositions)
224: m_proximityPositions = new int[nPredicates];
225:
226: for (int i = 0; i < nPredicates; i++) {
227: try {
228: initProximityPosition(i);
229: } catch (Exception e) {
230: // TODO: Fix this...
231: throw new org.apache.xml.utils.WrappedRuntimeException(
232: e);
233: }
234: }
235: }
236: }
237:
238: /**
239: * Init the proximity position to zero for a forward axes.
240: *
241: * @param i The index into the m_proximityPositions array.
242: *
243: * @throws javax.xml.transform.TransformerException
244: */
245: public void initProximityPosition(int i)
246: throws javax.xml.transform.TransformerException {
247: m_proximityPositions[i] = 0;
248: }
249:
250: /**
251: * Count forward one proximity position.
252: *
253: * @param i The index into the m_proximityPositions array, where the increment
254: * will occur.
255: */
256: protected void countProximityPosition(int i) {
257: // Note that in the case of a UnionChildIterator, this may be a
258: // static object and so m_proximityPositions may indeed be null!
259: int[] pp = m_proximityPositions;
260: if ((null != pp) && (i < pp.length))
261: pp[i]++;
262: }
263:
264: /**
265: * Tells if this is a reverse axes.
266: *
267: * @return false, unless a derived class overrides.
268: */
269: public boolean isReverseAxes() {
270: return false;
271: }
272:
273: /**
274: * Get which predicate is executing.
275: *
276: * @return The current predicate index, or -1 if no predicate is executing.
277: */
278: public int getPredicateIndex() {
279: return m_predicateIndex;
280: }
281:
282: /**
283: * Process the predicates.
284: *
285: * @param context The current context node.
286: * @param xctxt The XPath runtime context.
287: *
288: * @return the result of executing the predicate expressions.
289: *
290: * @throws javax.xml.transform.TransformerException
291: */
292: boolean executePredicates(int context, XPathContext xctxt)
293: throws javax.xml.transform.TransformerException {
294:
295: int nPredicates = getPredicateCount();
296: // System.out.println("nPredicates: "+nPredicates);
297: if (nPredicates == 0)
298: return true;
299:
300: PrefixResolver savedResolver = xctxt.getNamespaceContext();
301:
302: try {
303: m_predicateIndex = 0;
304: xctxt.pushSubContextList(this );
305: xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
306: xctxt.pushCurrentNode(context);
307:
308: for (int i = 0; i < nPredicates; i++) {
309: // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
310: XObject pred = m_predicates[i].execute(xctxt);
311: // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
312: // System.out.println("pred.getType(): "+pred.getType());
313: if (XObject.CLASS_NUMBER == pred.getType()) {
314: if (DEBUG_PREDICATECOUNTING) {
315: System.out.flush();
316: System.out
317: .println("\n===== start predicate count ========");
318: System.out.println("m_predicateIndex: "
319: + m_predicateIndex);
320: // System.out.println("getProximityPosition(m_predicateIndex): "
321: // + getProximityPosition(m_predicateIndex));
322: System.out.println("pred.num(): " + pred.num());
323: }
324:
325: int proxPos = this
326: .getProximityPosition(m_predicateIndex);
327: int predIndex = (int) pred.num();
328: if (proxPos != predIndex) {
329: if (DEBUG_PREDICATECOUNTING) {
330: System.out.println("\nnode context: "
331: + nodeToString(context));
332: System.out
333: .println("index predicate is false: "
334: + proxPos);
335: System.out
336: .println("\n===== end predicate count ========");
337: }
338: return false;
339: } else if (DEBUG_PREDICATECOUNTING) {
340: System.out.println("\nnode context: "
341: + nodeToString(context));
342: System.out.println("index predicate is true: "
343: + proxPos);
344: System.out
345: .println("\n===== end predicate count ========");
346: }
347:
348: // If there is a proximity index that will not change during the
349: // course of itteration, then we know there can be no more true
350: // occurances of this predicate, so flag that we're done after
351: // this.
352: //
353: // bugzilla 14365
354: // We can't set m_foundLast = true unless we're sure that -all-
355: // remaining parameters are stable, or else last() fails. Fixed so
356: // only sets m_foundLast if on the last predicate
357: if (m_predicates[i].isStableNumber()
358: && i == nPredicates - 1) {
359: m_foundLast = true;
360: }
361: } else if (!pred.bool())
362: return false;
363:
364: countProximityPosition(++m_predicateIndex);
365: }
366: } finally {
367: xctxt.popCurrentNode();
368: xctxt.popNamespaceContext();
369: xctxt.popSubContextList();
370: m_predicateIndex = -1;
371: }
372:
373: return true;
374: }
375:
376: /**
377: * This function is used to fixup variables from QNames to stack frame
378: * indexes at stylesheet build time.
379: * @param vars List of QNames that correspond to variables. This list
380: * should be searched backwards for the first qualified name that
381: * corresponds to the variable reference qname. The position of the
382: * QName in the vector from the start of the vector will be its position
383: * in the stack frame (but variables above the globalsTop value will need
384: * to be offset to the current stack frame).
385: */
386: public void fixupVariables(java.util.Vector vars, int globalsSize) {
387: super .fixupVariables(vars, globalsSize);
388:
389: int nPredicates = getPredicateCount();
390:
391: for (int i = 0; i < nPredicates; i++) {
392: m_predicates[i].fixupVariables(vars, globalsSize);
393: }
394: }
395:
396: /**
397: * Diagnostics.
398: *
399: * @param n Node to give diagnostic information about, or null.
400: *
401: * @return Informative string about the argument.
402: */
403: protected String nodeToString(int n) {
404: if (DTM.NULL != n) {
405: DTM dtm = m_lpi.getXPathContext().getDTM(n);
406: return dtm.getNodeName(n) + "{" + (n + 1) + "}";
407: } else {
408: return "null";
409: }
410: }
411:
412: //=============== NodeFilter Implementation ===============
413:
414: /**
415: * Test whether a specified node is visible in the logical view of a
416: * TreeWalker or NodeIterator. This function will be called by the
417: * implementation of TreeWalker and NodeIterator; it is not intended to
418: * be called directly from user code.
419: * @param n The node to check to see if it passes the filter or not.
420: * @return a constant to determine whether the node is accepted,
421: * rejected, or skipped, as defined above .
422: */
423: public short acceptNode(int n) {
424:
425: XPathContext xctxt = m_lpi.getXPathContext();
426:
427: try {
428: xctxt.pushCurrentNode(n);
429:
430: XObject score = execute(xctxt, n);
431:
432: // System.out.println("\n::acceptNode - score: "+score.num()+"::");
433: if (score != NodeTest.SCORE_NONE) {
434: if (getPredicateCount() > 0) {
435: countProximityPosition(0);
436:
437: if (!executePredicates(n, xctxt))
438: return DTMIterator.FILTER_SKIP;
439: }
440:
441: return DTMIterator.FILTER_ACCEPT;
442: }
443: } catch (javax.xml.transform.TransformerException se) {
444:
445: // TODO: Fix this.
446: throw new RuntimeException(se.getMessage());
447: } finally {
448: xctxt.popCurrentNode();
449: }
450:
451: return DTMIterator.FILTER_SKIP;
452: }
453:
454: /**
455: * Get the owning location path iterator.
456: *
457: * @return the owning location path iterator, which should not be null.
458: */
459: public LocPathIterator getLocPathIterator() {
460: return m_lpi;
461: }
462:
463: /**
464: * Set the location path iterator owner for this walker. Besides
465: * initialization, this function is called during cloning operations.
466: *
467: * @param li non-null reference to the owning location path iterator.
468: */
469: public void setLocPathIterator(LocPathIterator li) {
470: m_lpi = li;
471: if (this != li)
472: li.exprSetParent(this );
473: }
474:
475: /**
476: * Tell if this expression or it's subexpressions can traverse outside
477: * the current subtree.
478: *
479: * @return true if traversal outside the context node's subtree can occur.
480: */
481: public boolean canTraverseOutsideSubtree() {
482: int n = getPredicateCount();
483: for (int i = 0; i < n; i++) {
484: if (getPredicate(i).canTraverseOutsideSubtree())
485: return true;
486: }
487: return false;
488: }
489:
490: /**
491: * This will traverse the heararchy, calling the visitor for
492: * each member. If the called visitor method returns
493: * false, the subtree should not be called.
494: *
495: * @param visitor The visitor whose appropriate method will be called.
496: */
497: public void callPredicateVisitors(XPathVisitor visitor) {
498: if (null != m_predicates) {
499: int n = m_predicates.length;
500: for (int i = 0; i < n; i++) {
501: ExpressionOwner predOwner = new PredOwner(i);
502: if (visitor.visitPredicate(predOwner, m_predicates[i])) {
503: m_predicates[i].callVisitors(predOwner, visitor);
504: }
505:
506: }
507: }
508: }
509:
510: /**
511: * @see Expression#deepEquals(Expression)
512: */
513: public boolean deepEquals(Expression expr) {
514: if (!super .deepEquals(expr))
515: return false;
516:
517: PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
518: if (null != m_predicates) {
519:
520: int n = m_predicates.length;
521: if ((null == pnt.m_predicates)
522: || (pnt.m_predicates.length != n))
523: return false;
524: for (int i = 0; i < n; i++) {
525: if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
526: return false;
527: }
528: } else if (null != pnt.m_predicates)
529: return false;
530:
531: return true;
532: }
533:
534: /** This is true if nextNode returns null. */
535: transient protected boolean m_foundLast = false;
536:
537: /** The owning location path iterator.
538: * @serial */
539: protected LocPathIterator m_lpi;
540:
541: /**
542: * Which predicate we are executing.
543: */
544: transient int m_predicateIndex = -1;
545:
546: /** The list of predicate expressions. Is static and does not need
547: * to be deep cloned.
548: * @serial
549: */
550: private Expression[] m_predicates;
551:
552: /**
553: * An array of counts that correspond to the number
554: * of predicates the step contains.
555: */
556: transient protected int[] m_proximityPositions;
557:
558: /** If true, diagnostic messages about predicate execution will be posted. */
559: static final boolean DEBUG_PREDICATECOUNTING = false;
560:
561: class PredOwner implements ExpressionOwner {
562: int m_index;
563:
564: PredOwner(int index) {
565: m_index = index;
566: }
567:
568: /**
569: * @see ExpressionOwner#getExpression()
570: */
571: public Expression getExpression() {
572: return m_predicates[m_index];
573: }
574:
575: /**
576: * @see ExpressionOwner#setExpression(Expression)
577: */
578: public void setExpression(Expression exp) {
579: exp.exprSetParent(PredicatedNodeTest.this);
580: m_predicates[m_index] = exp;
581: }
582: }
583:
584: }
|