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