1. package org.apache.xpath.axes;
  2. import java.io.IOException;
  3. import java.io.ObjectInputStream;
  4. import java.util.Vector;
  5. import javax.xml.transform.TransformerException;
  6. import org.apache.xml.dtm.DTM;
  7. import org.apache.xml.dtm.DTMIterator;
  8. import org.apache.xml.utils.PrefixResolver;
  9. import org.apache.xpath.Expression;
  10. import org.apache.xpath.ExpressionOwner;
  11. import org.apache.xpath.XPathContext;
  12. import org.apache.xpath.XPathVisitor;
  13. import org.apache.xpath.compiler.Compiler;
  14. import org.apache.xpath.objects.XObject;
  15. import org.apache.xpath.patterns.NodeTest;
  16. public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
  17. {
  18. /**
  19. * Construct an AxesWalker using a LocPathIterator.
  20. *
  21. * @param locPathIterator non-null reference to the parent iterator.
  22. */
  23. PredicatedNodeTest(LocPathIterator locPathIterator)
  24. {
  25. m_lpi = locPathIterator;
  26. }
  27. /**
  28. * Construct an AxesWalker. The location path iterator will have to be set
  29. * before use.
  30. */
  31. PredicatedNodeTest()
  32. {
  33. }
  34. /**
  35. * Read the object from a serialization stream.
  36. *
  37. * @param stream Input stream to read from
  38. *
  39. * @throws java.io.IOException
  40. * @throws javax.xml.transform.TransformerException
  41. */
  42. private void readObject(java.io.ObjectInputStream stream)
  43. throws java.io.IOException, javax.xml.transform.TransformerException
  44. {
  45. try
  46. {
  47. stream.defaultReadObject();
  48. m_predicateIndex = -1;
  49. resetProximityPositions();
  50. }
  51. catch (ClassNotFoundException cnfe)
  52. {
  53. throw new javax.xml.transform.TransformerException(cnfe);
  54. }
  55. }
  56. /**
  57. * Get a cloned PrdicatedNodeTest.
  58. *
  59. * @return A new PredicatedNodeTest that can be used without mutating this one.
  60. *
  61. * @throws CloneNotSupportedException
  62. */
  63. public Object clone() throws CloneNotSupportedException
  64. {
  65. // Do not access the location path itterator during this operation!
  66. PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
  67. if ((null != this.m_proximityPositions)
  68. && (this.m_proximityPositions == clone.m_proximityPositions))
  69. {
  70. clone.m_proximityPositions = new int[this.m_proximityPositions.length];
  71. System.arraycopy(this.m_proximityPositions, 0,
  72. clone.m_proximityPositions, 0,
  73. this.m_proximityPositions.length);
  74. }
  75. if(clone.m_lpi == this)
  76. clone.m_lpi = (LocPathIterator)clone;
  77. return clone;
  78. }
  79. // Only for clones for findLastPos. See bug4638.
  80. protected int m_predCount = -1;
  81. /**
  82. * Get the number of predicates that this walker has.
  83. *
  84. * @return the number of predicates that this walker has.
  85. */
  86. public int getPredicateCount()
  87. {
  88. if(-1 == m_predCount)
  89. return (null == m_predicates) ? 0 : m_predicates.length;
  90. else
  91. return m_predCount;
  92. }
  93. /**
  94. * Set the number of predicates that this walker has. This does more
  95. * that one would think, as it creates a new predicate array of the
  96. * size of the count argument, and copies count predicates into the new
  97. * one from the old, and then reassigns the predicates value. All this
  98. * to keep from having to have a predicate count value.
  99. *
  100. * @param count The number of predicates, which must be equal or less
  101. * than the existing count.
  102. */
  103. public void setPredicateCount(int count)
  104. {
  105. if(count > 0)
  106. {
  107. Expression[] newPredicates = new Expression[count];
  108. for (int i = 0; i < count; i++)
  109. {
  110. newPredicates[i] = m_predicates[i];
  111. }
  112. m_predicates = newPredicates;
  113. }
  114. else
  115. m_predicates = null;
  116. }
  117. /**
  118. * Init predicate info.
  119. *
  120. * @param compiler The Compiler object that has information about this
  121. * walker in the op map.
  122. * @param opPos The op code position of this location step.
  123. *
  124. * @throws javax.xml.transform.TransformerException
  125. */
  126. protected void initPredicateInfo(Compiler compiler, int opPos)
  127. throws javax.xml.transform.TransformerException
  128. {
  129. int pos = compiler.getFirstPredicateOpPos(opPos);
  130. if(pos > 0)
  131. {
  132. m_predicates = compiler.getCompiledPredicates(pos);
  133. if(null != m_predicates)
  134. {
  135. for(int i = 0; i < m_predicates.length; i++)
  136. {
  137. m_predicates[i].exprSetParent(this);
  138. }
  139. }
  140. }
  141. }
  142. /**
  143. * Get a predicate expression at the given index.
  144. *
  145. *
  146. * @param index Index of the predicate.
  147. *
  148. * @return A predicate expression.
  149. */
  150. public Expression getPredicate(int index)
  151. {
  152. return m_predicates[index];
  153. }
  154. /**
  155. * Get the current sub-context position.
  156. *
  157. * @return The node position of this walker in the sub-context node list.
  158. */
  159. public int getProximityPosition()
  160. {
  161. // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
  162. return getProximityPosition(m_predicateIndex);
  163. }
  164. /**
  165. * Get the current sub-context position.
  166. *
  167. * @param xctxt The XPath runtime context.
  168. *
  169. * @return The node position of this walker in the sub-context node list.
  170. */
  171. public int getProximityPosition(XPathContext xctxt)
  172. {
  173. return getProximityPosition();
  174. }
  175. /**
  176. * Get the index of the last node that can be itterated to.
  177. *
  178. *
  179. * @param xctxt XPath runtime context.
  180. *
  181. * @return the index of the last node that can be itterated to.
  182. */
  183. public abstract int getLastPos(XPathContext xctxt);
  184. /**
  185. * Get the current sub-context position.
  186. *
  187. * @param predicateIndex The index of the predicate where the proximity
  188. * should be taken from.
  189. *
  190. * @return The node position of this walker in the sub-context node list.
  191. */
  192. protected int getProximityPosition(int predicateIndex)
  193. {
  194. return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
  195. }
  196. /**
  197. * Reset the proximity positions counts.
  198. */
  199. public void resetProximityPositions()
  200. {
  201. int nPredicates = getPredicateCount();
  202. if (nPredicates > 0)
  203. {
  204. if (null == m_proximityPositions)
  205. m_proximityPositions = new int[nPredicates];
  206. for (int i = 0; i < nPredicates; i++)
  207. {
  208. try
  209. {
  210. initProximityPosition(i);
  211. }
  212. catch(Exception e)
  213. {
  214. // TODO: Fix this...
  215. throw new org.apache.xml.utils.WrappedRuntimeException(e);
  216. }
  217. }
  218. }
  219. }
  220. /**
  221. * Init the proximity position to zero for a forward axes.
  222. *
  223. * @param i The index into the m_proximityPositions array.
  224. *
  225. * @throws javax.xml.transform.TransformerException
  226. */
  227. public void initProximityPosition(int i) throws javax.xml.transform.TransformerException
  228. {
  229. m_proximityPositions[i] = 0;
  230. }
  231. /**
  232. * Count forward one proximity position.
  233. *
  234. * @param i The index into the m_proximityPositions array, where the increment
  235. * will occur.
  236. */
  237. protected void countProximityPosition(int i)
  238. {
  239. // Note that in the case of a UnionChildIterator, this may be a
  240. // static object and so m_proximityPositions may indeed be null!
  241. int[] pp = m_proximityPositions;
  242. if ((null != pp) && (i < pp.length))
  243. pp[i]++;
  244. }
  245. /**
  246. * Tells if this is a reverse axes.
  247. *
  248. * @return false, unless a derived class overrides.
  249. */
  250. public boolean isReverseAxes()
  251. {
  252. return false;
  253. }
  254. /**
  255. * Get which predicate is executing.
  256. *
  257. * @return The current predicate index, or -1 if no predicate is executing.
  258. */
  259. public int getPredicateIndex()
  260. {
  261. return m_predicateIndex;
  262. }
  263. /**
  264. * Process the predicates.
  265. *
  266. * @param context The current context node.
  267. * @param xctxt The XPath runtime context.
  268. *
  269. * @return the result of executing the predicate expressions.
  270. *
  271. * @throws javax.xml.transform.TransformerException
  272. */
  273. boolean executePredicates(int context, XPathContext xctxt)
  274. throws javax.xml.transform.TransformerException
  275. {
  276. int nPredicates = getPredicateCount();
  277. // System.out.println("nPredicates: "+nPredicates);
  278. if (nPredicates == 0)
  279. return true;
  280. PrefixResolver savedResolver = xctxt.getNamespaceContext();
  281. try
  282. {
  283. m_predicateIndex = 0;
  284. xctxt.pushSubContextList(this);
  285. xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
  286. xctxt.pushCurrentNode(context);
  287. for (int i = 0; i < nPredicates; i++)
  288. {
  289. // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
  290. XObject pred = m_predicates[i].execute(xctxt);
  291. // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
  292. // System.out.println("pred.getType(): "+pred.getType());
  293. if (XObject.CLASS_NUMBER == pred.getType())
  294. {
  295. if (DEBUG_PREDICATECOUNTING)
  296. {
  297. System.out.flush();
  298. System.out.println("\n===== start predicate count ========");
  299. System.out.println("m_predicateIndex: " + m_predicateIndex);
  300. // System.out.println("getProximityPosition(m_predicateIndex): "
  301. // + getProximityPosition(m_predicateIndex));
  302. System.out.println("pred.num(): " + pred.num());
  303. }
  304. int proxPos = this.getProximityPosition(m_predicateIndex);
  305. int predIndex = (int) pred.num();
  306. if (proxPos != predIndex)
  307. {
  308. if (DEBUG_PREDICATECOUNTING)
  309. {
  310. System.out.println("\nnode context: "+nodeToString(context));
  311. System.out.println("index predicate is false: "+proxPos);
  312. System.out.println("\n===== end predicate count ========");
  313. }
  314. return false;
  315. }
  316. else if (DEBUG_PREDICATECOUNTING)
  317. {
  318. System.out.println("\nnode context: "+nodeToString(context));
  319. System.out.println("index predicate is true: "+proxPos);
  320. System.out.println("\n===== end predicate count ========");
  321. }
  322. // If there is a proximity index that will not change during the
  323. // course of itteration, then we know there can be no more true
  324. // occurances of this predicate, so flag that we're done after
  325. // this.
  326. if(m_predicates[i].isStableNumber())
  327. {
  328. m_foundLast = true;
  329. }
  330. }
  331. else if (!pred.bool())
  332. return false;
  333. countProximityPosition(++m_predicateIndex);
  334. }
  335. }
  336. finally
  337. {
  338. xctxt.popCurrentNode();
  339. xctxt.popNamespaceContext();
  340. xctxt.popSubContextList();
  341. m_predicateIndex = -1;
  342. }
  343. return true;
  344. }
  345. /**
  346. * This function is used to fixup variables from QNames to stack frame
  347. * indexes at stylesheet build time.
  348. * @param vars List of QNames that correspond to variables. This list
  349. * should be searched backwards for the first qualified name that
  350. * corresponds to the variable reference qname. The position of the
  351. * QName in the vector from the start of the vector will be its position
  352. * in the stack frame (but variables above the globalsTop value will need
  353. * to be offset to the current stack frame).
  354. */
  355. public void fixupVariables(java.util.Vector vars, int globalsSize)
  356. {
  357. super.fixupVariables(vars, globalsSize);
  358. int nPredicates = getPredicateCount();
  359. for (int i = 0; i < nPredicates; i++)
  360. {
  361. m_predicates[i].fixupVariables(vars, globalsSize);
  362. }
  363. }
  364. /**
  365. * Diagnostics.
  366. *
  367. * @param n Node to give diagnostic information about, or null.
  368. *
  369. * @return Informative string about the argument.
  370. */
  371. protected String nodeToString(int n)
  372. {
  373. if(DTM.NULL != n)
  374. {
  375. DTM dtm = m_lpi.getXPathContext().getDTM(n);
  376. return dtm.getNodeName(n) + "{" + (n+1) + "}";
  377. }
  378. else
  379. {
  380. return "null";
  381. }
  382. }
  383. //=============== NodeFilter Implementation ===============
  384. /**
  385. * Test whether a specified node is visible in the logical view of a
  386. * TreeWalker or NodeIterator. This function will be called by the
  387. * implementation of TreeWalker and NodeIterator; it is not intended to
  388. * be called directly from user code.
  389. * @param n The node to check to see if it passes the filter or not.
  390. * @return a constant to determine whether the node is accepted,
  391. * rejected, or skipped, as defined above .
  392. */
  393. public short acceptNode(int n)
  394. {
  395. XPathContext xctxt = m_lpi.getXPathContext();
  396. try
  397. {
  398. xctxt.pushCurrentNode(n);
  399. XObject score = execute(xctxt, n);
  400. // System.out.println("\n::acceptNode - score: "+score.num()+"::");
  401. if (score != NodeTest.SCORE_NONE)
  402. {
  403. if (getPredicateCount() > 0)
  404. {
  405. countProximityPosition(0);
  406. if (!executePredicates(n, xctxt))
  407. return DTMIterator.FILTER_SKIP;
  408. }
  409. return DTMIterator.FILTER_ACCEPT;
  410. }
  411. }
  412. catch (javax.xml.transform.TransformerException se)
  413. {
  414. // TODO: Fix this.
  415. throw new RuntimeException(se.getMessage());
  416. }
  417. finally
  418. {
  419. xctxt.popCurrentNode();
  420. }
  421. return DTMIterator.FILTER_SKIP;
  422. }
  423. /**
  424. * Get the owning location path iterator.
  425. *
  426. * @return the owning location path iterator, which should not be null.
  427. */
  428. public LocPathIterator getLocPathIterator()
  429. {
  430. return m_lpi;
  431. }
  432. /**
  433. * Set the location path iterator owner for this walker. Besides
  434. * initialization, this function is called during cloning operations.
  435. *
  436. * @param li non-null reference to the owning location path iterator.
  437. */
  438. public void setLocPathIterator(LocPathIterator li)
  439. {
  440. m_lpi = li;
  441. if(this != li)
  442. li.exprSetParent(this);
  443. }
  444. /**
  445. * Tell if this expression or it's subexpressions can traverse outside
  446. * the current subtree.
  447. *
  448. * @return true if traversal outside the context node's subtree can occur.
  449. */
  450. public boolean canTraverseOutsideSubtree()
  451. {
  452. int n = getPredicateCount();
  453. for (int i = 0; i < n; i++)
  454. {
  455. if(getPredicate(i).canTraverseOutsideSubtree())
  456. return true;
  457. }
  458. return false;
  459. }
  460. /**
  461. * This will traverse the heararchy, calling the visitor for
  462. * each member. If the called visitor method returns
  463. * false, the subtree should not be called.
  464. *
  465. * @param owner The owner of the visitor, where that path may be
  466. * rewritten if needed.
  467. * @param visitor The visitor whose appropriate method will be called.
  468. */
  469. public void callPredicateVisitors(XPathVisitor visitor)
  470. {
  471. if (null != m_predicates)
  472. {
  473. int n = m_predicates.length;
  474. for (int i = 0; i < n; i++)
  475. {
  476. ExpressionOwner predOwner = new PredOwner(i);
  477. if (visitor.visitPredicate(predOwner, m_predicates[i]))
  478. {
  479. m_predicates[i].callVisitors(predOwner, visitor);
  480. }
  481. }
  482. }
  483. }
  484. /**
  485. * @see Expression#deepEquals(Expression)
  486. */
  487. public boolean deepEquals(Expression expr)
  488. {
  489. if (!super.deepEquals(expr))
  490. return false;
  491. PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
  492. if (null != m_predicates)
  493. {
  494. int n = m_predicates.length;
  495. if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n))
  496. return false;
  497. for (int i = 0; i < n; i++)
  498. {
  499. if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
  500. return false;
  501. }
  502. }
  503. else if (null != pnt.m_predicates)
  504. return false;
  505. return true;
  506. }
  507. /** This is true if nextNode returns null. */
  508. transient protected boolean m_foundLast = false;
  509. /** The owning location path iterator.
  510. * @serial */
  511. protected LocPathIterator m_lpi;
  512. /**
  513. * Which predicate we are executing.
  514. */
  515. transient int m_predicateIndex = -1;
  516. /** The list of predicate expressions. Is static and does not need
  517. * to be deep cloned.
  518. * @serial
  519. */
  520. private Expression[] m_predicates;
  521. /**
  522. * An array of counts that correspond to the number
  523. * of predicates the step contains.
  524. */
  525. transient protected int[] m_proximityPositions;
  526. /** If true, diagnostic messages about predicate execution will be posted. */
  527. static final boolean DEBUG_PREDICATECOUNTING = false;
  528. class PredOwner implements ExpressionOwner
  529. {
  530. int m_index;
  531. PredOwner(int index)
  532. {
  533. m_index = index;
  534. }
  535. /**
  536. * @see ExpressionOwner#getExpression()
  537. */
  538. public Expression getExpression()
  539. {
  540. return m_predicates[m_index];
  541. }
  542. /**
  543. * @see ExpressionOwner#setExpression(Expression)
  544. */
  545. public void setExpression(Expression exp)
  546. {
  547. exp.exprSetParent(PredicatedNodeTest.this);
  548. m_predicates[m_index] = exp;
  549. }
  550. }
  551. }