1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is TransforMiiX XSLT processor code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The MITRE Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Keith Visco <kvisco@ziplink.net> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "txExpr.h"
40 : #include "txNodeSet.h"
41 : #include "txNodeSetContext.h"
42 : #include "txSingleNodeContext.h"
43 : #include "txXMLUtils.h"
44 : #include "txXPathTreeWalker.h"
45 :
46 : //------------/
47 : //- PathExpr -/
48 : //------------/
49 :
50 : /**
51 : * Adds the Expr to this PathExpr
52 : * @param expr the Expr to add to this PathExpr
53 : **/
54 : nsresult
55 0 : PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp)
56 : {
57 0 : NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
58 : "First step has to be relative in PathExpr");
59 0 : PathExprItem* pxi = mItems.AppendElement();
60 0 : if (!pxi) {
61 0 : return NS_ERROR_OUT_OF_MEMORY;
62 : }
63 0 : pxi->expr = aExpr;
64 0 : pxi->pathOp = aPathOp;
65 :
66 0 : return NS_OK;
67 : }
68 :
69 : //-----------------------------/
70 : //- Virtual methods from Expr -/
71 : //-----------------------------/
72 :
73 : /**
74 : * Evaluates this Expr based on the given context node and processor state
75 : * @param context the context node for evaluation of this Expr
76 : * @param ps the ContextState containing the stack information needed
77 : * for evaluation
78 : * @return the result of the evaluation
79 : **/
80 : nsresult
81 0 : PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
82 : {
83 0 : *aResult = nsnull;
84 :
85 : // We need to evaluate the first step with the current context since it
86 : // can depend on the context size and position. For example:
87 : // key('books', concat('book', position()))
88 0 : nsRefPtr<txAExprResult> res;
89 0 : nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
90 0 : NS_ENSURE_SUCCESS(rv, rv);
91 :
92 0 : NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
93 : NS_ERROR_XSLT_NODESET_EXPECTED);
94 :
95 : nsRefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
96 : (static_cast<txAExprResult*>
97 0 : (res));
98 0 : if (nodes->isEmpty()) {
99 0 : res.swap(*aResult);
100 :
101 0 : return NS_OK;
102 : }
103 0 : res = nsnull; // To allow recycling
104 :
105 : // Evaluate remaining steps
106 0 : PRUint32 i, len = mItems.Length();
107 0 : for (i = 1; i < len; ++i) {
108 0 : PathExprItem& pxi = mItems[i];
109 0 : nsRefPtr<txNodeSet> tmpNodes;
110 0 : txNodeSetContext eContext(nodes, aContext);
111 0 : while (eContext.hasNext()) {
112 0 : eContext.next();
113 :
114 0 : nsRefPtr<txNodeSet> resNodes;
115 0 : if (pxi.pathOp == DESCENDANT_OP) {
116 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
117 0 : NS_ENSURE_SUCCESS(rv, rv);
118 :
119 0 : rv = evalDescendants(pxi.expr, eContext.getContextNode(),
120 0 : &eContext, resNodes);
121 0 : NS_ENSURE_SUCCESS(rv, rv);
122 : }
123 : else {
124 0 : nsRefPtr<txAExprResult> res;
125 0 : rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
126 0 : NS_ENSURE_SUCCESS(rv, rv);
127 :
128 0 : if (res->getResultType() != txAExprResult::NODESET) {
129 : //XXX ErrorReport: report nonnodeset error
130 0 : return NS_ERROR_XSLT_NODESET_EXPECTED;
131 : }
132 : resNodes = static_cast<txNodeSet*>
133 : (static_cast<txAExprResult*>
134 0 : (res));
135 : }
136 :
137 0 : if (tmpNodes) {
138 0 : if (!resNodes->isEmpty()) {
139 0 : nsRefPtr<txNodeSet> oldSet;
140 0 : oldSet.swap(tmpNodes);
141 0 : rv = aContext->recycler()->
142 0 : getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes));
143 0 : NS_ENSURE_SUCCESS(rv, rv);
144 :
145 0 : oldSet.swap(resNodes);
146 0 : rv = aContext->recycler()->
147 0 : getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes));
148 0 : NS_ENSURE_SUCCESS(rv, rv);
149 :
150 0 : tmpNodes->addAndTransfer(resNodes);
151 : }
152 : }
153 : else {
154 0 : tmpNodes = resNodes;
155 : }
156 : }
157 0 : nodes = tmpNodes;
158 0 : if (nodes->isEmpty()) {
159 : break;
160 : }
161 : }
162 :
163 0 : *aResult = nodes;
164 0 : NS_ADDREF(*aResult);
165 :
166 0 : return NS_OK;
167 : } //-- evaluate
168 :
169 : /**
170 : * Selects from the descendants of the context node
171 : * all nodes that match the Expr
172 : **/
173 : nsresult
174 0 : PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
175 : txIMatchContext* aContext, txNodeSet* resNodes)
176 : {
177 0 : txSingleNodeContext eContext(aNode, aContext);
178 0 : nsRefPtr<txAExprResult> res;
179 0 : nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
180 0 : NS_ENSURE_SUCCESS(rv, rv);
181 :
182 0 : if (res->getResultType() != txAExprResult::NODESET) {
183 : //XXX ErrorReport: report nonnodeset error
184 0 : return NS_ERROR_XSLT_NODESET_EXPECTED;
185 : }
186 :
187 : txNodeSet* oldSet = static_cast<txNodeSet*>
188 0 : (static_cast<txAExprResult*>(res));
189 0 : nsRefPtr<txNodeSet> newSet;
190 0 : rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
191 0 : getter_AddRefs(newSet));
192 0 : NS_ENSURE_SUCCESS(rv, rv);
193 :
194 0 : resNodes->addAndTransfer(newSet);
195 :
196 0 : bool filterWS = aContext->isStripSpaceAllowed(aNode);
197 :
198 0 : txXPathTreeWalker walker(aNode);
199 0 : if (!walker.moveToFirstChild()) {
200 0 : return NS_OK;
201 : }
202 :
203 0 : do {
204 0 : const txXPathNode& node = walker.getCurrentPosition();
205 0 : if (!(filterWS && txXPathNodeUtils::isText(node) &&
206 0 : txXPathNodeUtils::isWhitespace(node))) {
207 0 : rv = evalDescendants(aStep, node, aContext, resNodes);
208 0 : NS_ENSURE_SUCCESS(rv, rv);
209 : }
210 : } while (walker.moveToNextSibling());
211 :
212 0 : return NS_OK;
213 : } //-- evalDescendants
214 :
215 : Expr::ExprType
216 0 : PathExpr::getType()
217 : {
218 0 : return PATH_EXPR;
219 : }
220 :
221 0 : TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
222 :
223 : Expr*
224 0 : PathExpr::getSubExprAt(PRUint32 aPos)
225 : {
226 0 : return aPos < mItems.Length() ? mItems[aPos].expr.get() : nsnull;
227 : }
228 : void
229 0 : PathExpr::setSubExprAt(PRUint32 aPos, Expr* aExpr)
230 : {
231 0 : NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
232 0 : mItems[aPos].expr.forget();
233 0 : mItems[aPos].expr = aExpr;
234 0 : }
235 :
236 :
237 : bool
238 0 : PathExpr::isSensitiveTo(ContextSensitivity aContext)
239 : {
240 0 : if (mItems[0].expr->isSensitiveTo(aContext)) {
241 0 : return true;
242 : }
243 :
244 : // We're creating a new node/nodeset so we can ignore those bits.
245 : Expr::ContextSensitivity context =
246 0 : aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
247 0 : if (context == NO_CONTEXT) {
248 0 : return false;
249 : }
250 :
251 0 : PRUint32 i, len = mItems.Length();
252 0 : for (i = 0; i < len; ++i) {
253 0 : NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
254 : "Step cannot depend on nodeset-context");
255 0 : if (mItems[i].expr->isSensitiveTo(context)) {
256 0 : return true;
257 : }
258 : }
259 :
260 0 : return false;
261 : }
262 :
263 : #ifdef TX_TO_STRING
264 : void
265 0 : PathExpr::toString(nsAString& dest)
266 : {
267 0 : if (!mItems.IsEmpty()) {
268 0 : NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
269 : "First step should be relative");
270 0 : mItems[0].expr->toString(dest);
271 : }
272 :
273 0 : PRUint32 i, len = mItems.Length();
274 0 : for (i = 1; i < len; ++i) {
275 0 : switch (mItems[i].pathOp) {
276 : case DESCENDANT_OP:
277 0 : dest.AppendLiteral("//");
278 0 : break;
279 : case RELATIVE_OP:
280 0 : dest.Append(PRUnichar('/'));
281 0 : break;
282 : }
283 0 : mItems[i].expr->toString(dest);
284 : }
285 0 : }
286 : #endif
|