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