2
* Copyright (C) 2005 Frerich Raabe <raabe@kde.org>
3
* Copyright (C) 2006, 2009 Apple Inc.
4
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
#include "XPathFunctions.h"
32
#include "ProcessingInstruction.h"
33
#include "TreeScope.h"
35
#include "XPathUtil.h"
36
#include "XPathValue.h"
37
#include <wtf/MathExtras.h>
38
#include <wtf/text/StringBuilder.h>
43
static inline bool isWhitespace(UChar c)
45
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
49
#define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
53
static const int Inf = -1;
57
Interval(int min, int max);
59
bool contains(int value) const;
67
typedef Function *(*FactoryFn)();
72
static HashMap<String, FunctionRec>* functionMap;
74
class FunLast : public Function {
75
virtual Value evaluate() const;
76
virtual Value::Type resultType() const { return Value::NumberValue; }
78
FunLast() { setIsContextSizeSensitive(true); }
81
class FunPosition : public Function {
82
virtual Value evaluate() const;
83
virtual Value::Type resultType() const { return Value::NumberValue; }
85
FunPosition() { setIsContextPositionSensitive(true); }
88
class FunCount : public Function {
89
virtual Value evaluate() const;
90
virtual Value::Type resultType() const { return Value::NumberValue; }
93
class FunId : public Function {
94
virtual Value evaluate() const;
95
virtual Value::Type resultType() const { return Value::NodeSetValue; }
98
class FunLocalName : public Function {
99
virtual Value evaluate() const;
100
virtual Value::Type resultType() const { return Value::StringValue; }
102
FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node.
105
class FunNamespaceURI : public Function {
106
virtual Value evaluate() const;
107
virtual Value::Type resultType() const { return Value::StringValue; }
109
FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node.
112
class FunName : public Function {
113
virtual Value evaluate() const;
114
virtual Value::Type resultType() const { return Value::StringValue; }
116
FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node.
119
class FunString : public Function {
120
virtual Value evaluate() const;
121
virtual Value::Type resultType() const { return Value::StringValue; }
123
FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node.
126
class FunConcat : public Function {
127
virtual Value evaluate() const;
128
virtual Value::Type resultType() const { return Value::StringValue; }
131
class FunStartsWith : public Function {
132
virtual Value evaluate() const;
133
virtual Value::Type resultType() const { return Value::BooleanValue; }
136
class FunContains : public Function {
137
virtual Value evaluate() const;
138
virtual Value::Type resultType() const { return Value::BooleanValue; }
141
class FunSubstringBefore : public Function {
142
virtual Value evaluate() const;
143
virtual Value::Type resultType() const { return Value::StringValue; }
146
class FunSubstringAfter : public Function {
147
virtual Value evaluate() const;
148
virtual Value::Type resultType() const { return Value::StringValue; }
151
class FunSubstring : public Function {
152
virtual Value evaluate() const;
153
virtual Value::Type resultType() const { return Value::StringValue; }
156
class FunStringLength : public Function {
157
virtual Value evaluate() const;
158
virtual Value::Type resultType() const { return Value::NumberValue; }
160
FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node.
163
class FunNormalizeSpace : public Function {
164
virtual Value evaluate() const;
165
virtual Value::Type resultType() const { return Value::StringValue; }
167
FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node.
170
class FunTranslate : public Function {
171
virtual Value evaluate() const;
172
virtual Value::Type resultType() const { return Value::StringValue; }
175
class FunBoolean : public Function {
176
virtual Value evaluate() const;
177
virtual Value::Type resultType() const { return Value::BooleanValue; }
180
class FunNot : public Function {
181
virtual Value evaluate() const;
182
virtual Value::Type resultType() const { return Value::BooleanValue; }
185
class FunTrue : public Function {
186
virtual Value evaluate() const;
187
virtual Value::Type resultType() const { return Value::BooleanValue; }
190
class FunFalse : public Function {
191
virtual Value evaluate() const;
192
virtual Value::Type resultType() const { return Value::BooleanValue; }
195
class FunLang : public Function {
196
virtual Value evaluate() const;
197
virtual Value::Type resultType() const { return Value::BooleanValue; }
199
FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node.
202
class FunNumber : public Function {
203
virtual Value evaluate() const;
204
virtual Value::Type resultType() const { return Value::NumberValue; }
206
FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node.
209
class FunSum : public Function {
210
virtual Value evaluate() const;
211
virtual Value::Type resultType() const { return Value::NumberValue; }
214
class FunFloor : public Function {
215
virtual Value evaluate() const;
216
virtual Value::Type resultType() const { return Value::NumberValue; }
219
class FunCeiling : public Function {
220
virtual Value evaluate() const;
221
virtual Value::Type resultType() const { return Value::NumberValue; }
224
class FunRound : public Function {
225
virtual Value evaluate() const;
226
virtual Value::Type resultType() const { return Value::NumberValue; }
228
static double round(double);
231
DEFINE_FUNCTION_CREATOR(FunLast)
232
DEFINE_FUNCTION_CREATOR(FunPosition)
233
DEFINE_FUNCTION_CREATOR(FunCount)
234
DEFINE_FUNCTION_CREATOR(FunId)
235
DEFINE_FUNCTION_CREATOR(FunLocalName)
236
DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
237
DEFINE_FUNCTION_CREATOR(FunName)
239
DEFINE_FUNCTION_CREATOR(FunString)
240
DEFINE_FUNCTION_CREATOR(FunConcat)
241
DEFINE_FUNCTION_CREATOR(FunStartsWith)
242
DEFINE_FUNCTION_CREATOR(FunContains)
243
DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
244
DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
245
DEFINE_FUNCTION_CREATOR(FunSubstring)
246
DEFINE_FUNCTION_CREATOR(FunStringLength)
247
DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
248
DEFINE_FUNCTION_CREATOR(FunTranslate)
250
DEFINE_FUNCTION_CREATOR(FunBoolean)
251
DEFINE_FUNCTION_CREATOR(FunNot)
252
DEFINE_FUNCTION_CREATOR(FunTrue)
253
DEFINE_FUNCTION_CREATOR(FunFalse)
254
DEFINE_FUNCTION_CREATOR(FunLang)
256
DEFINE_FUNCTION_CREATOR(FunNumber)
257
DEFINE_FUNCTION_CREATOR(FunSum)
258
DEFINE_FUNCTION_CREATOR(FunFloor)
259
DEFINE_FUNCTION_CREATOR(FunCeiling)
260
DEFINE_FUNCTION_CREATOR(FunRound)
262
#undef DEFINE_FUNCTION_CREATOR
264
inline Interval::Interval()
265
: m_min(Inf), m_max(Inf)
269
inline Interval::Interval(int value)
270
: m_min(value), m_max(value)
274
inline Interval::Interval(int min, int max)
275
: m_min(min), m_max(max)
279
inline bool Interval::contains(int value) const
281
if (m_min == Inf && m_max == Inf)
285
return value <= m_max;
288
return value >= m_min;
290
return value >= m_min && value <= m_max;
293
void Function::setArguments(const Vector<Expression*>& args)
295
ASSERT(!subExprCount());
297
// Some functions use context node as implicit argument, so when explicit arguments are added, they may no longer be context node sensitive.
298
if (m_name != "lang" && !args.isEmpty())
299
setIsContextNodeSensitive(false);
301
Vector<Expression*>::const_iterator end = args.end();
302
for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
303
addSubExpression(*it);
306
Value FunLast::evaluate() const
308
return Expression::evaluationContext().size;
311
Value FunPosition::evaluate() const
313
return Expression::evaluationContext().position;
316
Value FunId::evaluate() const
318
Value a = arg(0)->evaluate();
319
StringBuilder idList; // A whitespace-separated list of IDs
322
const NodeSet& nodes = a.toNodeSet();
323
for (size_t i = 0; i < nodes.size(); ++i) {
324
String str = stringValue(nodes[i]);
329
String str = a.toString();
333
TreeScope* contextScope = evaluationContext().node->treeScope();
335
HashSet<Node*> resultSet;
337
unsigned startPos = 0;
338
unsigned length = idList.length();
340
while (startPos < length && isWhitespace(idList[startPos]))
343
if (startPos == length)
346
size_t endPos = startPos;
347
while (endPos < length && !isWhitespace(idList[endPos]))
350
// If there are several nodes with the same id, id() should return the first one.
351
// 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)
359
result.markSorted(false);
361
return Value(result, Value::adopt);
364
static inline String expandedNameLocalPart(Node* node)
366
// The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes.
367
ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet.
368
if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
369
return static_cast<ProcessingInstruction*>(node)->target();
370
return node->localName().string();
373
static inline String expandedName(Node* node)
375
const AtomicString& prefix = node->prefix();
376
return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node);
379
Value FunLocalName::evaluate() const
381
if (argCount() > 0) {
382
Value a = arg(0)->evaluate();
386
Node* node = a.toNodeSet().firstNode();
387
return node ? expandedNameLocalPart(node) : "";
390
return expandedNameLocalPart(evaluationContext().node.get());
393
Value FunNamespaceURI::evaluate() const
395
if (argCount() > 0) {
396
Value a = arg(0)->evaluate();
400
Node* node = a.toNodeSet().firstNode();
401
return node ? node->namespaceURI().string() : "";
404
return evaluationContext().node->namespaceURI().string();
407
Value FunName::evaluate() const
409
if (argCount() > 0) {
410
Value a = arg(0)->evaluate();
414
Node* node = a.toNodeSet().firstNode();
415
return node ? expandedName(node) : "";
418
return expandedName(evaluationContext().node.get());
421
Value FunCount::evaluate() const
423
Value a = arg(0)->evaluate();
425
return double(a.toNodeSet().size());
428
Value FunString::evaluate() const
431
return Value(Expression::evaluationContext().node.get()).toString();
432
return arg(0)->evaluate().toString();
435
Value FunConcat::evaluate() const
437
StringBuilder result;
438
result.reserveCapacity(1024);
440
unsigned count = argCount();
441
for (unsigned i = 0; i < count; ++i) {
442
String str(arg(i)->evaluate().toString());
446
return result.toString();
449
Value FunStartsWith::evaluate() const
451
String s1 = arg(0)->evaluate().toString();
452
String s2 = arg(1)->evaluate().toString();
457
return s1.startsWith(s2);
460
Value FunContains::evaluate() const
462
String s1 = arg(0)->evaluate().toString();
463
String s2 = arg(1)->evaluate().toString();
468
return s1.contains(s2) != 0;
471
Value FunSubstringBefore::evaluate() const
473
String s1 = arg(0)->evaluate().toString();
474
String s2 = arg(1)->evaluate().toString();
479
size_t i = s1.find(s2);
487
Value FunSubstringAfter::evaluate() const
489
String s1 = arg(0)->evaluate().toString();
490
String s2 = arg(1)->evaluate().toString();
492
size_t i = s1.find(s2);
496
return s1.substring(i + s2.length());
499
Value FunSubstring::evaluate() const
501
String s = arg(0)->evaluate().toString();
502
double doublePos = arg(1)->evaluate().toNumber();
503
if (isnan(doublePos))
505
long pos = static_cast<long>(FunRound::round(doublePos));
506
bool haveLength = argCount() == 3;
509
double doubleLen = arg(2)->evaluate().toNumber();
510
if (isnan(doubleLen))
512
len = static_cast<long>(FunRound::round(doubleLen));
515
if (pos > long(s.length()))
527
return s.substring(pos - 1, len);
530
Value FunStringLength::evaluate() const
533
return Value(Expression::evaluationContext().node.get()).toString().length();
534
return arg(0)->evaluate().toString().length();
537
Value FunNormalizeSpace::evaluate() const
540
String s = Value(Expression::evaluationContext().node.get()).toString();
541
return s.simplifyWhiteSpace();
544
String s = arg(0)->evaluate().toString();
545
return s.simplifyWhiteSpace();
548
Value FunTranslate::evaluate() const
550
String s1 = arg(0)->evaluate().toString();
551
String s2 = arg(1)->evaluate().toString();
552
String s3 = arg(2)->evaluate().toString();
553
StringBuilder result;
555
for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
557
size_t i2 = s2.find(ch);
561
else if (i2 < s3.length())
562
result.append(s3[i2]);
565
return result.toString();
568
Value FunBoolean::evaluate() const
570
return arg(0)->evaluate().toBoolean();
573
Value FunNot::evaluate() const
575
return !arg(0)->evaluate().toBoolean();
578
Value FunTrue::evaluate() const
583
Value FunLang::evaluate() const
585
String lang = arg(0)->evaluate().toString();
587
const Attribute* languageAttribute = 0;
588
Node* node = evaluationContext().node.get();
590
if (node->isElementNode()) {
591
Element* element = toElement(node);
592
if (element->hasAttributes())
593
languageAttribute = element->getAttributeItem(XMLNames::langAttr);
595
if (languageAttribute)
597
node = node->parentNode();
600
if (!languageAttribute)
603
String langValue = languageAttribute->value();
605
if (equalIgnoringCase(langValue, lang))
608
// Remove suffixes one by one.
609
size_t index = langValue.reverseFind('-');
610
if (index == notFound)
612
langValue = langValue.left(index);
618
Value FunFalse::evaluate() const
623
Value FunNumber::evaluate() const
626
return Value(Expression::evaluationContext().node.get()).toNumber();
627
return arg(0)->evaluate().toNumber();
630
Value FunSum::evaluate() const
632
Value a = arg(0)->evaluate();
637
const NodeSet& nodes = a.toNodeSet();
638
// To be really compliant, we should sort the node-set, as floating point addition is not associative.
639
// However, this is unlikely to ever become a practical issue, and sorting is slow.
641
for (unsigned i = 0; i < nodes.size(); i++)
642
sum += Value(stringValue(nodes[i])).toNumber();
647
Value FunFloor::evaluate() const
649
return floor(arg(0)->evaluate().toNumber());
652
Value FunCeiling::evaluate() const
654
return ceil(arg(0)->evaluate().toNumber());
657
double FunRound::round(double val)
659
if (!isnan(val) && !isinf(val)) {
660
if (signbit(val) && val >= -0.5)
661
val *= 0; // negative zero
663
val = floor(val + 0.5);
668
Value FunRound::evaluate() const
670
return round(arg(0)->evaluate().toNumber());
673
struct FunctionMapping {
675
FunctionRec function;
678
static void createFunctionMap()
680
static const FunctionMapping functions[] = {
681
{ "boolean", { &createFunBoolean, 1 } },
682
{ "ceiling", { &createFunCeiling, 1 } },
683
{ "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
684
{ "contains", { &createFunContains, 2 } },
685
{ "count", { &createFunCount, 1 } },
686
{ "false", { &createFunFalse, 0 } },
687
{ "floor", { &createFunFloor, 1 } },
688
{ "id", { &createFunId, 1 } },
689
{ "lang", { &createFunLang, 1 } },
690
{ "last", { &createFunLast, 0 } },
691
{ "local-name", { &createFunLocalName, Interval(0, 1) } },
692
{ "name", { &createFunName, Interval(0, 1) } },
693
{ "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
694
{ "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
695
{ "not", { &createFunNot, 1 } },
696
{ "number", { &createFunNumber, Interval(0, 1) } },
697
{ "position", { &createFunPosition, 0 } },
698
{ "round", { &createFunRound, 1 } },
699
{ "starts-with", { &createFunStartsWith, 2 } },
700
{ "string", { &createFunString, Interval(0, 1) } },
701
{ "string-length", { &createFunStringLength, Interval(0, 1) } },
702
{ "substring", { &createFunSubstring, Interval(2, 3) } },
703
{ "substring-after", { &createFunSubstringAfter, 2 } },
704
{ "substring-before", { &createFunSubstringBefore, 2 } },
705
{ "sum", { &createFunSum, 1 } },
706
{ "translate", { &createFunTranslate, 3 } },
707
{ "true", { &createFunTrue, 0 } },
710
functionMap = new HashMap<String, FunctionRec>;
711
for (size_t i = 0; i < WTF_ARRAY_LENGTH(functions); ++i)
712
functionMap->set(functions[i].name, functions[i].function);
715
Function* createFunction(const String& name, const Vector<Expression*>& args)
720
HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name);
721
FunctionRec* functionRec = 0;
723
if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->value)->args.contains(args.size()))
726
Function* function = functionRec->factoryFn();
727
function->setArguments(args);
728
function->setName(name);