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 : * Axel Hecht.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Axel Hecht <axel@pike.org>
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 "nsReadableUtils.h"
40 : #include "txExecutionState.h"
41 : #include "txXSLTPatterns.h"
42 : #include "txNodeSetContext.h"
43 : #include "txForwardContext.h"
44 : #include "txXMLUtils.h"
45 : #include "txXSLTFunctions.h"
46 : #include "nsWhitespaceTokenizer.h"
47 : #include "nsIContent.h"
48 :
49 : /*
50 : * Returns the default priority of this Pattern.
51 : * UnionPatterns don't like this.
52 : * This should be called on the simple patterns.
53 : */
54 0 : double txUnionPattern::getDefaultPriority()
55 : {
56 0 : NS_ERROR("Don't call getDefaultPriority on txUnionPattern");
57 0 : return txDouble::NaN;
58 : }
59 :
60 : /*
61 : * Determines whether this Pattern matches the given node within
62 : * the given context
63 : * This should be called on the simple patterns for xsl:template,
64 : * but is fine for xsl:key and xsl:number
65 : */
66 0 : bool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
67 : {
68 0 : PRUint32 i, len = mLocPathPatterns.Length();
69 0 : for (i = 0; i < len; ++i) {
70 0 : if (mLocPathPatterns[i]->matches(aNode, aContext)) {
71 0 : return true;
72 : }
73 : }
74 0 : return false;
75 : }
76 :
77 : txPattern::Type
78 0 : txUnionPattern::getType()
79 : {
80 0 : return UNION_PATTERN;
81 : }
82 :
83 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txUnionPattern)
84 : txPattern*
85 0 : txUnionPattern::getSubPatternAt(PRUint32 aPos)
86 : {
87 0 : return mLocPathPatterns.SafeElementAt(aPos);
88 : }
89 :
90 : void
91 0 : txUnionPattern::setSubPatternAt(PRUint32 aPos, txPattern* aPattern)
92 : {
93 0 : NS_ASSERTION(aPos < mLocPathPatterns.Length(),
94 : "setting bad subexpression index");
95 0 : mLocPathPatterns[aPos] = aPattern;
96 0 : }
97 :
98 :
99 : #ifdef TX_TO_STRING
100 : void
101 0 : txUnionPattern::toString(nsAString& aDest)
102 : {
103 : #ifdef DEBUG
104 0 : aDest.AppendLiteral("txUnionPattern{");
105 : #endif
106 0 : for (PRUint32 i = 0; i < mLocPathPatterns.Length(); ++i) {
107 0 : if (i != 0)
108 0 : aDest.AppendLiteral(" | ");
109 0 : mLocPathPatterns[i]->toString(aDest);
110 : }
111 : #ifdef DEBUG
112 0 : aDest.Append(PRUnichar('}'));
113 : #endif
114 0 : }
115 : #endif
116 :
117 :
118 : /*
119 : * LocationPathPattern
120 : *
121 : * a list of step patterns, can start with id or key
122 : * (dealt with by the parser)
123 : */
124 :
125 0 : nsresult txLocPathPattern::addStep(txPattern* aPattern, bool isChild)
126 : {
127 0 : Step* step = mSteps.AppendElement();
128 0 : if (!step)
129 0 : return NS_ERROR_OUT_OF_MEMORY;
130 :
131 0 : step->pattern = aPattern;
132 0 : step->isChild = isChild;
133 :
134 0 : return NS_OK;
135 : }
136 :
137 0 : bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
138 : {
139 0 : NS_ASSERTION(mSteps.Length() > 1, "Internal error");
140 :
141 : /*
142 : * The idea is to split up a path into blocks separated by descendant
143 : * operators. For example "foo/bar//baz/bop//ying/yang" is split up into
144 : * three blocks. The "ying/yang" block is handled by the first while-loop
145 : * and the "foo/bar" and "baz/bop" blocks are handled by the second
146 : * while-loop.
147 : * A block is considered matched when we find a list of ancestors that
148 : * match the block. If there are more than one list of ancestors that
149 : * match a block we only need to find the one furthermost down in the
150 : * tree.
151 : */
152 :
153 0 : PRUint32 pos = mSteps.Length();
154 0 : Step* step = &mSteps[--pos];
155 0 : if (!step->pattern->matches(aNode, aContext))
156 0 : return false;
157 :
158 0 : txXPathTreeWalker walker(aNode);
159 0 : bool hasParent = walker.moveToParent();
160 :
161 0 : while (step->isChild) {
162 0 : if (!pos)
163 0 : return true; // all steps matched
164 0 : step = &mSteps[--pos];
165 0 : if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
166 0 : return false; // no more ancestors or no match
167 :
168 0 : hasParent = walker.moveToParent();
169 : }
170 :
171 : // We have at least one // path separator
172 0 : txXPathTreeWalker blockWalker(walker);
173 0 : PRUint32 blockPos = pos;
174 :
175 0 : while (pos) {
176 0 : if (!hasParent)
177 0 : return false; // There are more steps in the current block
178 : // than ancestors of the tested node
179 :
180 0 : step = &mSteps[--pos];
181 0 : if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
182 : // Didn't match. We restart at beginning of block using a new
183 : // start node
184 0 : pos = blockPos;
185 0 : hasParent = blockWalker.moveToParent();
186 0 : walker.moveTo(blockWalker);
187 : }
188 : else {
189 0 : hasParent = walker.moveToParent();
190 0 : if (!step->isChild) {
191 : // We've matched an entire block. Set new start pos and start node
192 0 : blockPos = pos;
193 0 : blockWalker.moveTo(walker);
194 : }
195 : }
196 : }
197 :
198 0 : return true;
199 : } // txLocPathPattern::matches
200 :
201 0 : double txLocPathPattern::getDefaultPriority()
202 : {
203 0 : NS_ASSERTION(mSteps.Length() > 1, "Internal error");
204 :
205 0 : return 0.5;
206 : }
207 :
208 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txLocPathPattern)
209 : txPattern*
210 0 : txLocPathPattern::getSubPatternAt(PRUint32 aPos)
211 : {
212 0 : return aPos < mSteps.Length() ? mSteps[aPos].pattern.get() : nsnull;
213 : }
214 :
215 : void
216 0 : txLocPathPattern::setSubPatternAt(PRUint32 aPos, txPattern* aPattern)
217 : {
218 0 : NS_ASSERTION(aPos < mSteps.Length(), "setting bad subexpression index");
219 0 : Step* step = &mSteps[aPos];
220 0 : step->pattern.forget();
221 0 : step->pattern = aPattern;
222 0 : }
223 :
224 : #ifdef TX_TO_STRING
225 : void
226 0 : txLocPathPattern::toString(nsAString& aDest)
227 : {
228 : #ifdef DEBUG
229 0 : aDest.AppendLiteral("txLocPathPattern{");
230 : #endif
231 0 : for (PRUint32 i = 0; i < mSteps.Length(); ++i) {
232 0 : if (i != 0) {
233 0 : if (mSteps[i].isChild)
234 0 : aDest.Append(PRUnichar('/'));
235 : else
236 0 : aDest.AppendLiteral("//");
237 : }
238 0 : mSteps[i].pattern->toString(aDest);
239 : }
240 : #ifdef DEBUG
241 0 : aDest.Append(PRUnichar('}'));
242 : #endif
243 0 : }
244 : #endif
245 :
246 : /*
247 : * txRootPattern
248 : *
249 : * a txPattern matching the document node, or '/'
250 : */
251 :
252 0 : bool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
253 : {
254 0 : return txXPathNodeUtils::isRoot(aNode);
255 : }
256 :
257 0 : double txRootPattern::getDefaultPriority()
258 : {
259 0 : return 0.5;
260 : }
261 :
262 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txRootPattern)
263 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txRootPattern)
264 :
265 : #ifdef TX_TO_STRING
266 : void
267 0 : txRootPattern::toString(nsAString& aDest)
268 : {
269 : #ifdef DEBUG
270 0 : aDest.AppendLiteral("txRootPattern{");
271 : #endif
272 0 : if (mSerialize)
273 0 : aDest.Append(PRUnichar('/'));
274 : #ifdef DEBUG
275 0 : aDest.Append(PRUnichar('}'));
276 : #endif
277 0 : }
278 : #endif
279 :
280 : /*
281 : * txIdPattern
282 : *
283 : * txIdPattern matches if the given node has a ID attribute with one
284 : * of the space delimited values.
285 : * This looks like the id() function, but may only have LITERALs as
286 : * argument.
287 : */
288 0 : txIdPattern::txIdPattern(const nsSubstring& aString)
289 : {
290 0 : nsWhitespaceTokenizer tokenizer(aString);
291 0 : while (tokenizer.hasMoreTokens()) {
292 : // this can fail, XXX move to a Init(aString) method
293 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(tokenizer.nextToken());
294 0 : mIds.AppendObject(atom);
295 : }
296 0 : }
297 :
298 0 : bool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
299 : {
300 0 : if (!txXPathNodeUtils::isElement(aNode)) {
301 0 : return false;
302 : }
303 :
304 : // Get a ID attribute, if there is
305 0 : nsIContent* content = txXPathNativeNode::getContent(aNode);
306 0 : NS_ASSERTION(content, "a Element without nsIContent");
307 :
308 0 : nsIAtom* id = content->GetID();
309 0 : return id && mIds.IndexOf(id) > -1;
310 : }
311 :
312 0 : double txIdPattern::getDefaultPriority()
313 : {
314 0 : return 0.5;
315 : }
316 :
317 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txIdPattern)
318 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txIdPattern)
319 :
320 : #ifdef TX_TO_STRING
321 : void
322 0 : txIdPattern::toString(nsAString& aDest)
323 : {
324 : #ifdef DEBUG
325 0 : aDest.AppendLiteral("txIdPattern{");
326 : #endif
327 0 : aDest.AppendLiteral("id('");
328 0 : PRUint32 k, count = mIds.Count() - 1;
329 0 : for (k = 0; k < count; ++k) {
330 0 : nsAutoString str;
331 0 : mIds[k]->ToString(str);
332 0 : aDest.Append(str);
333 0 : aDest.Append(PRUnichar(' '));
334 : }
335 0 : nsAutoString str;
336 0 : mIds[count]->ToString(str);
337 0 : aDest.Append(str);
338 0 : aDest.Append(NS_LITERAL_STRING("')"));
339 : #ifdef DEBUG
340 0 : aDest.Append(PRUnichar('}'));
341 : #endif
342 0 : }
343 : #endif
344 :
345 : /*
346 : * txKeyPattern
347 : *
348 : * txKeyPattern matches if the given node is in the evalation of
349 : * the key() function
350 : * This resembles the key() function, but may only have LITERALs as
351 : * argument.
352 : */
353 :
354 0 : bool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
355 : {
356 0 : txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
357 0 : nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
358 0 : NS_ENSURE_TRUE(contextDoc, false);
359 :
360 0 : nsRefPtr<txNodeSet> nodes;
361 0 : nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, true,
362 0 : getter_AddRefs(nodes));
363 0 : NS_ENSURE_SUCCESS(rv, false);
364 :
365 0 : return nodes->contains(aNode);
366 : }
367 :
368 0 : double txKeyPattern::getDefaultPriority()
369 : {
370 0 : return 0.5;
371 : }
372 :
373 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txKeyPattern)
374 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txKeyPattern)
375 :
376 : #ifdef TX_TO_STRING
377 : void
378 0 : txKeyPattern::toString(nsAString& aDest)
379 : {
380 : #ifdef DEBUG
381 0 : aDest.AppendLiteral("txKeyPattern{");
382 : #endif
383 0 : aDest.AppendLiteral("key('");
384 0 : nsAutoString tmp;
385 0 : if (mPrefix) {
386 0 : mPrefix->ToString(tmp);
387 0 : aDest.Append(tmp);
388 0 : aDest.Append(PRUnichar(':'));
389 : }
390 0 : mName.mLocalName->ToString(tmp);
391 0 : aDest.Append(tmp);
392 0 : aDest.AppendLiteral(", ");
393 0 : aDest.Append(mValue);
394 0 : aDest.Append(NS_LITERAL_STRING("')"));
395 : #ifdef DEBUG
396 0 : aDest.Append(PRUnichar('}'));
397 : #endif
398 0 : }
399 : #endif
400 :
401 : /*
402 : * txStepPattern
403 : *
404 : * a txPattern to hold the NodeTest and the Predicates of a StepPattern
405 : */
406 :
407 0 : bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
408 : {
409 0 : NS_ASSERTION(mNodeTest, "Internal error");
410 :
411 0 : if (!mNodeTest->matches(aNode, aContext))
412 0 : return false;
413 :
414 0 : txXPathTreeWalker walker(aNode);
415 0 : if ((!mIsAttr &&
416 0 : txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) ||
417 0 : !walker.moveToParent()) {
418 0 : return false;
419 : }
420 0 : if (isEmpty()) {
421 0 : return true;
422 : }
423 :
424 : /*
425 : * Evaluate Predicates
426 : *
427 : * Copy all siblings/attributes matching mNodeTest to nodes
428 : * Up to the last Predicate do
429 : * Foreach node in nodes
430 : * evaluate Predicate with node as context node
431 : * if the result is a number, check the context position,
432 : * otherwise convert to bool
433 : * if result is true, copy node to newNodes
434 : * if aNode is not member of newNodes, return false
435 : * nodes = newNodes
436 : *
437 : * For the last Predicate, evaluate Predicate with aNode as
438 : * context node, if the result is a number, check the position,
439 : * otherwise return the result converted to boolean
440 : */
441 :
442 : // Create the context node set for evaluating the predicates
443 0 : nsRefPtr<txNodeSet> nodes;
444 0 : nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
445 0 : NS_ENSURE_SUCCESS(rv, false);
446 :
447 : bool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
448 0 : walker.moveToFirstChild();
449 0 : while (hasNext) {
450 0 : if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
451 0 : nodes->append(walker.getCurrentPosition());
452 : }
453 : hasNext = mIsAttr ? walker.moveToNextAttribute() :
454 0 : walker.moveToNextSibling();
455 : }
456 :
457 0 : Expr* predicate = mPredicates[0];
458 0 : nsRefPtr<txNodeSet> newNodes;
459 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
460 0 : NS_ENSURE_SUCCESS(rv, false);
461 :
462 0 : PRUint32 i, predLen = mPredicates.Length();
463 0 : for (i = 1; i < predLen; ++i) {
464 0 : newNodes->clear();
465 0 : bool contextIsInPredicate = false;
466 0 : txNodeSetContext predContext(nodes, aContext);
467 0 : while (predContext.hasNext()) {
468 0 : predContext.next();
469 0 : nsRefPtr<txAExprResult> exprResult;
470 0 : rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
471 0 : NS_ENSURE_SUCCESS(rv, false);
472 :
473 0 : switch(exprResult->getResultType()) {
474 : case txAExprResult::NUMBER:
475 : // handle default, [position() == numberValue()]
476 0 : if ((double)predContext.position() ==
477 0 : exprResult->numberValue()) {
478 0 : const txXPathNode& tmp = predContext.getContextNode();
479 0 : if (tmp == aNode)
480 0 : contextIsInPredicate = true;
481 0 : newNodes->append(tmp);
482 : }
483 0 : break;
484 : default:
485 0 : if (exprResult->booleanValue()) {
486 0 : const txXPathNode& tmp = predContext.getContextNode();
487 0 : if (tmp == aNode)
488 0 : contextIsInPredicate = true;
489 0 : newNodes->append(tmp);
490 : }
491 0 : break;
492 : }
493 : }
494 : // Move new NodeSet to the current one
495 0 : nodes->clear();
496 0 : nodes->append(*newNodes);
497 0 : if (!contextIsInPredicate) {
498 0 : return false;
499 : }
500 0 : predicate = mPredicates[i];
501 : }
502 0 : txForwardContext evalContext(aContext, aNode, nodes);
503 0 : nsRefPtr<txAExprResult> exprResult;
504 0 : rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
505 0 : NS_ENSURE_SUCCESS(rv, false);
506 :
507 0 : if (exprResult->getResultType() == txAExprResult::NUMBER)
508 : // handle default, [position() == numberValue()]
509 0 : return ((double)evalContext.position() == exprResult->numberValue());
510 :
511 0 : return exprResult->booleanValue();
512 : } // matches
513 :
514 0 : double txStepPattern::getDefaultPriority()
515 : {
516 0 : if (isEmpty())
517 0 : return mNodeTest->getDefaultPriority();
518 0 : return 0.5;
519 : }
520 :
521 : txPattern::Type
522 0 : txStepPattern::getType()
523 : {
524 0 : return STEP_PATTERN;
525 : }
526 :
527 0 : TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txStepPattern)
528 : Expr*
529 0 : txStepPattern::getSubExprAt(PRUint32 aPos)
530 : {
531 0 : return PredicateList::getSubExprAt(aPos);
532 : }
533 :
534 : void
535 0 : txStepPattern::setSubExprAt(PRUint32 aPos, Expr* aExpr)
536 : {
537 0 : PredicateList::setSubExprAt(aPos, aExpr);
538 0 : }
539 :
540 : #ifdef TX_TO_STRING
541 : void
542 0 : txStepPattern::toString(nsAString& aDest)
543 : {
544 : #ifdef DEBUG
545 0 : aDest.AppendLiteral("txStepPattern{");
546 : #endif
547 0 : if (mIsAttr)
548 0 : aDest.Append(PRUnichar('@'));
549 0 : if (mNodeTest)
550 0 : mNodeTest->toString(aDest);
551 :
552 0 : PredicateList::toString(aDest);
553 : #ifdef DEBUG
554 0 : aDest.Append(PRUnichar('}'));
555 : #endif
556 0 : }
557 : #endif
|