txPathExpr.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "txExpr.h"
  6. #include "txNodeSet.h"
  7. #include "txNodeSetContext.h"
  8. #include "txSingleNodeContext.h"
  9. #include "txXMLUtils.h"
  10. #include "txXPathTreeWalker.h"
  11. //------------/
  12. //- PathExpr -/
  13. //------------/
  14. /**
  15. * Adds the Expr to this PathExpr
  16. * @param expr the Expr to add to this PathExpr
  17. **/
  18. nsresult
  19. PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp)
  20. {
  21. NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
  22. "First step has to be relative in PathExpr");
  23. PathExprItem* pxi = mItems.AppendElement();
  24. if (!pxi) {
  25. return NS_ERROR_OUT_OF_MEMORY;
  26. }
  27. pxi->expr = aExpr;
  28. pxi->pathOp = aPathOp;
  29. return NS_OK;
  30. }
  31. //-----------------------------/
  32. //- Virtual methods from Expr -/
  33. //-----------------------------/
  34. /**
  35. * Evaluates this Expr based on the given context node and processor state
  36. * @param context the context node for evaluation of this Expr
  37. * @param ps the ContextState containing the stack information needed
  38. * for evaluation
  39. * @return the result of the evaluation
  40. **/
  41. nsresult
  42. PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
  43. {
  44. *aResult = nullptr;
  45. // We need to evaluate the first step with the current context since it
  46. // can depend on the context size and position. For example:
  47. // key('books', concat('book', position()))
  48. RefPtr<txAExprResult> res;
  49. nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
  50. NS_ENSURE_SUCCESS(rv, rv);
  51. NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
  52. NS_ERROR_XSLT_NODESET_EXPECTED);
  53. RefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
  54. (static_cast<txAExprResult*>
  55. (res));
  56. if (nodes->isEmpty()) {
  57. res.forget(aResult);
  58. return NS_OK;
  59. }
  60. res = nullptr; // To allow recycling
  61. // Evaluate remaining steps
  62. uint32_t i, len = mItems.Length();
  63. for (i = 1; i < len; ++i) {
  64. PathExprItem& pxi = mItems[i];
  65. RefPtr<txNodeSet> tmpNodes;
  66. txNodeSetContext eContext(nodes, aContext);
  67. while (eContext.hasNext()) {
  68. eContext.next();
  69. RefPtr<txNodeSet> resNodes;
  70. if (pxi.pathOp == DESCENDANT_OP) {
  71. rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
  72. NS_ENSURE_SUCCESS(rv, rv);
  73. rv = evalDescendants(pxi.expr, eContext.getContextNode(),
  74. &eContext, resNodes);
  75. NS_ENSURE_SUCCESS(rv, rv);
  76. }
  77. else {
  78. RefPtr<txAExprResult> res;
  79. rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
  80. NS_ENSURE_SUCCESS(rv, rv);
  81. if (res->getResultType() != txAExprResult::NODESET) {
  82. //XXX ErrorReport: report nonnodeset error
  83. return NS_ERROR_XSLT_NODESET_EXPECTED;
  84. }
  85. resNodes = static_cast<txNodeSet*>
  86. (static_cast<txAExprResult*>
  87. (res));
  88. }
  89. if (tmpNodes) {
  90. if (!resNodes->isEmpty()) {
  91. RefPtr<txNodeSet> oldSet;
  92. oldSet.swap(tmpNodes);
  93. rv = aContext->recycler()->
  94. getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes));
  95. NS_ENSURE_SUCCESS(rv, rv);
  96. oldSet.swap(resNodes);
  97. rv = aContext->recycler()->
  98. getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes));
  99. NS_ENSURE_SUCCESS(rv, rv);
  100. tmpNodes->addAndTransfer(resNodes);
  101. }
  102. }
  103. else {
  104. tmpNodes = resNodes;
  105. }
  106. }
  107. nodes = tmpNodes;
  108. if (nodes->isEmpty()) {
  109. break;
  110. }
  111. }
  112. *aResult = nodes;
  113. NS_ADDREF(*aResult);
  114. return NS_OK;
  115. } //-- evaluate
  116. /**
  117. * Selects from the descendants of the context node
  118. * all nodes that match the Expr
  119. **/
  120. nsresult
  121. PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
  122. txIMatchContext* aContext, txNodeSet* resNodes)
  123. {
  124. txSingleNodeContext eContext(aNode, aContext);
  125. RefPtr<txAExprResult> res;
  126. nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
  127. NS_ENSURE_SUCCESS(rv, rv);
  128. if (res->getResultType() != txAExprResult::NODESET) {
  129. //XXX ErrorReport: report nonnodeset error
  130. return NS_ERROR_XSLT_NODESET_EXPECTED;
  131. }
  132. txNodeSet* oldSet = static_cast<txNodeSet*>
  133. (static_cast<txAExprResult*>(res));
  134. RefPtr<txNodeSet> newSet;
  135. rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
  136. getter_AddRefs(newSet));
  137. NS_ENSURE_SUCCESS(rv, rv);
  138. resNodes->addAndTransfer(newSet);
  139. bool filterWS = aContext->isStripSpaceAllowed(aNode);
  140. txXPathTreeWalker walker(aNode);
  141. if (!walker.moveToFirstChild()) {
  142. return NS_OK;
  143. }
  144. do {
  145. const txXPathNode& node = walker.getCurrentPosition();
  146. if (!(filterWS && txXPathNodeUtils::isText(node) &&
  147. txXPathNodeUtils::isWhitespace(node))) {
  148. rv = evalDescendants(aStep, node, aContext, resNodes);
  149. NS_ENSURE_SUCCESS(rv, rv);
  150. }
  151. } while (walker.moveToNextSibling());
  152. return NS_OK;
  153. } //-- evalDescendants
  154. Expr::ExprType
  155. PathExpr::getType()
  156. {
  157. return PATH_EXPR;
  158. }
  159. TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
  160. Expr*
  161. PathExpr::getSubExprAt(uint32_t aPos)
  162. {
  163. return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr;
  164. }
  165. void
  166. PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
  167. {
  168. NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
  169. mItems[aPos].expr.forget();
  170. mItems[aPos].expr = aExpr;
  171. }
  172. bool
  173. PathExpr::isSensitiveTo(ContextSensitivity aContext)
  174. {
  175. if (mItems[0].expr->isSensitiveTo(aContext)) {
  176. return true;
  177. }
  178. // We're creating a new node/nodeset so we can ignore those bits.
  179. Expr::ContextSensitivity context =
  180. aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
  181. if (context == NO_CONTEXT) {
  182. return false;
  183. }
  184. uint32_t i, len = mItems.Length();
  185. for (i = 0; i < len; ++i) {
  186. NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
  187. "Step cannot depend on nodeset-context");
  188. if (mItems[i].expr->isSensitiveTo(context)) {
  189. return true;
  190. }
  191. }
  192. return false;
  193. }
  194. #ifdef TX_TO_STRING
  195. void
  196. PathExpr::toString(nsAString& dest)
  197. {
  198. if (!mItems.IsEmpty()) {
  199. NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
  200. "First step should be relative");
  201. mItems[0].expr->toString(dest);
  202. }
  203. uint32_t i, len = mItems.Length();
  204. for (i = 1; i < len; ++i) {
  205. switch (mItems[i].pathOp) {
  206. case DESCENDANT_OP:
  207. dest.AppendLiteral("//");
  208. break;
  209. case RELATIVE_OP:
  210. dest.Append(char16_t('/'));
  211. break;
  212. }
  213. mItems[i].expr->toString(dest);
  214. }
  215. }
  216. #endif