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
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/
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
15
* The Original Code is TransforMiiX XSLT Processor.
17
* The Initial Developer of the Original Code is
19
* Portions created by the Initial Developer are Copyright (C) 2002
20
* the Initial Developer. All Rights Reserved.
23
* Axel Hecht <axel@pike.org>
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.
37
* ***** END LICENSE BLOCK ***** */
39
#include "nsReadableUtils.h"
40
#include "txExecutionState.h"
41
#include "txXSLTPatterns.h"
42
#include "txNodeSetContext.h"
43
#include "txForwardContext.h"
45
#include "XSLTFunctions.h"
47
#include "nsIContent.h"
48
#include "nsINodeInfo.h"
54
* Base class of all patterns
55
* Implements only a default getSimplePatterns
57
nsresult txPattern::getSimplePatterns(txList& aList)
63
txPattern::~txPattern()
71
* This pattern is returned by the parser for "foo | bar" constructs.
72
* |xsl:template|s should use the simple patterns
76
* Destructor, deletes all LocationPathPatterns
78
txUnionPattern::~txUnionPattern()
80
txListIterator iter(&mLocPathPatterns);
81
while (iter.hasNext()) {
82
delete (txPattern*)iter.next();
86
nsresult txUnionPattern::addPattern(txPattern* aPattern)
89
return NS_ERROR_NULL_POINTER;
90
mLocPathPatterns.add(aPattern);
95
* Returns the default priority of this Pattern.
96
* UnionPatterns don't like this.
97
* This should be called on the simple patterns.
99
double txUnionPattern::getDefaultPriority()
101
NS_ASSERTION(0, "Don't call getDefaultPriority on txUnionPattern");
106
* Determines whether this Pattern matches the given node within
108
* This should be called on the simple patterns for xsl:template,
109
* but is fine for xsl:key and xsl:number
111
MBool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
113
txListIterator iter(&mLocPathPatterns);
114
while (iter.hasNext()) {
115
txPattern* p = (txPattern*)iter.next();
116
if (p->matches(aNode, aContext)) {
123
nsresult txUnionPattern::getSimplePatterns(txList& aList)
125
txListIterator iter(&mLocPathPatterns);
126
while (iter.hasNext()) {
127
aList.add(iter.next());
134
* The String representation will be appended to any data in the
135
* destination String, to allow cascading calls to other
136
* toString() methods for mLocPathPatterns.
138
void txUnionPattern::toString(nsAString& aDest)
141
aDest.Append(NS_LITERAL_STRING("txUnionPattern{"));
143
txListIterator iter(&mLocPathPatterns);
145
((txPattern*)iter.next())->toString(aDest);
146
while (iter.hasNext()) {
147
aDest.Append(NS_LITERAL_STRING(" | "));
148
((txPattern*)iter.next())->toString(aDest);
151
aDest.Append(PRUnichar('}'));
157
* LocationPathPattern
159
* a list of step patterns, can start with id or key
160
* (dealt with by the parser)
164
* Destructor, deletes all PathPatterns
166
txLocPathPattern::~txLocPathPattern()
168
txListIterator iter(&mSteps);
169
while (iter.hasNext()) {
170
delete (Step*)iter.next();
174
nsresult txLocPathPattern::addStep(txPattern* aPattern, MBool isChild)
177
return NS_ERROR_NULL_POINTER;
178
Step* step = new Step(aPattern, isChild);
180
return NS_ERROR_OUT_OF_MEMORY;
185
MBool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
187
NS_ASSERTION(mSteps.getLength(), "Internal error");
190
* The idea is to split up a path into blocks separated by descendant
191
* operators. For example "foo/bar//baz/bop//ying/yang" is split up into
192
* three blocks. The "ying/yang" block is handled by the first while-loop
193
* and the "foo/bar" and "baz/bop" blocks are handled by the second
195
* A block is considered matched when we find a list of ancestors that
196
* match the block. If there are more than one list of ancestors that
197
* match a block we only need to find the one furthermost down in the
201
txListIterator iter(&mSteps);
205
step = (Step*)iter.previous();
206
if (!step->pattern->matches(aNode, aContext))
209
txXPathTreeWalker walker(aNode);
210
PRBool hasParent = walker.moveToParent();
212
while (step->isChild) {
213
step = (Step*)iter.previous();
215
return MB_TRUE; // all steps matched
216
if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
217
return MB_FALSE; // no more ancestors or no match
219
hasParent = walker.moveToParent();
222
// We have at least one // path separator
223
txXPathTreeWalker blockWalker(walker);
224
txListIterator blockIter(iter);
226
while ((step = (Step*)iter.previous())) {
228
return MB_FALSE; // There are more steps in the current block
229
// than ancestors of the tested node
231
if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
232
// Didn't match. We restart at beginning of block using a new
235
hasParent = blockWalker.moveToParent();
236
walker.moveTo(blockWalker);
239
hasParent = walker.moveToParent();
240
if (!step->isChild) {
241
// We've matched an entire block. Set new start iter and start node
243
blockWalker.moveTo(walker);
249
} // txLocPathPattern::matches
251
double txLocPathPattern::getDefaultPriority()
253
if (mSteps.getLength() > 1) {
257
return ((Step*)mSteps.get(0))->pattern->getDefaultPriority();
260
void txLocPathPattern::toString(nsAString& aDest)
262
txListIterator iter(&mSteps);
264
aDest.Append(NS_LITERAL_STRING("txLocPathPattern{"));
267
step = (Step*)iter.next();
269
step->pattern->toString(aDest);
271
while ((step = (Step*)iter.next())) {
273
aDest.Append(PRUnichar('/'));
275
aDest.Append(NS_LITERAL_STRING("//"));
276
step->pattern->toString(aDest);
279
aDest.Append(PRUnichar('}'));
281
} // txLocPathPattern::toString
286
* a txPattern matching the document node, or '/'
289
txRootPattern::~txRootPattern()
293
MBool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
295
return (txXPathNodeUtils::getNodeType(aNode) == txXPathNodeType::DOCUMENT_NODE);
298
double txRootPattern::getDefaultPriority()
303
void txRootPattern::toString(nsAString& aDest)
306
aDest.Append(NS_LITERAL_STRING("txRootPattern{"));
309
aDest.Append(PRUnichar('/'));
311
aDest.Append(PRUnichar('}'));
318
* txIdPattern matches if the given node has a ID attribute with one
319
* of the space delimited values.
320
* This looks like the id() function, but may only have LITERALs as
323
txIdPattern::txIdPattern(const nsAString& aString)
325
nsAString::const_iterator pos, begin, end;
326
aString.BeginReading(begin);
327
aString.EndReading(end);
330
while (pos != end && XMLUtils::isWhitespace(*pos))
333
while (pos != end && !XMLUtils::isWhitespace(*pos))
335
// this can fail, XXX move to a Init(aString) method
336
mIds.AppendString(Substring(begin, pos));
340
txIdPattern::~txIdPattern()
344
MBool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
346
if (txXPathNodeUtils::getNodeType(aNode) != txXPathNodeType::ELEMENT_NODE) {
350
// Get a ID attribute, if there is
354
nsresult rv = txXPathNativeNode::getElement(aNode, &elem);
355
NS_ASSERTION(NS_SUCCEEDED(rv), "So why claim it's an element above?");
356
if (!elem->getIDValue(value)) {
360
nsIContent* content = txXPathNativeNode::getContent(aNode);
361
NS_ASSERTION(content, "a Element without nsIContent");
366
nsIAtom* idAttr = content->GetIDAttributeName();
368
return MB_FALSE; // no ID for this element defined, can't match
370
nsresult rv = content->GetAttr(kNameSpaceID_None, idAttr, value);
371
if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
372
return MB_FALSE; // no ID attribute given
375
return mIds.IndexOf(value) > -1;
378
double txIdPattern::getDefaultPriority()
383
void txIdPattern::toString(nsAString& aDest)
386
aDest.Append(NS_LITERAL_STRING("txIdPattern{"));
388
aDest.Append(NS_LITERAL_STRING("id('"));
389
PRUint32 k, count = mIds.Count() - 1;
390
for (k = 0; k < count; ++k) {
391
aDest.Append(*mIds[k]);
392
aDest.Append(PRUnichar(' '));
394
aDest.Append(*mIds[count]);
395
aDest.Append(NS_LITERAL_STRING("')"));
397
aDest.Append(PRUnichar('}'));
404
* txKeyPattern matches if the given node is in the evalation of
406
* This resembles the key() function, but may only have LITERALs as
410
txKeyPattern::~txKeyPattern()
414
MBool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
416
txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
417
nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
418
NS_ENSURE_TRUE(contextDoc, PR_FALSE);
420
nsRefPtr<txNodeSet> nodes;
421
nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, PR_TRUE,
422
getter_AddRefs(nodes));
423
NS_ENSURE_SUCCESS(rv, PR_FALSE);
425
return nodes->contains(aNode);
428
double txKeyPattern::getDefaultPriority()
433
void txKeyPattern::toString(nsAString& aDest)
436
aDest.Append(NS_LITERAL_STRING("txKeyPattern{"));
438
aDest.Append(NS_LITERAL_STRING("key('"));
441
mPrefix->ToString(tmp);
443
aDest.Append(PRUnichar(':'));
445
mName.mLocalName->ToString(tmp);
447
aDest.Append(NS_LITERAL_STRING(", "));
448
aDest.Append(mValue);
449
aDest.Append(NS_LITERAL_STRING("')"));
451
aDest.Append(PRUnichar('}'));
458
* a txPattern to hold the NodeTest and the Predicates of a StepPattern
461
txStepPattern::~txStepPattern()
466
MBool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
468
NS_ASSERTION(mNodeTest, "Internal error");
470
if (!mNodeTest->matches(aNode, aContext))
473
txXPathTreeWalker walker(aNode);
474
if ((!mIsAttr && walker.getNodeType() == txXPathNodeType::ATTRIBUTE_NODE) ||
475
!walker.moveToParent()) {
483
* Evaluate Predicates
485
* Copy all siblings/attributes matching mNodeTest to nodes
486
* Up to the last Predicate do
487
* Foreach node in nodes
488
* evaluate Predicate with node as context node
489
* if the result is a number, check the context position,
490
* otherwise convert to bool
491
* if result is true, copy node to newNodes
492
* if aNode is not member of newNodes, return MB_FALSE
495
* For the last Predicate, evaluate Predicate with aNode as
496
* context node, if the result is a number, check the position,
497
* otherwise return the result converted to boolean
500
// Create the context node set for evaluating the predicates
501
nsRefPtr<txNodeSet> nodes;
502
nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
503
NS_ENSURE_SUCCESS(rv, rv);
505
PRBool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
506
walker.moveToFirstChild();
508
if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
509
nodes->append(walker.getCurrentPosition());
511
hasNext = mIsAttr ? walker.moveToNextAttribute() :
512
walker.moveToNextSibling();
515
txListIterator iter(&predicates);
516
Expr* predicate = (Expr*)iter.next();
517
nsRefPtr<txNodeSet> newNodes;
518
rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
519
NS_ENSURE_SUCCESS(rv, rv);
521
while (iter.hasNext()) {
523
MBool contextIsInPredicate = MB_FALSE;
524
txNodeSetContext predContext(nodes, aContext);
525
while (predContext.hasNext()) {
527
nsRefPtr<txAExprResult> exprResult;
528
rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
529
NS_ENSURE_SUCCESS(rv, PR_FALSE);
531
switch(exprResult->getResultType()) {
532
case txAExprResult::NUMBER:
533
// handle default, [position() == numberValue()]
534
if ((double)predContext.position() ==
535
exprResult->numberValue()) {
536
const txXPathNode& tmp = predContext.getContextNode();
538
contextIsInPredicate = MB_TRUE;
539
newNodes->append(tmp);
543
if (exprResult->booleanValue()) {
544
const txXPathNode& tmp = predContext.getContextNode();
546
contextIsInPredicate = MB_TRUE;
547
newNodes->append(tmp);
552
// Move new NodeSet to the current one
554
nodes->append(*newNodes);
555
if (!contextIsInPredicate) {
558
predicate = (Expr*)iter.next();
560
txForwardContext evalContext(aContext, aNode, nodes);
561
nsRefPtr<txAExprResult> exprResult;
562
rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
563
NS_ENSURE_SUCCESS(rv, PR_FALSE);
565
if (exprResult->getResultType() == txAExprResult::NUMBER)
566
// handle default, [position() == numberValue()]
567
return ((double)evalContext.position() == exprResult->numberValue());
569
return exprResult->booleanValue();
572
double txStepPattern::getDefaultPriority()
575
return mNodeTest->getDefaultPriority();
579
void txStepPattern::toString(nsAString& aDest)
582
aDest.Append(NS_LITERAL_STRING("txStepPattern{"));
585
aDest.Append(PRUnichar('@'));
587
mNodeTest->toString(aDest);
589
PredicateList::toString(aDest);
591
aDest.Append(PRUnichar('}'));