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