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