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: StepPattern.java,v 1.28 2004/02/17 04:35:37 minchau Exp $
  18. */
  19. package com.sun.org.apache.xpath.internal.patterns;
  20. import com.sun.org.apache.xml.internal.dtm.Axis;
  21. import com.sun.org.apache.xml.internal.dtm.DTM;
  22. import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
  23. import com.sun.org.apache.xml.internal.dtm.DTMFilter;
  24. import com.sun.org.apache.xpath.internal.Expression;
  25. import com.sun.org.apache.xpath.internal.ExpressionOwner;
  26. import com.sun.org.apache.xpath.internal.XPathContext;
  27. import com.sun.org.apache.xpath.internal.XPathVisitor;
  28. import com.sun.org.apache.xpath.internal.axes.SubContextList;
  29. import com.sun.org.apache.xpath.internal.compiler.PsuedoNames;
  30. import com.sun.org.apache.xpath.internal.objects.XObject;
  31. /**
  32. * This class represents a single pattern match step.
  33. * @xsl.usage advanced
  34. */
  35. public class StepPattern extends NodeTest implements SubContextList, ExpressionOwner
  36. {
  37. /** The axis for this test. */
  38. protected int m_axis;
  39. /**
  40. * Construct a StepPattern that tests for namespaces and node names.
  41. *
  42. *
  43. * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
  44. * @param namespace The namespace to be tested.
  45. * @param name The local name to be tested.
  46. * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
  47. * @param axisForPredicate No longer used.
  48. */
  49. public StepPattern(int whatToShow, String namespace, String name, int axis,
  50. int axisForPredicate)
  51. {
  52. super(whatToShow, namespace, name);
  53. m_axis = axis;
  54. }
  55. /**
  56. * Construct a StepPattern that doesn't test for node names.
  57. *
  58. *
  59. * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
  60. * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
  61. * @param axisForPredicate No longer used.
  62. */
  63. public StepPattern(int whatToShow, int axis, int axisForPredicate)
  64. {
  65. super(whatToShow);
  66. m_axis = axis;
  67. }
  68. /**
  69. * The target local name or psuedo name, for hash table lookup optimization.
  70. * @serial
  71. */
  72. String m_targetString; // only calculate on head
  73. /**
  74. * Calculate the local name or psuedo name of the node that this pattern will test,
  75. * for hash table lookup optimization.
  76. *
  77. * @see com.sun.org.apache.xpath.internal.compiler.PsuedoNames
  78. */
  79. public void calcTargetString()
  80. {
  81. int whatToShow = getWhatToShow();
  82. switch (whatToShow)
  83. {
  84. case DTMFilter.SHOW_COMMENT :
  85. m_targetString = PsuedoNames.PSEUDONAME_COMMENT;
  86. break;
  87. case DTMFilter.SHOW_TEXT :
  88. case DTMFilter.SHOW_CDATA_SECTION :
  89. case (DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION) :
  90. m_targetString = PsuedoNames.PSEUDONAME_TEXT;
  91. break;
  92. case DTMFilter.SHOW_ALL :
  93. m_targetString = PsuedoNames.PSEUDONAME_ANY;
  94. break;
  95. case DTMFilter.SHOW_DOCUMENT :
  96. case DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT :
  97. m_targetString = PsuedoNames.PSEUDONAME_ROOT;
  98. break;
  99. case DTMFilter.SHOW_ELEMENT :
  100. if (this.WILD == m_name)
  101. m_targetString = PsuedoNames.PSEUDONAME_ANY;
  102. else
  103. m_targetString = m_name;
  104. break;
  105. default :
  106. m_targetString = PsuedoNames.PSEUDONAME_ANY;
  107. break;
  108. }
  109. }
  110. /**
  111. * Get the local name or psuedo name of the node that this pattern will test,
  112. * for hash table lookup optimization.
  113. *
  114. *
  115. * @return local name or psuedo name of the node.
  116. * @see com.sun.org.apache.xpath.internal.compiler.PsuedoNames
  117. */
  118. public String getTargetString()
  119. {
  120. return m_targetString;
  121. }
  122. /**
  123. * Reference to nodetest and predicate for
  124. * parent or ancestor.
  125. * @serial
  126. */
  127. StepPattern m_relativePathPattern;
  128. /**
  129. * This function is used to fixup variables from QNames to stack frame
  130. * indexes at stylesheet build time.
  131. * @param vars List of QNames that correspond to variables. This list
  132. * should be searched backwards for the first qualified name that
  133. * corresponds to the variable reference qname. The position of the
  134. * QName in the vector from the start of the vector will be its position
  135. * in the stack frame (but variables above the globalsTop value will need
  136. * to be offset to the current stack frame).
  137. * @param globalsSize The number of variables in the global variable area.
  138. */
  139. public void fixupVariables(java.util.Vector vars, int globalsSize)
  140. {
  141. super.fixupVariables(vars, globalsSize);
  142. if (null != m_predicates)
  143. {
  144. for (int i = 0; i < m_predicates.length; i++)
  145. {
  146. m_predicates[i].fixupVariables(vars, globalsSize);
  147. }
  148. }
  149. if (null != m_relativePathPattern)
  150. {
  151. m_relativePathPattern.fixupVariables(vars, globalsSize);
  152. }
  153. }
  154. /**
  155. * Set the reference to nodetest and predicate for
  156. * parent or ancestor.
  157. *
  158. *
  159. * @param expr The relative pattern expression.
  160. */
  161. public void setRelativePathPattern(StepPattern expr)
  162. {
  163. m_relativePathPattern = expr;
  164. expr.exprSetParent(this);
  165. calcScore();
  166. }
  167. /**
  168. * Get the reference to nodetest and predicate for
  169. * parent or ancestor.
  170. *
  171. *
  172. * @return The relative pattern expression.
  173. */
  174. public StepPattern getRelativePathPattern()
  175. {
  176. return m_relativePathPattern;
  177. }
  178. // /**
  179. // * Set the list of predicate expressions for this pattern step.
  180. // * @param predicates List of expression objects.
  181. // */
  182. // public void setPredicates(Expression[] predicates)
  183. // {
  184. // m_predicates = predicates;
  185. // }
  186. /**
  187. * Set the list of predicate expressions for this pattern step.
  188. * @return List of expression objects.
  189. */
  190. public Expression[] getPredicates()
  191. {
  192. return m_predicates;
  193. }
  194. /**
  195. * The list of predicate expressions for this pattern step.
  196. * @serial
  197. */
  198. Expression[] m_predicates;
  199. /**
  200. * Tell if this expression or it's subexpressions can traverse outside
  201. * the current subtree.
  202. *
  203. * NOTE: Ancestors tests with predicates are problematic, and will require
  204. * special treatment.
  205. *
  206. * @return true if traversal outside the context node's subtree can occur.
  207. */
  208. public boolean canTraverseOutsideSubtree()
  209. {
  210. int n = getPredicateCount();
  211. for (int i = 0; i < n; i++)
  212. {
  213. if (getPredicate(i).canTraverseOutsideSubtree())
  214. return true;
  215. }
  216. return false;
  217. }
  218. /**
  219. * Get a predicate expression.
  220. *
  221. *
  222. * @param i The index of the predicate.
  223. *
  224. * @return A predicate expression.
  225. */
  226. public Expression getPredicate(int i)
  227. {
  228. return m_predicates[i];
  229. }
  230. /**
  231. * Get the number of predicates for this match pattern step.
  232. *
  233. *
  234. * @return the number of predicates for this match pattern step.
  235. */
  236. public final int getPredicateCount()
  237. {
  238. return (null == m_predicates) ? 0 : m_predicates.length;
  239. }
  240. /**
  241. * Set the predicates for this match pattern step.
  242. *
  243. *
  244. * @param predicates An array of expressions that define predicates
  245. * for this step.
  246. */
  247. public void setPredicates(Expression[] predicates)
  248. {
  249. m_predicates = predicates;
  250. if(null != predicates)
  251. {
  252. for(int i = 0; i < predicates.length; i++)
  253. {
  254. predicates[i].exprSetParent(this);
  255. }
  256. }
  257. calcScore();
  258. }
  259. /**
  260. * Static calc of match score.
  261. */
  262. public void calcScore()
  263. {
  264. if ((getPredicateCount() > 0) || (null != m_relativePathPattern))
  265. {
  266. m_score = SCORE_OTHER;
  267. }
  268. else
  269. super.calcScore();
  270. if (null == m_targetString)
  271. calcTargetString();
  272. }
  273. /**
  274. * Execute this pattern step, including predicates.
  275. *
  276. *
  277. * @param xctxt XPath runtime context.
  278. * @param currentNode The current node context.
  279. *
  280. * @return {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NODETEST},
  281. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NONE},
  282. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NSWILD},
  283. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_QNAME}, or
  284. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_OTHER}.
  285. *
  286. * @throws javax.xml.transform.TransformerException
  287. */
  288. public XObject execute(XPathContext xctxt, int currentNode)
  289. throws javax.xml.transform.TransformerException
  290. {
  291. DTM dtm = xctxt.getDTM(currentNode);
  292. if (dtm != null)
  293. {
  294. int expType = dtm.getExpandedTypeID(currentNode);
  295. return execute(xctxt, currentNode, dtm, expType);
  296. }
  297. return NodeTest.SCORE_NONE;
  298. }
  299. /**
  300. * Execute this pattern step, including predicates.
  301. *
  302. *
  303. * @param xctxt XPath runtime context.
  304. *
  305. * @return {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NODETEST},
  306. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NONE},
  307. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NSWILD},
  308. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_QNAME}, or
  309. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_OTHER}.
  310. *
  311. * @throws javax.xml.transform.TransformerException
  312. */
  313. public XObject execute(XPathContext xctxt)
  314. throws javax.xml.transform.TransformerException
  315. {
  316. return execute(xctxt, xctxt.getCurrentNode());
  317. }
  318. /**
  319. * Execute an expression in the XPath runtime context, and return the
  320. * result of the expression.
  321. *
  322. *
  323. * @param xctxt The XPath runtime context.
  324. * @param currentNode The currentNode.
  325. * @param dtm The DTM of the current node.
  326. * @param expType The expanded type ID of the current node.
  327. *
  328. * @return The result of the expression in the form of a <code>XObject</code>.
  329. *
  330. * @throws javax.xml.transform.TransformerException if a runtime exception
  331. * occurs.
  332. */
  333. public XObject execute(
  334. XPathContext xctxt, int currentNode, DTM dtm, int expType)
  335. throws javax.xml.transform.TransformerException
  336. {
  337. if (m_whatToShow == NodeTest.SHOW_BYFUNCTION)
  338. {
  339. if (null != m_relativePathPattern)
  340. {
  341. return m_relativePathPattern.execute(xctxt);
  342. }
  343. else
  344. return NodeTest.SCORE_NONE;
  345. }
  346. XObject score;
  347. score = super.execute(xctxt, currentNode, dtm, expType);
  348. if (score == NodeTest.SCORE_NONE)
  349. return NodeTest.SCORE_NONE;
  350. if (getPredicateCount() != 0)
  351. {
  352. if (!executePredicates(xctxt, dtm, currentNode))
  353. return NodeTest.SCORE_NONE;
  354. }
  355. if (null != m_relativePathPattern)
  356. return m_relativePathPattern.executeRelativePathPattern(xctxt, dtm,
  357. currentNode);
  358. return score;
  359. }
  360. /**
  361. * New Method to check whether the current node satisfies a position predicate
  362. *
  363. * @param xctxt The XPath runtime context.
  364. * @param predPos Which predicate we're evaluating of foo[1][2][3].
  365. * @param dtm The DTM of the current node.
  366. * @param context The currentNode.
  367. * @param pos The position being requested, i.e. the value returned by
  368. * m_predicates[predPos].execute(xctxt).
  369. *
  370. * @return true of the position of the context matches pos, false otherwise.
  371. */
  372. private final boolean checkProximityPosition(XPathContext xctxt,
  373. int predPos, DTM dtm, int context, int pos)
  374. {
  375. try
  376. {
  377. DTMAxisTraverser traverser =
  378. dtm.getAxisTraverser(Axis.PRECEDINGSIBLING);
  379. for (int child = traverser.first(context); DTM.NULL != child;
  380. child = traverser.next(context, child))
  381. {
  382. try
  383. {
  384. xctxt.pushCurrentNode(child);
  385. if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
  386. {
  387. boolean pass = true;
  388. try
  389. {
  390. xctxt.pushSubContextList(this);
  391. for (int i = 0; i < predPos; i++)
  392. {
  393. xctxt.pushPredicatePos(i);
  394. try
  395. {
  396. XObject pred = m_predicates[i].execute(xctxt);
  397. try
  398. {
  399. if (XObject.CLASS_NUMBER == pred.getType())
  400. {
  401. throw new Error("Why: Should never have been called");
  402. }
  403. else if (!pred.boolWithSideEffects())
  404. {
  405. pass = false;
  406. break;
  407. }
  408. }
  409. finally
  410. {
  411. pred.detach();
  412. }
  413. }
  414. finally
  415. {
  416. xctxt.popPredicatePos();
  417. }
  418. }
  419. }
  420. finally
  421. {
  422. xctxt.popSubContextList();
  423. }
  424. if (pass)
  425. pos--;
  426. if (pos < 1)
  427. return false;
  428. }
  429. }
  430. finally
  431. {
  432. xctxt.popCurrentNode();
  433. }
  434. }
  435. }
  436. catch (javax.xml.transform.TransformerException se)
  437. {
  438. // TODO: should keep throw sax exception...
  439. throw new java.lang.RuntimeException(se.getMessage());
  440. }
  441. return (pos == 1);
  442. }
  443. /**
  444. * Get the proximity position index of the current node based on this
  445. * node test.
  446. *
  447. *
  448. * @param xctxt XPath runtime context.
  449. * @param predPos Which predicate we're evaluating of foo[1][2][3].
  450. * @param findLast If true, don't terminate when the context node is found.
  451. *
  452. * @return the proximity position index of the current node based on the
  453. * node test.
  454. */
  455. private final int getProximityPosition(XPathContext xctxt, int predPos,
  456. boolean findLast)
  457. {
  458. int pos = 0;
  459. int context = xctxt.getCurrentNode();
  460. DTM dtm = xctxt.getDTM(context);
  461. int parent = dtm.getParent(context);
  462. try
  463. {
  464. DTMAxisTraverser traverser = dtm.getAxisTraverser(Axis.CHILD);
  465. for (int child = traverser.first(parent); DTM.NULL != child;
  466. child = traverser.next(parent, child))
  467. {
  468. try
  469. {
  470. xctxt.pushCurrentNode(child);
  471. if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
  472. {
  473. boolean pass = true;
  474. try
  475. {
  476. xctxt.pushSubContextList(this);
  477. for (int i = 0; i < predPos; i++)
  478. {
  479. xctxt.pushPredicatePos(i);
  480. try
  481. {
  482. XObject pred = m_predicates[i].execute(xctxt);
  483. try
  484. {
  485. if (XObject.CLASS_NUMBER == pred.getType())
  486. {
  487. if ((pos + 1) != (int) pred.numWithSideEffects())
  488. {
  489. pass = false;
  490. break;
  491. }
  492. }
  493. else if (!pred.boolWithSideEffects())
  494. {
  495. pass = false;
  496. break;
  497. }
  498. }
  499. finally
  500. {
  501. pred.detach();
  502. }
  503. }
  504. finally
  505. {
  506. xctxt.popPredicatePos();
  507. }
  508. }
  509. }
  510. finally
  511. {
  512. xctxt.popSubContextList();
  513. }
  514. if (pass)
  515. pos++;
  516. if (!findLast && child == context)
  517. {
  518. return pos;
  519. }
  520. }
  521. }
  522. finally
  523. {
  524. xctxt.popCurrentNode();
  525. }
  526. }
  527. }
  528. catch (javax.xml.transform.TransformerException se)
  529. {
  530. // TODO: should keep throw sax exception...
  531. throw new java.lang.RuntimeException(se.getMessage());
  532. }
  533. return pos;
  534. }
  535. /**
  536. * Get the proximity position index of the current node based on this
  537. * node test.
  538. *
  539. *
  540. * @param xctxt XPath runtime context.
  541. *
  542. * @return the proximity position index of the current node based on the
  543. * node test.
  544. */
  545. public int getProximityPosition(XPathContext xctxt)
  546. {
  547. return getProximityPosition(xctxt, xctxt.getPredicatePos(), false);
  548. }
  549. /**
  550. * Get the count of the nodes that match the test, which is the proximity
  551. * position of the last node that can pass this test in the sub context
  552. * selection. In XSLT 1-based indexing, this count is the index of the last
  553. * node.
  554. *
  555. *
  556. * @param xctxt XPath runtime context.
  557. *
  558. * @return the count of the nodes that match the test.
  559. */
  560. public int getLastPos(XPathContext xctxt)
  561. {
  562. return getProximityPosition(xctxt, xctxt.getPredicatePos(), true);
  563. }
  564. /**
  565. * Execute the match pattern step relative to another step.
  566. *
  567. *
  568. * @param xctxt The XPath runtime context.
  569. * @param dtm The DTM of the current node.
  570. * @param currentNode The current node context.
  571. *
  572. * @return {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NODETEST},
  573. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NONE},
  574. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NSWILD},
  575. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_QNAME}, or
  576. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_OTHER}.
  577. *
  578. * @throws javax.xml.transform.TransformerException
  579. */
  580. protected final XObject executeRelativePathPattern(
  581. XPathContext xctxt, DTM dtm, int currentNode)
  582. throws javax.xml.transform.TransformerException
  583. {
  584. XObject score = NodeTest.SCORE_NONE;
  585. int context = currentNode;
  586. DTMAxisTraverser traverser;
  587. traverser = dtm.getAxisTraverser(m_axis);
  588. for (int relative = traverser.first(context); DTM.NULL != relative;
  589. relative = traverser.next(context, relative))
  590. {
  591. try
  592. {
  593. xctxt.pushCurrentNode(relative);
  594. score = execute(xctxt);
  595. if (score != NodeTest.SCORE_NONE)
  596. break;
  597. }
  598. finally
  599. {
  600. xctxt.popCurrentNode();
  601. }
  602. }
  603. return score;
  604. }
  605. /**
  606. * Execute the predicates on this step to determine if the current node
  607. * should be filtered or accepted.
  608. *
  609. * @param xctxt The XPath runtime context.
  610. * @param dtm The DTM of the current node.
  611. * @param currentNode The current node context.
  612. *
  613. * @return true if the node should be accepted, false otherwise.
  614. *
  615. * @throws javax.xml.transform.TransformerException
  616. */
  617. protected final boolean executePredicates(
  618. XPathContext xctxt, DTM dtm, int currentNode)
  619. throws javax.xml.transform.TransformerException
  620. {
  621. boolean result = true;
  622. boolean positionAlreadySeen = false;
  623. int n = getPredicateCount();
  624. try
  625. {
  626. xctxt.pushSubContextList(this);
  627. for (int i = 0; i < n; i++)
  628. {
  629. xctxt.pushPredicatePos(i);
  630. try
  631. {
  632. XObject pred = m_predicates[i].execute(xctxt);
  633. try
  634. {
  635. if (XObject.CLASS_NUMBER == pred.getType())
  636. {
  637. int pos = (int) pred.num();
  638. if (positionAlreadySeen)
  639. {
  640. result = (pos == 1);
  641. break;
  642. }
  643. else
  644. {
  645. positionAlreadySeen = true;
  646. if (!checkProximityPosition(xctxt, i, dtm, currentNode, pos))
  647. {
  648. result = false;
  649. break;
  650. }
  651. }
  652. }
  653. else if (!pred.boolWithSideEffects())
  654. {
  655. result = false;
  656. break;
  657. }
  658. }
  659. finally
  660. {
  661. pred.detach();
  662. }
  663. }
  664. finally
  665. {
  666. xctxt.popPredicatePos();
  667. }
  668. }
  669. }
  670. finally
  671. {
  672. xctxt.popSubContextList();
  673. }
  674. return result;
  675. }
  676. /**
  677. * Get the string represenentation of this step for diagnostic purposes.
  678. *
  679. *
  680. * @return A string representation of this step, built by reverse-engineering
  681. * the contained info.
  682. */
  683. public String toString()
  684. {
  685. StringBuffer buf = new StringBuffer();
  686. for (StepPattern pat = this; pat != null; pat = pat.m_relativePathPattern)
  687. {
  688. if (pat != this)
  689. buf.append("/");
  690. buf.append(Axis.names[pat.m_axis]);
  691. buf.append("::");
  692. if (0x000005000 == pat.m_whatToShow)
  693. {
  694. buf.append("doc()");
  695. }
  696. else if (DTMFilter.SHOW_BYFUNCTION == pat.m_whatToShow)
  697. {
  698. buf.append("function()");
  699. }
  700. else if (DTMFilter.SHOW_ALL == pat.m_whatToShow)
  701. {
  702. buf.append("node()");
  703. }
  704. else if (DTMFilter.SHOW_TEXT == pat.m_whatToShow)
  705. {
  706. buf.append("text()");
  707. }
  708. else if (DTMFilter.SHOW_PROCESSING_INSTRUCTION == pat.m_whatToShow)
  709. {
  710. buf.append("processing-instruction(");
  711. if (null != pat.m_name)
  712. {
  713. buf.append(pat.m_name);
  714. }
  715. buf.append(")");
  716. }
  717. else if (DTMFilter.SHOW_COMMENT == pat.m_whatToShow)
  718. {
  719. buf.append("comment()");
  720. }
  721. else if (null != pat.m_name)
  722. {
  723. if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
  724. {
  725. buf.append("@");
  726. }
  727. if (null != pat.m_namespace)
  728. {
  729. buf.append("{");
  730. buf.append(pat.m_namespace);
  731. buf.append("}");
  732. }
  733. buf.append(pat.m_name);
  734. }
  735. else if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
  736. {
  737. buf.append("@");
  738. }
  739. else if ((DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT)
  740. == pat.m_whatToShow)
  741. {
  742. buf.append("doc-root()");
  743. }
  744. else
  745. {
  746. buf.append("?" + Integer.toHexString(pat.m_whatToShow));
  747. }
  748. if (null != pat.m_predicates)
  749. {
  750. for (int i = 0; i < pat.m_predicates.length; i++)
  751. {
  752. buf.append("[");
  753. buf.append(pat.m_predicates[i]);
  754. buf.append("]");
  755. }
  756. }
  757. }
  758. return buf.toString();
  759. }
  760. /** Set to true to send diagnostics about pattern matches to the consol. */
  761. private static final boolean DEBUG_MATCHES = false;
  762. /**
  763. * Get the match score of the given node.
  764. *
  765. * @param xctxt The XPath runtime context.
  766. * @param context The node to be tested.
  767. *
  768. * @return {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NODETEST},
  769. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NONE},
  770. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_NSWILD},
  771. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_QNAME}, or
  772. * {@link com.sun.org.apache.xpath.internal.patterns.NodeTest#SCORE_OTHER}.
  773. *
  774. * @throws javax.xml.transform.TransformerException
  775. */
  776. public double getMatchScore(XPathContext xctxt, int context)
  777. throws javax.xml.transform.TransformerException
  778. {
  779. xctxt.pushCurrentNode(context);
  780. xctxt.pushCurrentExpressionNode(context);
  781. try
  782. {
  783. XObject score = execute(xctxt);
  784. return score.num();
  785. }
  786. finally
  787. {
  788. xctxt.popCurrentNode();
  789. xctxt.popCurrentExpressionNode();
  790. }
  791. // return XPath.MATCH_SCORE_NONE;
  792. }
  793. /**
  794. * Set the axis that this step should follow.
  795. *
  796. *
  797. * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
  798. */
  799. public void setAxis(int axis)
  800. {
  801. m_axis = axis;
  802. }
  803. /**
  804. * Get the axis that this step follows.
  805. *
  806. *
  807. * @return The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
  808. */
  809. public int getAxis()
  810. {
  811. return m_axis;
  812. }
  813. class PredOwner implements ExpressionOwner
  814. {
  815. int m_index;
  816. PredOwner(int index)
  817. {
  818. m_index = index;
  819. }
  820. /**
  821. * @see ExpressionOwner#getExpression()
  822. */
  823. public Expression getExpression()
  824. {
  825. return m_predicates[m_index];
  826. }
  827. /**
  828. * @see ExpressionOwner#setExpression(Expression)
  829. */
  830. public void setExpression(Expression exp)
  831. {
  832. exp.exprSetParent(StepPattern.this);
  833. m_predicates[m_index] = exp;
  834. }
  835. }
  836. /**
  837. * @see XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
  838. */
  839. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  840. {
  841. if(visitor.visitMatchPattern(owner, this))
  842. {
  843. callSubtreeVisitors(visitor);
  844. }
  845. }
  846. /**
  847. * Call the visitors on the subtree. Factored out from callVisitors
  848. * so it may be called by derived classes.
  849. */
  850. protected void callSubtreeVisitors(XPathVisitor visitor)
  851. {
  852. if (null != m_predicates)
  853. {
  854. int n = m_predicates.length;
  855. for (int i = 0; i < n; i++)
  856. {
  857. ExpressionOwner predOwner = new PredOwner(i);
  858. if (visitor.visitPredicate(predOwner, m_predicates[i]))
  859. {
  860. m_predicates[i].callVisitors(predOwner, visitor);
  861. }
  862. }
  863. }
  864. if (null != m_relativePathPattern)
  865. {
  866. m_relativePathPattern.callVisitors(this, visitor);
  867. }
  868. }
  869. /**
  870. * @see ExpressionOwner#getExpression()
  871. */
  872. public Expression getExpression()
  873. {
  874. return m_relativePathPattern;
  875. }
  876. /**
  877. * @see ExpressionOwner#setExpression(Expression)
  878. */
  879. public void setExpression(Expression exp)
  880. {
  881. exp.exprSetParent(this);
  882. m_relativePathPattern = (StepPattern)exp;
  883. }
  884. /**
  885. * @see Expression#deepEquals(Expression)
  886. */
  887. public boolean deepEquals(Expression expr)
  888. {
  889. if(!super.deepEquals(expr))
  890. return false;
  891. StepPattern sp = (StepPattern)expr;
  892. if (null != m_predicates)
  893. {
  894. int n = m_predicates.length;
  895. if ((null == sp.m_predicates) || (sp.m_predicates.length != n))
  896. return false;
  897. for (int i = 0; i < n; i++)
  898. {
  899. if (!m_predicates[i].deepEquals(sp.m_predicates[i]))
  900. return false;
  901. }
  902. }
  903. else if (null != sp.m_predicates)
  904. return false;
  905. if(null != m_relativePathPattern)
  906. {
  907. if(!m_relativePathPattern.deepEquals(sp.m_relativePathPattern))
  908. return false;
  909. }
  910. else if(sp.m_relativePathPattern != null)
  911. return false;
  912. return true;
  913. }
  914. }