XPathFunctions.cpp 20.3 KB
Newer Older
1
/*
ap@webkit.org's avatar
ap@webkit.org committed
2 3
 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org>
 * Copyright (C) 2006, 2009 Apple Inc.
ap's avatar
ap committed
4
 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
darin's avatar
darin committed
27

28
#include "config.h"
darin's avatar
darin committed
29
#include "XPathFunctions.h"
30

darin's avatar
darin committed
31
#include "Element.h"
ap@webkit.org's avatar
ap@webkit.org committed
32
#include "ProcessingInstruction.h"
33
#include "TreeScope.h"
ap's avatar
ap committed
34
#include "XMLNames.h"
ap's avatar
ap committed
35
#include "XPathUtil.h"
darin's avatar
darin committed
36
#include "XPathValue.h"
darin's avatar
darin committed
37
#include <wtf/MathExtras.h>
38
#include <wtf/text/StringBuilder.h>
39

40 41
namespace WebCore {
namespace XPath {
ap's avatar
ap committed
42 43 44 45 46 47 48

static inline bool isWhitespace(UChar c)
{
    return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}


darin's avatar
darin committed
49
#define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
50

darin's avatar
darin committed
51
class Interval {
52
public:
darin's avatar
darin committed
53
    static const int Inf = -1;
54 55 56 57 58 59 60 61 62 63 64 65

    Interval();
    Interval(int value);
    Interval(int min, int max);

    bool contains(int value) const;

private:
    int m_min;
    int m_max;
};

darin's avatar
darin committed
66 67 68 69 70
struct FunctionRec {
    typedef Function *(*FactoryFn)();
    FactoryFn factoryFn;
    Interval args;
};
71

darin's avatar
darin committed
72 73 74
static HashMap<String, FunctionRec>* functionMap;

class FunLast : public Function {
weinig's avatar
weinig committed
75
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
76 77 78
    virtual Value::Type resultType() const { return Value::NumberValue; }
public:
    FunLast() { setIsContextSizeSensitive(true); }
79 80
};

darin's avatar
darin committed
81
class FunPosition : public Function {
weinig's avatar
weinig committed
82
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
83 84 85
    virtual Value::Type resultType() const { return Value::NumberValue; }
public:
    FunPosition() { setIsContextPositionSensitive(true); }
86 87
};

darin's avatar
darin committed
88
class FunCount : public Function {
weinig's avatar
weinig committed
89
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
90
    virtual Value::Type resultType() const { return Value::NumberValue; }
91 92
};

ap's avatar
ap committed
93
class FunId : public Function {
weinig's avatar
weinig committed
94
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
95
    virtual Value::Type resultType() const { return Value::NodeSetValue; }
ap's avatar
ap committed
96 97
};

darin's avatar
darin committed
98
class FunLocalName : public Function {
weinig's avatar
weinig committed
99
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
100 101 102
    virtual Value::Type resultType() const { return Value::StringValue; }
public:
    FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node. 
103 104
};

darin's avatar
darin committed
105
class FunNamespaceURI : public Function {
weinig's avatar
weinig committed
106
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
107 108 109
    virtual Value::Type resultType() const { return Value::StringValue; }
public:
    FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node. 
110 111
};

darin's avatar
darin committed
112
class FunName : public Function {
weinig's avatar
weinig committed
113
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
114 115 116
    virtual Value::Type resultType() const { return Value::StringValue; }
public:
    FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node. 
117 118
};

darin's avatar
darin committed
119
class FunString : public Function {
weinig's avatar
weinig committed
120
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
121 122 123
    virtual Value::Type resultType() const { return Value::StringValue; }
public:
    FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node. 
124 125
};

darin's avatar
darin committed
126
class FunConcat : public Function {
weinig's avatar
weinig committed
127
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
128
    virtual Value::Type resultType() const { return Value::StringValue; }
129 130
};

darin's avatar
darin committed
131
class FunStartsWith : public Function {
weinig's avatar
weinig committed
132
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
133
    virtual Value::Type resultType() const { return Value::BooleanValue; }
134 135
};

darin's avatar
darin committed
136
class FunContains : public Function {
weinig's avatar
weinig committed
137
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
138
    virtual Value::Type resultType() const { return Value::BooleanValue; }
139 140
};

darin's avatar
darin committed
141
class FunSubstringBefore : public Function {
weinig's avatar
weinig committed
142
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
143
    virtual Value::Type resultType() const { return Value::StringValue; }
144 145
};

darin's avatar
darin committed
146
class FunSubstringAfter : public Function {
weinig's avatar
weinig committed
147
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
148
    virtual Value::Type resultType() const { return Value::StringValue; }
149 150
};

darin's avatar
darin committed
151
class FunSubstring : public Function {
weinig's avatar
weinig committed
152
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
153
    virtual Value::Type resultType() const { return Value::StringValue; }
154 155
};

darin's avatar
darin committed
156
class FunStringLength : public Function {
weinig's avatar
weinig committed
157
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
158 159 160
    virtual Value::Type resultType() const { return Value::NumberValue; }
public:
    FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node. 
161 162
};

darin's avatar
darin committed
163
class FunNormalizeSpace : public Function {
weinig's avatar
weinig committed
164
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
165 166 167
    virtual Value::Type resultType() const { return Value::StringValue; }
public:
    FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node. 
168 169
};

darin's avatar
darin committed
170
class FunTranslate : public Function {
weinig's avatar
weinig committed
171
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
172
    virtual Value::Type resultType() const { return Value::StringValue; }
173 174
};

darin's avatar
darin committed
175
class FunBoolean : public Function {
weinig's avatar
weinig committed
176
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
177
    virtual Value::Type resultType() const { return Value::BooleanValue; }
178 179
};

darin's avatar
darin committed
180
class FunNot : public Function {
weinig's avatar
weinig committed
181
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
182
    virtual Value::Type resultType() const { return Value::BooleanValue; }
183 184
};

darin's avatar
darin committed
185
class FunTrue : public Function {
weinig's avatar
weinig committed
186
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
187
    virtual Value::Type resultType() const { return Value::BooleanValue; }
188 189
};

darin's avatar
darin committed
190
class FunFalse : public Function {
weinig's avatar
weinig committed
191
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
192
    virtual Value::Type resultType() const { return Value::BooleanValue; }
193 194
};

darin's avatar
darin committed
195
class FunLang : public Function {
weinig's avatar
weinig committed
196
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
197 198 199
    virtual Value::Type resultType() const { return Value::BooleanValue; }
public:
    FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node. 
200 201
};

darin's avatar
darin committed
202
class FunNumber : public Function {
weinig's avatar
weinig committed
203
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
204 205 206
    virtual Value::Type resultType() const { return Value::NumberValue; }
public:
    FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node. 
207 208
};

darin's avatar
darin committed
209
class FunSum : public Function {
weinig's avatar
weinig committed
210
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
211
    virtual Value::Type resultType() const { return Value::NumberValue; }
212 213
};

darin's avatar
darin committed
214
class FunFloor : public Function {
weinig's avatar
weinig committed
215
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
216
    virtual Value::Type resultType() const { return Value::NumberValue; }
217 218
};

darin's avatar
darin committed
219
class FunCeiling : public Function {
weinig's avatar
weinig committed
220
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
221
    virtual Value::Type resultType() const { return Value::NumberValue; }
222 223
};

darin's avatar
darin committed
224
class FunRound : public Function {
weinig's avatar
weinig committed
225
    virtual Value evaluate() const;
ap@webkit.org's avatar
ap@webkit.org committed
226
    virtual Value::Type resultType() const { return Value::NumberValue; }
ap's avatar
ap committed
227 228
public:
    static double round(double);
229 230 231 232 233
};

DEFINE_FUNCTION_CREATOR(FunLast)
DEFINE_FUNCTION_CREATOR(FunPosition)
DEFINE_FUNCTION_CREATOR(FunCount)
ap's avatar
ap committed
234
DEFINE_FUNCTION_CREATOR(FunId)
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
DEFINE_FUNCTION_CREATOR(FunLocalName)
DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
DEFINE_FUNCTION_CREATOR(FunName)

DEFINE_FUNCTION_CREATOR(FunString)
DEFINE_FUNCTION_CREATOR(FunConcat)
DEFINE_FUNCTION_CREATOR(FunStartsWith)
DEFINE_FUNCTION_CREATOR(FunContains)
DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
DEFINE_FUNCTION_CREATOR(FunSubstring)
DEFINE_FUNCTION_CREATOR(FunStringLength)
DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
DEFINE_FUNCTION_CREATOR(FunTranslate)

DEFINE_FUNCTION_CREATOR(FunBoolean)
DEFINE_FUNCTION_CREATOR(FunNot)
DEFINE_FUNCTION_CREATOR(FunTrue)
DEFINE_FUNCTION_CREATOR(FunFalse)
DEFINE_FUNCTION_CREATOR(FunLang)

DEFINE_FUNCTION_CREATOR(FunNumber)
DEFINE_FUNCTION_CREATOR(FunSum)
DEFINE_FUNCTION_CREATOR(FunFloor)
DEFINE_FUNCTION_CREATOR(FunCeiling)
DEFINE_FUNCTION_CREATOR(FunRound)

#undef DEFINE_FUNCTION_CREATOR

darin's avatar
darin committed
264 265
inline Interval::Interval()
    : m_min(Inf), m_max(Inf)
266 267 268
{
}

darin's avatar
darin committed
269 270
inline Interval::Interval(int value)
    : m_min(value), m_max(value)
271 272 273
{
}

darin's avatar
darin committed
274 275
inline Interval::Interval(int min, int max)
    : m_min(min), m_max(max)
276 277 278
{
}

darin's avatar
darin committed
279
inline bool Interval::contains(int value) const
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
{
    if (m_min == Inf && m_max == Inf)
        return true;

    if (m_min == Inf)
        return value <= m_max;

    if (m_max == Inf)
        return value >= m_min;

    return value >= m_min && value <= m_max;
}

void Function::setArguments(const Vector<Expression*>& args)
{
ap@webkit.org's avatar
ap@webkit.org committed
295 296 297 298 299
    ASSERT(!subExprCount());

    // Some functions use context node as implicit argument, so when explicit arguments are added, they may no longer be context node sensitive.
    if (m_name != "lang" && !args.isEmpty())
        setIsContextNodeSensitive(false);
300

ap@webkit.org's avatar
ap@webkit.org committed
301
    Vector<Expression*>::const_iterator end = args.end();
302
    for (Vector<Expression*>::const_iterator it = args.begin(); it != end; ++it)
303 304 305
        addSubExpression(*it);
}

weinig's avatar
weinig committed
306
Value FunLast::evaluate() const
307
{
darin's avatar
darin committed
308
    return Expression::evaluationContext().size;
309 310
}

weinig's avatar
weinig committed
311
Value FunPosition::evaluate() const
312
{
darin's avatar
darin committed
313
    return Expression::evaluationContext().position;
314 315
}

weinig's avatar
weinig committed
316
Value FunId::evaluate() const
ap's avatar
ap committed
317 318
{
    Value a = arg(0)->evaluate();
319
    StringBuilder idList; // A whitespace-separated list of IDs
ap's avatar
ap committed
320

ap's avatar
ap committed
321 322
    if (a.isNodeSet()) {
        const NodeSet& nodes = a.toNodeSet();
ap's avatar
ap committed
323
        for (size_t i = 0; i < nodes.size(); ++i) {
ap's avatar
ap committed
324
            String str = stringValue(nodes[i]);
325
            idList.append(str);
ap's avatar
ap committed
326 327 328 329
            idList.append(' ');
        }
    } else {
        String str = a.toString();
330
        idList.append(str);
ap's avatar
ap committed
331 332
    }
    
333
    TreeScope* contextScope = evaluationContext().node->treeScope();
ap's avatar
ap committed
334
    NodeSet result;
ap's avatar
ap committed
335 336
    HashSet<Node*> resultSet;

337 338
    unsigned startPos = 0;
    unsigned length = idList.length();
ap's avatar
ap committed
339 340 341 342
    while (true) {
        while (startPos < length && isWhitespace(idList[startPos]))
            ++startPos;
        
oliver's avatar
oliver committed
343 344 345
        if (startPos == length)
            break;

ap's avatar
ap committed
346 347 348 349 350 351
        size_t endPos = startPos;
        while (endPos < length && !isWhitespace(idList[endPos]))
            ++endPos;

        // If there are several nodes with the same id, id() should return the first one.
        // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
352
        Node* node = contextScope->getElementById(String(idList.characters() + startPos, endPos - startPos));
353
        if (node && resultSet.add(node).isNewEntry)
ap's avatar
ap committed
354 355 356 357 358
            result.append(node);
        
        startPos = endPos;
    }
    
ap's avatar
ap committed
359 360
    result.markSorted(false);
    
ap's avatar
ap committed
361
    return Value(result, Value::adopt);
ap's avatar
ap committed
362 363
}

ap@webkit.org's avatar
ap@webkit.org committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
static inline String expandedNameLocalPart(Node* node)
{
    // The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes.
    ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet.
    if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
        return static_cast<ProcessingInstruction*>(node)->target();
    return node->localName().string();
}

static inline String expandedName(Node* node)
{
    const AtomicString& prefix = node->prefix();
    return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node);
}

weinig's avatar
weinig committed
379
Value FunLocalName::evaluate() const
380 381 382
{
    if (argCount() > 0) {
        Value a = arg(0)->evaluate();
ap's avatar
ap committed
383 384 385
        if (!a.isNodeSet())
            return "";

ap@webkit.org's avatar
ap@webkit.org committed
386 387
        Node* node = a.toNodeSet().firstNode();
        return node ? expandedNameLocalPart(node) : "";
388 389
    }

ap@webkit.org's avatar
ap@webkit.org committed
390
    return expandedNameLocalPart(evaluationContext().node.get());
391 392
}

weinig's avatar
weinig committed
393
Value FunNamespaceURI::evaluate() const
394 395 396
{
    if (argCount() > 0) {
        Value a = arg(0)->evaluate();
ap's avatar
ap committed
397
        if (!a.isNodeSet())
398 399
            return "";

ap@webkit.org's avatar
ap@webkit.org committed
400 401
        Node* node = a.toNodeSet().firstNode();
        return node ? node->namespaceURI().string() : "";
402 403
    }

ap@webkit.org's avatar
ap@webkit.org committed
404
    return evaluationContext().node->namespaceURI().string();
405 406
}

weinig's avatar
weinig committed
407
Value FunName::evaluate() const
408 409 410
{
    if (argCount() > 0) {
        Value a = arg(0)->evaluate();
ap's avatar
ap committed
411
        if (!a.isNodeSet())
412 413
            return "";

ap@webkit.org's avatar
ap@webkit.org committed
414 415
        Node* node = a.toNodeSet().firstNode();
        return node ? expandedName(node) : "";
416 417
    }

ap@webkit.org's avatar
ap@webkit.org committed
418
    return expandedName(evaluationContext().node.get());
419 420
}

weinig's avatar
weinig committed
421
Value FunCount::evaluate() const
422 423 424
{
    Value a = arg(0)->evaluate();
    
425
    return double(a.toNodeSet().size());
426 427
}

weinig's avatar
weinig committed
428
Value FunString::evaluate() const
429
{
ap's avatar
ap committed
430
    if (!argCount())
ap's avatar
ap committed
431
        return Value(Expression::evaluationContext().node.get()).toString();
432 433 434
    return arg(0)->evaluate().toString();
}

weinig's avatar
weinig committed
435
Value FunConcat::evaluate() const
436
{
437 438
    StringBuilder result;
    result.reserveCapacity(1024);
439

ap's avatar
ap committed
440 441 442
    unsigned count = argCount();
    for (unsigned i = 0; i < count; ++i) {
        String str(arg(i)->evaluate().toString());
443
        result.append(str);
ap's avatar
ap committed
444
    }
445

446
    return result.toString();
447 448
}

weinig's avatar
weinig committed
449
Value FunStartsWith::evaluate() const
450 451 452 453 454 455 456 457 458 459
{
    String s1 = arg(0)->evaluate().toString();
    String s2 = arg(1)->evaluate().toString();

    if (s2.isEmpty())
        return true;

    return s1.startsWith(s2);
}

weinig's avatar
weinig committed
460
Value FunContains::evaluate() const
461 462 463 464 465 466 467 468 469 470
{
    String s1 = arg(0)->evaluate().toString();
    String s2 = arg(1)->evaluate().toString();

    if (s2.isEmpty()) 
        return true;

    return s1.contains(s2) != 0;
}

weinig's avatar
weinig committed
471
Value FunSubstringBefore::evaluate() const
472 473 474 475 476 477 478
{
    String s1 = arg(0)->evaluate().toString();
    String s2 = arg(1)->evaluate().toString();

    if (s2.isEmpty())
        return "";

479
    size_t i = s1.find(s2);
480

481
    if (i == notFound)
482 483 484 485 486
        return "";

    return s1.left(i);
}

weinig's avatar
weinig committed
487
Value FunSubstringAfter::evaluate() const
488 489 490 491
{
    String s1 = arg(0)->evaluate().toString();
    String s2 = arg(1)->evaluate().toString();

492 493
    size_t i = s1.find(s2);
    if (i == notFound)
494 495
        return "";

oliver's avatar
oliver committed
496
    return s1.substring(i + s2.length());
497 498
}

weinig's avatar
weinig committed
499
Value FunSubstring::evaluate() const
500 501
{
    String s = arg(0)->evaluate().toString();
502
    double doublePos = arg(1)->evaluate().toNumber();
503
    if (std::isnan(doublePos))
504 505
        return "";
    long pos = static_cast<long>(FunRound::round(doublePos));
506 507
    bool haveLength = argCount() == 3;
    long len = -1;
ap's avatar
ap committed
508 509
    if (haveLength) {
        double doubleLen = arg(2)->evaluate().toNumber();
510
        if (std::isnan(doubleLen))
ap's avatar
ap committed
511 512 513
            return "";
        len = static_cast<long>(FunRound::round(doubleLen));
    }
514 515 516 517

    if (pos > long(s.length())) 
        return "";

518 519 520 521 522 523
    if (pos < 1) {
        if (haveLength) {
            len -= 1 - pos;
            if (len < 1)
                return "";
        }
524 525 526
        pos = 1;
    }

darin's avatar
darin committed
527
    return s.substring(pos - 1, len);
528 529
}

weinig's avatar
weinig committed
530
Value FunStringLength::evaluate() const
531
{
ap's avatar
ap committed
532
    if (!argCount())
ap's avatar
ap committed
533
        return Value(Expression::evaluationContext().node.get()).toString().length();
darin's avatar
darin committed
534
    return arg(0)->evaluate().toString().length();
535 536
}

weinig's avatar
weinig committed
537
Value FunNormalizeSpace::evaluate() const
538
{
ap's avatar
ap committed
539
    if (!argCount()) {
ap's avatar
ap committed
540
        String s = Value(Expression::evaluationContext().node.get()).toString();
ap's avatar
ap committed
541
        return s.simplifyWhiteSpace();
542 543 544
    }

    String s = arg(0)->evaluate().toString();
ap's avatar
ap committed
545
    return s.simplifyWhiteSpace();
546 547
}

weinig's avatar
weinig committed
548
Value FunTranslate::evaluate() const
549 550 551 552
{
    String s1 = arg(0)->evaluate().toString();
    String s2 = arg(1)->evaluate().toString();
    String s3 = arg(2)->evaluate().toString();
553
    StringBuilder result;
554 555

    for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
darin's avatar
darin committed
556
        UChar ch = s1[i1];
557
        size_t i2 = s2.find(ch);
558
        
559
        if (i2 == notFound)
560 561 562
            result.append(ch);
        else if (i2 < s3.length())
            result.append(s3[i2]);
563 564
    }

565
    return result.toString();
566 567
}

weinig's avatar
weinig committed
568
Value FunBoolean::evaluate() const
569 570 571 572
{
    return arg(0)->evaluate().toBoolean();
}

weinig's avatar
weinig committed
573
Value FunNot::evaluate() const
574 575 576 577
{
    return !arg(0)->evaluate().toBoolean();
}

weinig's avatar
weinig committed
578
Value FunTrue::evaluate() const
579 580 581 582
{
    return true;
}

weinig's avatar
weinig committed
583
Value FunLang::evaluate() const
584 585 586
{
    String lang = arg(0)->evaluate().toString();

587
    const Attribute* languageAttribute = 0;
588 589
    Node* node = evaluationContext().node.get();
    while (node) {
590
        if (node->isElementNode()) {
591 592 593
            Element* element = toElement(node);
            if (element->hasAttributes())
                languageAttribute = element->getAttributeItem(XMLNames::langAttr);
594
        }
595
        if (languageAttribute)
596 597 598 599
            break;
        node = node->parentNode();
    }

600
    if (!languageAttribute)
601 602
        return false;

603
    String langValue = languageAttribute->value();
ap's avatar
ap committed
604
    while (true) {
605
        if (equalIgnoringCase(langValue, lang))
ap's avatar
ap committed
606
            return true;
607

ap's avatar
ap committed
608
        // Remove suffixes one by one.
609 610
        size_t index = langValue.reverseFind('-');
        if (index == notFound)
ap's avatar
ap committed
611
            break;
612
        langValue = langValue.left(index);
ap's avatar
ap committed
613
    }
darin's avatar
darin committed
614

ap's avatar
ap committed
615
    return false;
616 617
}

weinig's avatar
weinig committed
618
Value FunFalse::evaluate() const
619 620 621 622
{
    return false;
}

weinig's avatar
weinig committed
623
Value FunNumber::evaluate() const
624
{
ap's avatar
ap committed
625 626
    if (!argCount())
        return Value(Expression::evaluationContext().node.get()).toNumber();
627 628 629
    return arg(0)->evaluate().toNumber();
}

weinig's avatar
weinig committed
630
Value FunSum::evaluate() const
631 632
{
    Value a = arg(0)->evaluate();
ap's avatar
ap committed
633
    if (!a.isNodeSet())
634 635 636
        return 0.0;

    double sum = 0.0;
ap's avatar
ap committed
637 638 639 640
    const NodeSet& nodes = a.toNodeSet();
    // To be really compliant, we should sort the node-set, as floating point addition is not associative.
    // However, this is unlikely to ever become a practical issue, and sorting is slow.

641
    for (unsigned i = 0; i < nodes.size(); i++)
ap's avatar
ap committed
642
        sum += Value(stringValue(nodes[i])).toNumber();
643 644 645 646
    
    return sum;
}

weinig's avatar
weinig committed
647
Value FunFloor::evaluate() const
648
{
darin's avatar
darin committed
649
    return floor(arg(0)->evaluate().toNumber());
650 651
}

weinig's avatar
weinig committed
652
Value FunCeiling::evaluate() const
653
{
darin's avatar
darin committed
654
    return ceil(arg(0)->evaluate().toNumber());
655 656
}

ap's avatar
ap committed
657 658
double FunRound::round(double val)
{
659
    if (!std::isnan(val) && !std::isinf(val)) {
660
        if (std::signbit(val) && val >= -0.5)
ap's avatar
ap committed
661 662 663 664 665 666 667
            val *= 0; // negative zero
        else
            val = floor(val + 0.5);
    }
    return val;
}

weinig's avatar
weinig committed
668
Value FunRound::evaluate() const
669 670 671 672
{
    return round(arg(0)->evaluate().toNumber());
}

673 674 675 676 677
struct FunctionMapping {
    const char* name;
    FunctionRec function;
};

darin's avatar
darin committed
678
static void createFunctionMap()
679 680
{
    static const FunctionMapping functions[] = {
darin's avatar
darin committed
681 682 683 684 685 686 687
        { "boolean", { &createFunBoolean, 1 } },
        { "ceiling", { &createFunCeiling, 1 } },
        { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
        { "contains", { &createFunContains, 2 } },
        { "count", { &createFunCount, 1 } },
        { "false", { &createFunFalse, 0 } },
        { "floor", { &createFunFloor, 1 } },
ap's avatar
ap committed
688
        { "id", { &createFunId, 1 } },
darin's avatar
darin committed
689 690 691 692 693
        { "lang", { &createFunLang, 1 } },
        { "last", { &createFunLast, 0 } },
        { "local-name", { &createFunLocalName, Interval(0, 1) } },
        { "name", { &createFunName, Interval(0, 1) } },
        { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
ap's avatar
ap committed
694
        { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
darin's avatar
darin committed
695
        { "not", { &createFunNot, 1 } },
ap's avatar
ap committed
696
        { "number", { &createFunNumber, Interval(0, 1) } },
darin's avatar
darin committed
697 698 699 700
        { "position", { &createFunPosition, 0 } },
        { "round", { &createFunRound, 1 } },
        { "starts-with", { &createFunStartsWith, 2 } },
        { "string", { &createFunString, Interval(0, 1) } },
ap's avatar
ap committed
701
        { "string-length", { &createFunStringLength, Interval(0, 1) } },
darin's avatar
darin committed
702 703 704 705 706 707
        { "substring", { &createFunSubstring, Interval(2, 3) } },
        { "substring-after", { &createFunSubstringAfter, 2 } },
        { "substring-before", { &createFunSubstringBefore, 2 } },
        { "sum", { &createFunSum, 1 } },
        { "translate", { &createFunTranslate, 3 } },
        { "true", { &createFunTrue, 0 } },
708
    };
darin's avatar
darin committed
709 710

    functionMap = new HashMap<String, FunctionRec>;
711
    for (size_t i = 0; i < WTF_ARRAY_LENGTH(functions); ++i)
darin's avatar
darin committed
712
        functionMap->set(functions[i].name, functions[i].function);
713 714
}

darin's avatar
darin committed
715
Function* createFunction(const String& name, const Vector<Expression*>& args)
716
{
darin's avatar
darin committed
717 718 719
    if (!functionMap)
        createFunctionMap();

ap's avatar
ap committed
720 721 722
    HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name);
    FunctionRec* functionRec = 0;

723
    if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->value)->args.contains(args.size()))
ap's avatar
ap committed
724
        return 0;
725

ap's avatar
ap committed
726
    Function* function = functionRec->factoryFn();
727 728 729 730 731 732 733
    function->setArguments(args);
    function->setName(name);
    return function;
}

}
}