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