~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/extensions/transformiix/source/xslt/txXSLTPatterns.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
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 "XMLUtils.h"
 
45
#include "XSLTFunctions.h"
 
46
#ifndef TX_EXE
 
47
#include "nsIContent.h"
 
48
#include "nsINodeInfo.h"
 
49
#endif
 
50
 
 
51
/*
 
52
 * txPattern
 
53
 *
 
54
 * Base class of all patterns
 
55
 * Implements only a default getSimplePatterns
 
56
 */
 
57
nsresult txPattern::getSimplePatterns(txList& aList)
 
58
{
 
59
    aList.add(this);
 
60
    return NS_OK;
 
61
}
 
62
 
 
63
txPattern::~txPattern()
 
64
{
 
65
}
 
66
 
 
67
 
 
68
/*
 
69
 * txUnionPattern
 
70
 *
 
71
 * This pattern is returned by the parser for "foo | bar" constructs.
 
72
 * |xsl:template|s should use the simple patterns
 
73
 */
 
74
 
 
75
/*
 
76
 * Destructor, deletes all LocationPathPatterns
 
77
 */
 
78
txUnionPattern::~txUnionPattern()
 
79
{
 
80
    txListIterator iter(&mLocPathPatterns);
 
81
    while (iter.hasNext()) {
 
82
        delete (txPattern*)iter.next();
 
83
    }
 
84
}
 
85
 
 
86
nsresult txUnionPattern::addPattern(txPattern* aPattern)
 
87
{
 
88
    if (!aPattern)
 
89
        return NS_ERROR_NULL_POINTER;
 
90
    mLocPathPatterns.add(aPattern);
 
91
    return NS_OK;
 
92
}
 
93
 
 
94
/*
 
95
 * Returns the default priority of this Pattern.
 
96
 * UnionPatterns don't like this.
 
97
 * This should be called on the simple patterns.
 
98
 */
 
99
double txUnionPattern::getDefaultPriority()
 
100
{
 
101
    NS_ASSERTION(0, "Don't call getDefaultPriority on txUnionPattern");
 
102
    return Double::NaN;
 
103
}
 
104
 
 
105
/*
 
106
 * Determines whether this Pattern matches the given node within
 
107
 * the given context
 
108
 * This should be called on the simple patterns for xsl:template,
 
109
 * but is fine for xsl:key and xsl:number
 
110
 */
 
111
MBool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
112
{
 
113
    txListIterator iter(&mLocPathPatterns);
 
114
    while (iter.hasNext()) {
 
115
        txPattern* p = (txPattern*)iter.next();
 
116
        if (p->matches(aNode, aContext)) {
 
117
            return MB_TRUE;
 
118
        }
 
119
    }
 
120
    return MB_FALSE;
 
121
}
 
122
 
 
123
nsresult txUnionPattern::getSimplePatterns(txList& aList)
 
124
{
 
125
    txListIterator iter(&mLocPathPatterns);
 
126
    while (iter.hasNext()) {
 
127
        aList.add(iter.next());
 
128
        iter.remove();
 
129
    }
 
130
    return NS_OK;
 
131
}
 
132
 
 
133
/*
 
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.
 
137
 */
 
138
void txUnionPattern::toString(nsAString& aDest)
 
139
{
 
140
#ifdef DEBUG
 
141
    aDest.Append(NS_LITERAL_STRING("txUnionPattern{"));
 
142
#endif
 
143
    txListIterator iter(&mLocPathPatterns);
 
144
    if (iter.hasNext())
 
145
        ((txPattern*)iter.next())->toString(aDest);
 
146
    while (iter.hasNext()) {
 
147
        aDest.Append(NS_LITERAL_STRING(" | "));
 
148
        ((txPattern*)iter.next())->toString(aDest);
 
149
    }
 
150
#ifdef DEBUG
 
151
    aDest.Append(PRUnichar('}'));
 
152
#endif
 
153
} // toString
 
154
 
 
155
 
 
156
/*
 
157
 * LocationPathPattern
 
158
 *
 
159
 * a list of step patterns, can start with id or key
 
160
 * (dealt with by the parser)
 
161
 */
 
162
 
 
163
/*
 
164
 * Destructor, deletes all PathPatterns
 
165
 */
 
166
txLocPathPattern::~txLocPathPattern()
 
167
{
 
168
    txListIterator iter(&mSteps);
 
169
    while (iter.hasNext()) {
 
170
         delete (Step*)iter.next();
 
171
    }
 
172
}
 
173
 
 
174
nsresult txLocPathPattern::addStep(txPattern* aPattern, MBool isChild)
 
175
{
 
176
    if (!aPattern)
 
177
        return NS_ERROR_NULL_POINTER;
 
178
    Step* step = new Step(aPattern, isChild);
 
179
    if (!step)
 
180
        return NS_ERROR_OUT_OF_MEMORY;
 
181
    mSteps.add(step);
 
182
    return NS_OK;
 
183
}
 
184
 
 
185
MBool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
186
{
 
187
    NS_ASSERTION(mSteps.getLength(), "Internal error");
 
188
 
 
189
    /*
 
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
 
194
     * while-loop.
 
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
 
198
     * tree.
 
199
     */
 
200
 
 
201
    txListIterator iter(&mSteps);
 
202
    iter.resetToEnd();
 
203
 
 
204
    Step* step;
 
205
    step = (Step*)iter.previous();
 
206
    if (!step->pattern->matches(aNode, aContext))
 
207
        return MB_FALSE;
 
208
 
 
209
    txXPathTreeWalker walker(aNode);
 
210
    PRBool hasParent = walker.moveToParent();
 
211
 
 
212
    while (step->isChild) {
 
213
        step = (Step*)iter.previous();
 
214
        if (!step)
 
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
 
218
 
 
219
        hasParent = walker.moveToParent();
 
220
    }
 
221
 
 
222
    // We have at least one // path separator
 
223
    txXPathTreeWalker blockWalker(walker);
 
224
    txListIterator blockIter(iter);
 
225
 
 
226
    while ((step = (Step*)iter.previous())) {
 
227
        if (!hasParent)
 
228
            return MB_FALSE; // There are more steps in the current block 
 
229
                             // than ancestors of the tested node
 
230
 
 
231
        if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
 
232
            // Didn't match. We restart at beginning of block using a new
 
233
            // start node
 
234
            iter = blockIter;
 
235
            hasParent = blockWalker.moveToParent();
 
236
            walker.moveTo(blockWalker);
 
237
        }
 
238
        else {
 
239
            hasParent = walker.moveToParent();
 
240
            if (!step->isChild) {
 
241
                // We've matched an entire block. Set new start iter and start node
 
242
                blockIter = iter;
 
243
                blockWalker.moveTo(walker);
 
244
            }
 
245
        }
 
246
    }
 
247
 
 
248
    return MB_TRUE;
 
249
} // txLocPathPattern::matches
 
250
 
 
251
double txLocPathPattern::getDefaultPriority()
 
252
{
 
253
    if (mSteps.getLength() > 1) {
 
254
        return 0.5;
 
255
    }
 
256
 
 
257
    return ((Step*)mSteps.get(0))->pattern->getDefaultPriority();
 
258
}
 
259
 
 
260
void txLocPathPattern::toString(nsAString& aDest)
 
261
{
 
262
    txListIterator iter(&mSteps);
 
263
#ifdef DEBUG
 
264
    aDest.Append(NS_LITERAL_STRING("txLocPathPattern{"));
 
265
#endif
 
266
    Step* step;
 
267
    step = (Step*)iter.next();
 
268
    if (step) {
 
269
        step->pattern->toString(aDest);
 
270
    }
 
271
    while ((step = (Step*)iter.next())) {
 
272
        if (step->isChild)
 
273
            aDest.Append(PRUnichar('/'));
 
274
        else
 
275
            aDest.Append(NS_LITERAL_STRING("//"));
 
276
        step->pattern->toString(aDest);
 
277
    }
 
278
#ifdef DEBUG
 
279
    aDest.Append(PRUnichar('}'));
 
280
#endif
 
281
} // txLocPathPattern::toString
 
282
 
 
283
/*
 
284
 * txRootPattern
 
285
 *
 
286
 * a txPattern matching the document node, or '/'
 
287
 */
 
288
 
 
289
txRootPattern::~txRootPattern()
 
290
{
 
291
}
 
292
 
 
293
MBool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
294
{
 
295
    return (txXPathNodeUtils::getNodeType(aNode) == txXPathNodeType::DOCUMENT_NODE);
 
296
}
 
297
 
 
298
double txRootPattern::getDefaultPriority()
 
299
{
 
300
    return 0.5;
 
301
}
 
302
 
 
303
void txRootPattern::toString(nsAString& aDest)
 
304
{
 
305
#ifdef DEBUG
 
306
    aDest.Append(NS_LITERAL_STRING("txRootPattern{"));
 
307
#endif
 
308
    if (mSerialize)
 
309
        aDest.Append(PRUnichar('/'));
 
310
#ifdef DEBUG
 
311
    aDest.Append(PRUnichar('}'));
 
312
#endif
 
313
}
 
314
 
 
315
/*
 
316
 * txIdPattern
 
317
 *
 
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
 
321
 * argument.
 
322
 */
 
323
txIdPattern::txIdPattern(const nsAString& aString)
 
324
{
 
325
    nsAString::const_iterator pos, begin, end;
 
326
    aString.BeginReading(begin);
 
327
    aString.EndReading(end);
 
328
    pos = begin;
 
329
    while (pos != end) {
 
330
        while (pos != end && XMLUtils::isWhitespace(*pos))
 
331
            ++pos;
 
332
        begin = pos;
 
333
        while (pos != end && !XMLUtils::isWhitespace(*pos))
 
334
            ++pos;
 
335
        // this can fail, XXX move to a Init(aString) method
 
336
        mIds.AppendString(Substring(begin, pos));
 
337
    }
 
338
}
 
339
 
 
340
txIdPattern::~txIdPattern()
 
341
{
 
342
}
 
343
 
 
344
MBool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
345
{
 
346
    if (txXPathNodeUtils::getNodeType(aNode) != txXPathNodeType::ELEMENT_NODE) {
 
347
        return MB_FALSE;
 
348
    }
 
349
 
 
350
    // Get a ID attribute, if there is
 
351
    nsAutoString value;
 
352
#ifdef TX_EXE
 
353
    Element* elem;
 
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)) {
 
357
        return PR_FALSE;
 
358
    }
 
359
#else
 
360
    nsIContent* content = txXPathNativeNode::getContent(aNode);
 
361
    NS_ASSERTION(content, "a Element without nsIContent");
 
362
    if (!content) {
 
363
        return MB_FALSE;
 
364
    }
 
365
 
 
366
    nsIAtom* idAttr = content->GetIDAttributeName();
 
367
    if (!idAttr) {
 
368
        return MB_FALSE; // no ID for this element defined, can't match
 
369
    }
 
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
 
373
    }
 
374
#endif // TX_EXE
 
375
    return mIds.IndexOf(value) > -1;
 
376
}
 
377
 
 
378
double txIdPattern::getDefaultPriority()
 
379
{
 
380
    return 0.5;
 
381
}
 
382
 
 
383
void txIdPattern::toString(nsAString& aDest)
 
384
{
 
385
#ifdef DEBUG
 
386
    aDest.Append(NS_LITERAL_STRING("txIdPattern{"));
 
387
#endif
 
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(' '));
 
393
    }
 
394
    aDest.Append(*mIds[count]);
 
395
    aDest.Append(NS_LITERAL_STRING("')"));
 
396
#ifdef DEBUG
 
397
    aDest.Append(PRUnichar('}'));
 
398
#endif
 
399
}
 
400
 
 
401
/*
 
402
 * txKeyPattern
 
403
 *
 
404
 * txKeyPattern matches if the given node is in the evalation of 
 
405
 * the key() function
 
406
 * This resembles the key() function, but may only have LITERALs as
 
407
 * argument.
 
408
 */
 
409
 
 
410
txKeyPattern::~txKeyPattern()
 
411
{
 
412
}
 
413
 
 
414
MBool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
415
{
 
416
    txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
 
417
    nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
 
418
    NS_ENSURE_TRUE(contextDoc, PR_FALSE);
 
419
 
 
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);
 
424
 
 
425
    return nodes->contains(aNode);
 
426
}
 
427
 
 
428
double txKeyPattern::getDefaultPriority()
 
429
{
 
430
    return 0.5;
 
431
}
 
432
 
 
433
void txKeyPattern::toString(nsAString& aDest)
 
434
{
 
435
#ifdef DEBUG
 
436
    aDest.Append(NS_LITERAL_STRING("txKeyPattern{"));
 
437
#endif
 
438
    aDest.Append(NS_LITERAL_STRING("key('"));
 
439
    nsAutoString tmp;
 
440
    if (mPrefix) {
 
441
        mPrefix->ToString(tmp);
 
442
        aDest.Append(tmp);
 
443
        aDest.Append(PRUnichar(':'));
 
444
    }
 
445
    mName.mLocalName->ToString(tmp);
 
446
    aDest.Append(tmp);
 
447
    aDest.Append(NS_LITERAL_STRING(", "));
 
448
    aDest.Append(mValue);
 
449
    aDest.Append(NS_LITERAL_STRING("')"));
 
450
#ifdef DEBUG
 
451
    aDest.Append(PRUnichar('}'));
 
452
#endif
 
453
}
 
454
 
 
455
/*
 
456
 * txStepPattern
 
457
 *
 
458
 * a txPattern to hold the NodeTest and the Predicates of a StepPattern
 
459
 */
 
460
 
 
461
txStepPattern::~txStepPattern()
 
462
{
 
463
    delete mNodeTest;
 
464
}
 
465
 
 
466
MBool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
467
{
 
468
    NS_ASSERTION(mNodeTest, "Internal error");
 
469
 
 
470
    if (!mNodeTest->matches(aNode, aContext))
 
471
        return MB_FALSE;
 
472
 
 
473
    txXPathTreeWalker walker(aNode);
 
474
    if ((!mIsAttr && walker.getNodeType() == txXPathNodeType::ATTRIBUTE_NODE) ||
 
475
        !walker.moveToParent()) {
 
476
        return MB_FALSE;
 
477
    }
 
478
    if (isEmpty()) {
 
479
        return MB_TRUE;
 
480
    }
 
481
 
 
482
    /*
 
483
     * Evaluate Predicates
 
484
     *
 
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
 
493
     *  nodes = newNodes
 
494
     *
 
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
 
498
     */
 
499
 
 
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);
 
504
 
 
505
    PRBool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
 
506
                               walker.moveToFirstChild();
 
507
    while (hasNext) {
 
508
        if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
 
509
            nodes->append(walker.getCurrentPosition());
 
510
        }
 
511
        hasNext = mIsAttr ? walker.moveToNextAttribute() :
 
512
                            walker.moveToNextSibling();
 
513
    }
 
514
 
 
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);
 
520
 
 
521
    while (iter.hasNext()) {
 
522
        newNodes->clear();
 
523
        MBool contextIsInPredicate = MB_FALSE;
 
524
        txNodeSetContext predContext(nodes, aContext);
 
525
        while (predContext.hasNext()) {
 
526
            predContext.next();
 
527
            nsRefPtr<txAExprResult> exprResult;
 
528
            rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
 
529
            NS_ENSURE_SUCCESS(rv, PR_FALSE);
 
530
 
 
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();
 
537
                        if (tmp == aNode)
 
538
                            contextIsInPredicate = MB_TRUE;
 
539
                        newNodes->append(tmp);
 
540
                    }
 
541
                    break;
 
542
                default:
 
543
                    if (exprResult->booleanValue()) {
 
544
                        const txXPathNode& tmp = predContext.getContextNode();
 
545
                        if (tmp == aNode)
 
546
                            contextIsInPredicate = MB_TRUE;
 
547
                        newNodes->append(tmp);
 
548
                    }
 
549
                    break;
 
550
            }
 
551
        }
 
552
        // Move new NodeSet to the current one
 
553
        nodes->clear();
 
554
        nodes->append(*newNodes);
 
555
        if (!contextIsInPredicate) {
 
556
            return MB_FALSE;
 
557
        }
 
558
        predicate = (Expr*)iter.next();
 
559
    }
 
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);
 
564
 
 
565
    if (exprResult->getResultType() == txAExprResult::NUMBER)
 
566
        // handle default, [position() == numberValue()]
 
567
        return ((double)evalContext.position() == exprResult->numberValue());
 
568
 
 
569
    return exprResult->booleanValue();
 
570
} // matches
 
571
 
 
572
double txStepPattern::getDefaultPriority()
 
573
{
 
574
    if (isEmpty())
 
575
        return mNodeTest->getDefaultPriority();
 
576
    return 0.5;
 
577
}
 
578
 
 
579
void txStepPattern::toString(nsAString& aDest)
 
580
{
 
581
#ifdef DEBUG
 
582
    aDest.Append(NS_LITERAL_STRING("txStepPattern{"));
 
583
#endif
 
584
    if (mIsAttr)
 
585
        aDest.Append(PRUnichar('@'));
 
586
    if (mNodeTest)
 
587
        mNodeTest->toString(aDest);
 
588
 
 
589
    PredicateList::toString(aDest);
 
590
#ifdef DEBUG
 
591
    aDest.Append(PRUnichar('}'));
 
592
#endif
 
593
}