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