2 * Copyright 2005 Frerich Raabe <raabe@kde.org>
3 * Copyright (C) 2006 Apple Computer, 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"
35 #include "NamedAttrMap.h"
37 #include "XPathUtil.h"
38 #include "XPathValue.h"
39 #include <wtf/MathExtras.h>
44 static inline bool isWhitespace(UChar c)
46 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
50 #define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
54 static const int Inf = -1;
58 Interval(int min, int max);
60 bool contains(int value) const;
68 typedef Function *(*FactoryFn)();
73 static HashMap<String, FunctionRec>* functionMap;
75 class FunLast : public Function {
76 virtual Value evaluate() const;
79 class FunPosition : public Function {
80 virtual Value evaluate() const;
83 class FunCount : public Function {
84 virtual Value evaluate() const;
87 class FunId : public Function {
88 virtual Value evaluate() const;
91 class FunLocalName : public Function {
92 virtual Value evaluate() const;
95 class FunNamespaceURI : public Function {
96 virtual Value evaluate() const;
99 class FunName : public Function {
100 virtual Value evaluate() const;
103 class FunString : public Function {
104 virtual Value evaluate() const;
107 class FunConcat : public Function {
108 virtual Value evaluate() const;
111 class FunStartsWith : public Function {
112 virtual Value evaluate() const;
115 class FunContains : public Function {
116 virtual Value evaluate() const;
119 class FunSubstringBefore : public Function {
120 virtual Value evaluate() const;
123 class FunSubstringAfter : public Function {
124 virtual Value evaluate() const;
127 class FunSubstring : public Function {
128 virtual Value evaluate() const;
131 class FunStringLength : public Function {
132 virtual Value evaluate() const;
135 class FunNormalizeSpace : public Function {
136 virtual Value evaluate() const;
139 class FunTranslate : public Function {
140 virtual Value evaluate() const;
143 class FunBoolean : public Function {
144 virtual Value evaluate() const;
147 class FunNot : public Function {
148 virtual Value evaluate() const;
151 class FunTrue : public Function {
152 virtual Value evaluate() const;
155 class FunFalse : public Function {
156 virtual Value evaluate() const;
159 class FunLang : public Function {
160 virtual Value evaluate() const;
163 class FunNumber : public Function {
164 virtual Value evaluate() const;
167 class FunSum : public Function {
168 virtual Value evaluate() const;
171 class FunFloor : public Function {
172 virtual Value evaluate() const;
175 class FunCeiling : public Function {
176 virtual Value evaluate() const;
179 class FunRound : public Function {
180 virtual Value evaluate() const;
182 static double round(double);
185 DEFINE_FUNCTION_CREATOR(FunLast)
186 DEFINE_FUNCTION_CREATOR(FunPosition)
187 DEFINE_FUNCTION_CREATOR(FunCount)
188 DEFINE_FUNCTION_CREATOR(FunId)
189 DEFINE_FUNCTION_CREATOR(FunLocalName)
190 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
191 DEFINE_FUNCTION_CREATOR(FunName)
193 DEFINE_FUNCTION_CREATOR(FunString)
194 DEFINE_FUNCTION_CREATOR(FunConcat)
195 DEFINE_FUNCTION_CREATOR(FunStartsWith)
196 DEFINE_FUNCTION_CREATOR(FunContains)
197 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
198 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
199 DEFINE_FUNCTION_CREATOR(FunSubstring)
200 DEFINE_FUNCTION_CREATOR(FunStringLength)
201 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
202 DEFINE_FUNCTION_CREATOR(FunTranslate)
204 DEFINE_FUNCTION_CREATOR(FunBoolean)
205 DEFINE_FUNCTION_CREATOR(FunNot)
206 DEFINE_FUNCTION_CREATOR(FunTrue)
207 DEFINE_FUNCTION_CREATOR(FunFalse)
208 DEFINE_FUNCTION_CREATOR(FunLang)
210 DEFINE_FUNCTION_CREATOR(FunNumber)
211 DEFINE_FUNCTION_CREATOR(FunSum)
212 DEFINE_FUNCTION_CREATOR(FunFloor)
213 DEFINE_FUNCTION_CREATOR(FunCeiling)
214 DEFINE_FUNCTION_CREATOR(FunRound)
216 #undef DEFINE_FUNCTION_CREATOR
218 inline Interval::Interval()
219 : m_min(Inf), m_max(Inf)
223 inline Interval::Interval(int value)
224 : m_min(value), m_max(value)
228 inline Interval::Interval(int min, int max)
229 : m_min(min), m_max(max)
233 inline bool Interval::contains(int value) const
235 if (m_min == Inf && m_max == Inf)
239 return value <= m_max;
242 return value >= m_min;
244 return value >= m_min && value <= m_max;
247 void Function::setArguments(const Vector<Expression*>& args)
249 Vector<Expression*>::const_iterator end = args.end();
251 for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
252 addSubExpression(*it);
255 Value FunLast::evaluate() const
257 return Expression::evaluationContext().size;
260 Value FunPosition::evaluate() const
262 return Expression::evaluationContext().position;
265 Value FunId::evaluate() const
267 Value a = arg(0)->evaluate();
268 Vector<UChar> idList; // A whitespace-separated list of IDs
271 const NodeSet& nodes = a.toNodeSet();
272 for (size_t i = 0; i < nodes.size(); ++i) {
273 String str = stringValue(nodes[i]);
274 idList.append(str.characters(), str.length());
278 String str = a.toString();
279 idList.append(str.characters(), str.length());
282 Document* contextDocument = evaluationContext().node->document();
284 HashSet<Node*> resultSet;
287 size_t length = idList.size();
289 while (startPos < length && isWhitespace(idList[startPos]))
292 if (startPos == length)
295 size_t endPos = startPos;
296 while (endPos < length && !isWhitespace(idList[endPos]))
299 // If there are several nodes with the same id, id() should return the first one.
300 // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
301 Node* node = contextDocument->getElementById(String(&idList[startPos], endPos - startPos));
302 if (node && resultSet.add(node).second)
308 result.markSorted(false);
310 return Value(result, Value::adopt);
313 Value FunLocalName::evaluate() const
316 if (argCount() > 0) {
317 Value a = arg(0)->evaluate();
321 node = a.toNodeSet().firstNode();
327 node = evaluationContext().node.get();
329 return node->localName().domString();
332 Value FunNamespaceURI::evaluate() const
335 if (argCount() > 0) {
336 Value a = arg(0)->evaluate();
340 node = a.toNodeSet().firstNode();
346 node = evaluationContext().node.get();
348 return node->namespaceURI().domString();
351 Value FunName::evaluate() const
354 if (argCount() > 0) {
355 Value a = arg(0)->evaluate();
359 node = a.toNodeSet().firstNode();
365 node = evaluationContext().node.get();
367 const AtomicString& prefix = node->prefix();
368 return prefix.isEmpty() ? node->localName().domString() : prefix + ":" + node->localName();
371 Value FunCount::evaluate() const
373 Value a = arg(0)->evaluate();
378 return a.toNodeSet().size();
381 Value FunString::evaluate() const
384 return Value(Expression::evaluationContext().node.get()).toString();
385 return arg(0)->evaluate().toString();
388 Value FunConcat::evaluate() const
390 Vector<UChar, 1024> result;
392 unsigned count = argCount();
393 for (unsigned i = 0; i < count; ++i) {
394 String str(arg(i)->evaluate().toString());
395 result.append(str.characters(), str.length());
398 return String(result.data(), result.size());
401 Value FunStartsWith::evaluate() const
403 String s1 = arg(0)->evaluate().toString();
404 String s2 = arg(1)->evaluate().toString();
409 return s1.startsWith(s2);
412 Value FunContains::evaluate() const
414 String s1 = arg(0)->evaluate().toString();
415 String s2 = arg(1)->evaluate().toString();
420 return s1.contains(s2) != 0;
423 Value FunSubstringBefore::evaluate() const
425 String s1 = arg(0)->evaluate().toString();
426 String s2 = arg(1)->evaluate().toString();
439 Value FunSubstringAfter::evaluate() const
441 String s1 = arg(0)->evaluate().toString();
442 String s2 = arg(1)->evaluate().toString();
448 return s1.substring(i + s2.length());
451 Value FunSubstring::evaluate() const
453 String s = arg(0)->evaluate().toString();
454 long pos = static_cast<long>(FunRound::round(arg(1)->evaluate().toNumber()));
455 bool haveLength = argCount() == 3;
458 double doubleLen = arg(2)->evaluate().toNumber();
459 if (isnan(doubleLen))
461 len = static_cast<long>(FunRound::round(doubleLen));
464 if (pos > long(s.length()))
467 if (haveLength && pos < 1) {
474 return s.substring(pos - 1, len);
477 Value FunStringLength::evaluate() const
480 return Value(Expression::evaluationContext().node.get()).toString().length();
481 return arg(0)->evaluate().toString().length();
484 Value FunNormalizeSpace::evaluate() const
487 String s = Value(Expression::evaluationContext().node.get()).toString();
488 return s.simplifyWhiteSpace();
491 String s = arg(0)->evaluate().toString();
492 return s.simplifyWhiteSpace();
495 Value FunTranslate::evaluate() const
497 String s1 = arg(0)->evaluate().toString();
498 String s2 = arg(1)->evaluate().toString();
499 String s3 = arg(2)->evaluate().toString();
502 // FIXME: Building a String a character at a time is quite slow.
503 for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
505 int i2 = s2.find(ch);
508 newString += String(&ch, 1);
509 else if ((unsigned)i2 < s3.length()) {
511 newString += String(&c2, 1);
518 Value FunBoolean::evaluate() const
520 return arg(0)->evaluate().toBoolean();
523 Value FunNot::evaluate() const
525 return !arg(0)->evaluate().toBoolean();
528 Value FunTrue::evaluate() const
533 Value FunLang::evaluate() const
535 String lang = arg(0)->evaluate().toString();
537 RefPtr<Node> langNode = 0;
538 Node* node = evaluationContext().node.get();
540 NamedAttrMap* attrs = node->attributes();
542 langNode = attrs->getNamedItemNS(XMLNames::xmlNamespaceURI, "lang");
545 node = node->parentNode();
551 String langNodeValue = langNode->nodeValue();
553 if (equalIgnoringCase(langNodeValue, lang))
556 // Remove suffixes one by one.
557 int index = langNodeValue.reverseFind('-');
560 langNodeValue = langNodeValue.left(index);
566 Value FunFalse::evaluate() const
571 Value FunNumber::evaluate() const
574 return Value(Expression::evaluationContext().node.get()).toNumber();
575 return arg(0)->evaluate().toNumber();
578 Value FunSum::evaluate() const
580 Value a = arg(0)->evaluate();
585 const NodeSet& nodes = a.toNodeSet();
586 // To be really compliant, we should sort the node-set, as floating point addition is not associative.
587 // However, this is unlikely to ever become a practical issue, and sorting is slow.
589 for (unsigned i = 0; i < nodes.size(); i++)
590 sum += Value(stringValue(nodes[i])).toNumber();
595 Value FunFloor::evaluate() const
597 return floor(arg(0)->evaluate().toNumber());
600 Value FunCeiling::evaluate() const
602 return ceil(arg(0)->evaluate().toNumber());
605 double FunRound::round(double val)
607 if (!isnan(val) && !isinf(val)) {
608 if (signbit(val) && val >= -0.5)
609 val *= 0; // negative zero
611 val = floor(val + 0.5);
616 Value FunRound::evaluate() const
618 return round(arg(0)->evaluate().toNumber());
621 static void createFunctionMap()
623 struct FunctionMapping {
625 FunctionRec function;
627 static const FunctionMapping functions[] = {
628 { "boolean", { &createFunBoolean, 1 } },
629 { "ceiling", { &createFunCeiling, 1 } },
630 { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
631 { "contains", { &createFunContains, 2 } },
632 { "count", { &createFunCount, 1 } },
633 { "false", { &createFunFalse, 0 } },
634 { "floor", { &createFunFloor, 1 } },
635 { "id", { &createFunId, 1 } },
636 { "lang", { &createFunLang, 1 } },
637 { "last", { &createFunLast, 0 } },
638 { "local-name", { &createFunLocalName, Interval(0, 1) } },
639 { "name", { &createFunName, Interval(0, 1) } },
640 { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
641 { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
642 { "not", { &createFunNot, 1 } },
643 { "number", { &createFunNumber, Interval(0, 1) } },
644 { "position", { &createFunPosition, 0 } },
645 { "round", { &createFunRound, 1 } },
646 { "starts-with", { &createFunStartsWith, 2 } },
647 { "string", { &createFunString, Interval(0, 1) } },
648 { "string-length", { &createFunStringLength, Interval(0, 1) } },
649 { "substring", { &createFunSubstring, Interval(2, 3) } },
650 { "substring-after", { &createFunSubstringAfter, 2 } },
651 { "substring-before", { &createFunSubstringBefore, 2 } },
652 { "sum", { &createFunSum, 1 } },
653 { "translate", { &createFunTranslate, 3 } },
654 { "true", { &createFunTrue, 0 } },
656 const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]);
658 functionMap = new HashMap<String, FunctionRec>;
659 for (unsigned i = 0; i < numFunctions; ++i)
660 functionMap->set(functions[i].name, functions[i].function);
663 Function* createFunction(const String& name, const Vector<Expression*>& args)
668 HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name);
669 FunctionRec* functionRec = 0;
671 if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->second)->args.contains(args.size()))
674 Function* function = functionRec->factoryFn();
675 function->setArguments(args);
676 function->setName(name);
683 #endif // ENABLE(XPATH)