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"
34 #include "NamedAttrMap.h"
35 #include "XPathValue.h"
36 #include <wtf/MathExtras.h>
41 static inline bool isWhitespace(UChar c)
43 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
47 #define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
51 static const int Inf = -1;
55 Interval(int min, int max);
57 bool contains(int value) const;
65 typedef Function *(*FactoryFn)();
70 static HashMap<String, FunctionRec>* functionMap;
72 class FunLast : public Function {
73 virtual bool isConstant() const;
74 virtual Value doEvaluate() const;
77 class FunPosition : public Function {
78 virtual bool isConstant() const;
79 virtual Value doEvaluate() const;
82 class FunCount : public Function {
83 virtual bool isConstant() const;
84 virtual Value doEvaluate() const;
87 class FunId : public Function {
88 virtual bool isConstant() const;
89 virtual Value doEvaluate() const;
92 class FunLocalName : public Function {
93 virtual bool isConstant() const;
94 virtual Value doEvaluate() const;
97 class FunNamespaceURI : public Function {
98 virtual bool isConstant() const;
99 virtual Value doEvaluate() const;
102 class FunName : public Function {
103 virtual bool isConstant() const;
104 virtual Value doEvaluate() const;
107 class FunString : public Function {
108 virtual Value doEvaluate() const;
111 class FunConcat : public Function {
112 virtual Value doEvaluate() const;
115 class FunStartsWith : public Function {
116 virtual Value doEvaluate() const;
119 class FunContains : public Function {
120 virtual Value doEvaluate() const;
123 class FunSubstringBefore : public Function {
124 virtual Value doEvaluate() const;
127 class FunSubstringAfter : public Function {
128 virtual Value doEvaluate() const;
131 class FunSubstring : public Function {
132 virtual Value doEvaluate() const;
135 class FunStringLength : public Function {
136 virtual Value doEvaluate() const;
139 class FunNormalizeSpace : public Function {
140 virtual Value doEvaluate() const;
143 class FunTranslate : public Function {
144 virtual Value doEvaluate() const;
147 class FunBoolean : public Function {
148 virtual Value doEvaluate() const;
151 class FunNot : public Function {
152 virtual Value doEvaluate() const;
155 class FunTrue : public Function {
156 virtual bool isConstant() const;
157 virtual Value doEvaluate() const;
160 class FunFalse : public Function {
161 virtual bool isConstant() const;
162 virtual Value doEvaluate() const;
165 class FunLang : public Function {
166 virtual bool isConstant() const;
167 virtual Value doEvaluate() const;
170 class FunNumber : public Function {
171 virtual Value doEvaluate() const;
174 class FunSum : public Function {
175 virtual Value doEvaluate() const;
178 class FunFloor : public Function {
179 virtual Value doEvaluate() const;
182 class FunCeiling : public Function {
183 virtual Value doEvaluate() const;
186 class FunRound : public Function {
187 virtual Value doEvaluate() const;
190 DEFINE_FUNCTION_CREATOR(FunLast)
191 DEFINE_FUNCTION_CREATOR(FunPosition)
192 DEFINE_FUNCTION_CREATOR(FunCount)
193 DEFINE_FUNCTION_CREATOR(FunId)
194 DEFINE_FUNCTION_CREATOR(FunLocalName)
195 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
196 DEFINE_FUNCTION_CREATOR(FunName)
198 DEFINE_FUNCTION_CREATOR(FunString)
199 DEFINE_FUNCTION_CREATOR(FunConcat)
200 DEFINE_FUNCTION_CREATOR(FunStartsWith)
201 DEFINE_FUNCTION_CREATOR(FunContains)
202 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
203 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
204 DEFINE_FUNCTION_CREATOR(FunSubstring)
205 DEFINE_FUNCTION_CREATOR(FunStringLength)
206 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
207 DEFINE_FUNCTION_CREATOR(FunTranslate)
209 DEFINE_FUNCTION_CREATOR(FunBoolean)
210 DEFINE_FUNCTION_CREATOR(FunNot)
211 DEFINE_FUNCTION_CREATOR(FunTrue)
212 DEFINE_FUNCTION_CREATOR(FunFalse)
213 DEFINE_FUNCTION_CREATOR(FunLang)
215 DEFINE_FUNCTION_CREATOR(FunNumber)
216 DEFINE_FUNCTION_CREATOR(FunSum)
217 DEFINE_FUNCTION_CREATOR(FunFloor)
218 DEFINE_FUNCTION_CREATOR(FunCeiling)
219 DEFINE_FUNCTION_CREATOR(FunRound)
221 #undef DEFINE_FUNCTION_CREATOR
223 inline Interval::Interval()
224 : m_min(Inf), m_max(Inf)
228 inline Interval::Interval(int value)
229 : m_min(value), m_max(value)
233 inline Interval::Interval(int min, int max)
234 : m_min(min), m_max(max)
238 inline bool Interval::contains(int value) const
240 if (m_min == Inf && m_max == Inf)
244 return value <= m_max;
247 return value >= m_min;
249 return value >= m_min && value <= m_max;
252 void Function::setArguments(const Vector<Expression*>& args)
254 Vector<Expression*>::const_iterator end = args.end();
256 for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
257 addSubExpression(*it);
260 void Function::setName(const String& name)
265 Expression* Function::arg(int i)
270 const Expression* Function::arg(int i) const
275 unsigned int Function::argCount() const
277 return subExprCount();
280 String Function::name() const
285 Value FunLast::doEvaluate() const
287 return Expression::evaluationContext().size;
290 bool FunLast::isConstant() const
295 Value FunPosition::doEvaluate() const
297 return Expression::evaluationContext().position;
300 bool FunPosition::isConstant() const
305 bool FunId::isConstant() const
310 Value FunId::doEvaluate() const
312 // FIXME: this algorithm does not produce an ordered node-set, as it should.
314 Value a = arg(0)->evaluate();
315 Vector<UChar> idList; // A whitespace-separated list of IDs
317 if (a.isNodeVector()) {
318 const NodeVector& nodes = a.toNodeVector();
319 for (size_t i = 0; i < nodes.size(); ++i) {
320 String str = stringValue(nodes[i].get());
321 idList.append(str.characters(), str.length());
325 String str = a.toString();
326 idList.append(str.characters(), str.length());
329 Document* contextDocument = evaluationContext().node->document();
331 HashSet<Node*> resultSet;
334 size_t length = idList.size();
336 while (startPos < length && isWhitespace(idList[startPos]))
339 size_t endPos = startPos;
340 while (endPos < length && !isWhitespace(idList[endPos]))
343 if (endPos == length)
346 // If there are several nodes with the same id, id() should return the first one.
347 // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
348 Node* node = contextDocument->getElementById(String(&idList[startPos], endPos - startPos));
349 if (node && resultSet.add(node).second)
358 bool FunLocalName::isConstant() const
363 Value FunLocalName::doEvaluate() const
366 if (argCount() > 0) {
367 Value a = arg(0)->evaluate();
368 if (!a.isNodeVector() || a.toNodeVector().size() == 0)
371 node = a.toNodeVector()[0].get();
375 node = evaluationContext().node.get();
377 return Value(node->localName());
380 bool FunNamespaceURI::isConstant() const
385 Value FunNamespaceURI::doEvaluate() const
388 if (argCount() > 0) {
389 Value a = arg(0)->evaluate();
390 if (!a.isNodeVector() || a.toNodeVector().size() == 0)
393 node = a.toNodeVector()[0].get();
397 node = evaluationContext().node.get();
399 return Value(node->namespaceURI());
402 bool FunName::isConstant() const
407 Value FunName::doEvaluate() const
410 if (argCount() > 0) {
411 Value a = arg(0)->evaluate();
412 if (!a.isNodeVector() || a.toNodeVector().size() == 0)
415 node = a.toNodeVector()[0].get();
419 node = evaluationContext().node.get();
421 const AtomicString& prefix = node->prefix();
422 return prefix.isEmpty() ? node->localName().domString() : node->prefix() + ":" + node->localName();
425 Value FunCount::doEvaluate() const
427 Value a = arg(0)->evaluate();
429 if (!a.isNodeVector())
432 return a.toNodeVector().size();
435 bool FunCount::isConstant() const
440 Value FunString::doEvaluate() const
443 return Value(Expression::evaluationContext().node).toString();
444 return arg(0)->evaluate().toString();
447 Value FunConcat::doEvaluate() const
451 for (unsigned i = 0; i < argCount(); ++i)
452 str += arg(i)->evaluate().toString();
457 Value FunStartsWith::doEvaluate() const
459 String s1 = arg(0)->evaluate().toString();
460 String s2 = arg(1)->evaluate().toString();
465 return s1.startsWith(s2);
468 Value FunContains::doEvaluate() const
470 String s1 = arg(0)->evaluate().toString();
471 String s2 = arg(1)->evaluate().toString();
476 return s1.contains(s2) != 0;
479 Value FunSubstringBefore::doEvaluate() const
481 String s1 = arg(0)->evaluate().toString();
482 String s2 = arg(1)->evaluate().toString();
495 Value FunSubstringAfter::doEvaluate() const
497 String s1 = arg(0)->evaluate().toString();
498 String s2 = arg(1)->evaluate().toString();
507 return s1.substring(i + 1);
510 Value FunSubstring::doEvaluate() const
512 String s = arg(0)->evaluate().toString();
513 long pos = lround(arg(1)->evaluate().toNumber());
514 bool haveLength = argCount() == 3;
517 len = lround(arg(2)->evaluate().toNumber());
519 if (pos > long(s.length()))
522 if (haveLength && pos < 1) {
529 return s.substring(pos - 1, len);
532 Value FunStringLength::doEvaluate() const
535 return Value(Expression::evaluationContext().node).toString().length();
536 return arg(0)->evaluate().toString().length();
539 Value FunNormalizeSpace::doEvaluate() const
541 if (argCount() == 0) {
542 String s = Value(Expression::evaluationContext().node).toString();
543 return Value(s.simplifyWhiteSpace());
546 String s = arg(0)->evaluate().toString();
547 return Value(s.simplifyWhiteSpace());
550 Value FunTranslate::doEvaluate() const
552 String s1 = arg(0)->evaluate().toString();
553 String s2 = arg(1)->evaluate().toString();
554 String s3 = arg(2)->evaluate().toString();
557 // FIXME: Building a String a character at a time is quite slow.
558 for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
560 int i2 = s2.find(ch);
563 newString += String(&ch, 1);
564 else if ((unsigned)i2 < s3.length()) {
566 newString += String(&c2, 1);
573 Value FunBoolean::doEvaluate() const
575 return arg(0)->evaluate().toBoolean();
578 Value FunNot::doEvaluate() const
580 return !arg(0)->evaluate().toBoolean();
583 Value FunTrue::doEvaluate() const
588 bool FunTrue::isConstant() const
593 Value FunLang::doEvaluate() const
595 String lang = arg(0)->evaluate().toString();
597 RefPtr<Node> langNode = 0;
598 Node* node = evaluationContext().node.get();
599 String xmsnsURI = node->lookupNamespaceURI("xms");
601 NamedAttrMap* attrs = node->attributes();
602 langNode = attrs->getNamedItemNS(xmsnsURI, "lang");
605 node = node->parentNode();
611 String langNodeValue = langNode->nodeValue();
613 // extract 'en' out of 'en-us'
614 int index = langNodeValue.find('-');
616 langNodeValue = langNodeValue.left(index);
618 return equalIgnoringCase(langNodeValue, lang);
621 bool FunLang::isConstant() const
626 Value FunFalse::doEvaluate() const
631 bool FunFalse::isConstant() const
636 Value FunNumber::doEvaluate() const
638 return arg(0)->evaluate().toNumber();
641 Value FunSum::doEvaluate() const
643 Value a = arg(0)->evaluate();
644 if (!a.isNodeVector())
648 NodeVector nodes = a.toNodeVector();
650 for (unsigned i = 0; i < nodes.size(); i++)
651 sum += Value(stringValue(nodes[i].get())).toNumber();
656 Value FunFloor::doEvaluate() const
658 return floor(arg(0)->evaluate().toNumber());
661 Value FunCeiling::doEvaluate() const
663 return ceil(arg(0)->evaluate().toNumber());
666 Value FunRound::doEvaluate() const
668 return round(arg(0)->evaluate().toNumber());
671 static void createFunctionMap()
673 struct FunctionMapping {
675 FunctionRec function;
677 static const FunctionMapping functions[] = {
678 { "boolean", { &createFunBoolean, 1 } },
679 { "ceiling", { &createFunCeiling, 1 } },
680 { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
681 { "contains", { &createFunContains, 2 } },
682 { "count", { &createFunCount, 1 } },
683 { "false", { &createFunFalse, 0 } },
684 { "floor", { &createFunFloor, 1 } },
685 { "id", { &createFunId, 1 } },
686 { "lang", { &createFunLang, 1 } },
687 { "last", { &createFunLast, 0 } },
688 { "local-name", { &createFunLocalName, Interval(0, 1) } },
689 { "name", { &createFunName, Interval(0, 1) } },
690 { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
691 { "normalize-space", { &createFunNormalizeSpace, 1 } },
692 { "not", { &createFunNot, 1 } },
693 { "number", { &createFunNumber, 1 } },
694 { "position", { &createFunPosition, 0 } },
695 { "round", { &createFunRound, 1 } },
696 { "starts-with", { &createFunStartsWith, 2 } },
697 { "string", { &createFunString, Interval(0, 1) } },
698 { "string-length", { &createFunStringLength, 1 } },
699 { "substring", { &createFunSubstring, Interval(2, 3) } },
700 { "substring-after", { &createFunSubstringAfter, 2 } },
701 { "substring-before", { &createFunSubstringBefore, 2 } },
702 { "sum", { &createFunSum, 1 } },
703 { "translate", { &createFunTranslate, 3 } },
704 { "true", { &createFunTrue, 0 } },
706 const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]);
708 functionMap = new HashMap<String, FunctionRec>;
709 for (unsigned i = 0; i < numFunctions; ++i)
710 functionMap->set(functions[i].name, functions[i].function);
713 Function* createFunction(const String& name, const Vector<Expression*>& args)
718 if (!functionMap->contains(name)) {
719 deleteAllValues(args);
721 // Return a dummy function instead of 0.
722 Function* funcTrue = functionMap->get("true").factoryFn();
723 funcTrue->setName("true");
727 FunctionRec functionRec = functionMap->get(name);
728 if (!functionRec.args.contains(args.size())) {
729 deleteAllValues(args);
733 Function* function = functionRec.factoryFn();
734 function->setArguments(args);
735 function->setName(name);
742 #endif // XPATH_SUPPORT