3ba7fa4cacaa2031192ee110c34702daacb75473
[WebKit-https.git] / WebCore / xml / XPathFunctions.cpp
1 /*
2  * Copyright 2005 Frerich Raabe <raabe@kde.org>
3  * Copyright (C) 2006 Apple Computer, Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "XPathFunctions.h"
29
30 #ifdef XPATH_SUPPORT
31
32 #include "NamedAttrMap.h"
33 #include "XPathValue.h"
34 #include <wtf/MathExtras.h>
35
36 namespace WebCore {
37 namespace XPath {
38         
39 #define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
40
41 class Interval {
42 public:
43     static const int Inf = -1;
44
45     Interval();
46     Interval(int value);
47     Interval(int min, int max);
48
49     bool contains(int value) const;
50
51 private:
52     int m_min;
53     int m_max;
54 };
55
56 struct FunctionRec {
57     typedef Function *(*FactoryFn)();
58     FactoryFn factoryFn;
59     Interval args;
60 };
61
62 static HashMap<String, FunctionRec>* functionMap;
63
64 class FunLast : public Function {
65     virtual bool isConstant() const;
66     virtual Value doEvaluate() const;
67 };
68
69 class FunPosition : public Function {
70     virtual bool isConstant() const;
71     virtual Value doEvaluate() const;
72 };
73
74 class FunCount : public Function {
75     virtual bool isConstant() const;
76     virtual Value doEvaluate() const;
77 };
78
79 class FunLocalName : public Function {
80     virtual bool isConstant() const;
81     virtual Value doEvaluate() const;
82 };
83
84 class FunNamespaceURI : public Function {
85     virtual bool isConstant() const;
86     virtual Value doEvaluate() const;
87 };
88
89 class FunName : public Function {
90     virtual bool isConstant() const;
91     virtual Value doEvaluate() const;
92 };
93
94 class FunString : public Function {
95     virtual Value doEvaluate() const;
96 };
97
98 class FunConcat : public Function {
99     virtual Value doEvaluate() const;
100 };
101
102 class FunStartsWith : public Function {
103     virtual Value doEvaluate() const;
104 };
105
106 class FunContains : public Function {
107     virtual Value doEvaluate() const;
108 };
109
110 class FunSubstringBefore : public Function {
111     virtual Value doEvaluate() const;
112 };
113
114 class FunSubstringAfter : public Function {
115     virtual Value doEvaluate() const;
116 };
117
118 class FunSubstring : public Function {
119     virtual Value doEvaluate() const;
120 };
121
122 class FunStringLength : public Function {
123     virtual Value doEvaluate() const;
124 };
125
126 class FunNormalizeSpace : public Function {
127     virtual Value doEvaluate() const;
128 };
129
130 class FunTranslate : public Function {
131     virtual Value doEvaluate() const;
132 };
133
134 class FunBoolean : public Function {
135     virtual Value doEvaluate() const;
136 };
137
138 class FunNot : public Function {
139     virtual Value doEvaluate() const;
140 };
141
142 class FunTrue : public Function {
143     virtual bool isConstant() const;
144     virtual Value doEvaluate() const;
145 };
146
147 class FunFalse : public Function {
148     virtual bool isConstant() const;
149     virtual Value doEvaluate() const;
150 };
151
152 class FunLang : public Function {
153     virtual bool isConstant() const;
154     virtual Value doEvaluate() const;
155 };
156
157 class FunNumber : public Function {
158     virtual Value doEvaluate() const;
159 };
160
161 class FunSum : public Function {
162     virtual Value doEvaluate() const;
163 };
164
165 class FunFloor : public Function {
166     virtual Value doEvaluate() const;
167 };
168
169 class FunCeiling : public Function {
170     virtual Value doEvaluate() const;
171 };
172
173 class FunRound : public Function {
174     virtual Value doEvaluate() const;
175 };
176
177 DEFINE_FUNCTION_CREATOR(FunLast)
178 DEFINE_FUNCTION_CREATOR(FunPosition)
179 DEFINE_FUNCTION_CREATOR(FunCount)
180 DEFINE_FUNCTION_CREATOR(FunLocalName)
181 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
182 DEFINE_FUNCTION_CREATOR(FunName)
183
184 DEFINE_FUNCTION_CREATOR(FunString)
185 DEFINE_FUNCTION_CREATOR(FunConcat)
186 DEFINE_FUNCTION_CREATOR(FunStartsWith)
187 DEFINE_FUNCTION_CREATOR(FunContains)
188 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
189 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
190 DEFINE_FUNCTION_CREATOR(FunSubstring)
191 DEFINE_FUNCTION_CREATOR(FunStringLength)
192 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
193 DEFINE_FUNCTION_CREATOR(FunTranslate)
194
195 DEFINE_FUNCTION_CREATOR(FunBoolean)
196 DEFINE_FUNCTION_CREATOR(FunNot)
197 DEFINE_FUNCTION_CREATOR(FunTrue)
198 DEFINE_FUNCTION_CREATOR(FunFalse)
199 DEFINE_FUNCTION_CREATOR(FunLang)
200
201 DEFINE_FUNCTION_CREATOR(FunNumber)
202 DEFINE_FUNCTION_CREATOR(FunSum)
203 DEFINE_FUNCTION_CREATOR(FunFloor)
204 DEFINE_FUNCTION_CREATOR(FunCeiling)
205 DEFINE_FUNCTION_CREATOR(FunRound)
206
207 #undef DEFINE_FUNCTION_CREATOR
208
209 inline Interval::Interval()
210     : m_min(Inf), m_max(Inf)
211 {
212 }
213
214 inline Interval::Interval(int value)
215     : m_min(value), m_max(value)
216 {
217 }
218
219 inline Interval::Interval(int min, int max)
220     : m_min(min), m_max(max)
221 {
222 }
223
224 inline bool Interval::contains(int value) const
225 {
226     if (m_min == Inf && m_max == Inf)
227         return true;
228
229     if (m_min == Inf)
230         return value <= m_max;
231
232     if (m_max == Inf)
233         return value >= m_min;
234
235     return value >= m_min && value <= m_max;
236 }
237
238 void Function::setArguments(const Vector<Expression*>& args)
239 {
240     Vector<Expression*>::const_iterator end = args.end();
241
242     for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
243         addSubExpression(*it);
244 }
245
246 void Function::setName(const String& name)
247 {
248     m_name = name;
249 }
250
251 Expression* Function::arg(int i)
252 {
253     return subExpr(i);
254 }
255
256 const Expression* Function::arg(int i) const
257 {
258     return subExpr(i);
259 }
260
261 unsigned int Function::argCount() const
262 {
263     return subExprCount();
264 }
265
266 String Function::name() const
267 {
268     return m_name;
269 }
270
271 Value FunLast::doEvaluate() const
272 {
273     return Expression::evaluationContext().size;
274 }
275
276 bool FunLast::isConstant() const
277 {
278     return false;
279 }
280
281 Value FunPosition::doEvaluate() const
282 {
283     return Expression::evaluationContext().position;
284 }
285
286 bool FunPosition::isConstant() const
287 {
288     return false;
289 }
290
291 bool FunLocalName::isConstant() const
292 {
293     return false;
294 }
295
296 Value FunLocalName::doEvaluate() const
297 {
298     Node* node = 0;
299     if (argCount() > 0) {
300         Value a = arg(0)->evaluate();
301         if (!a.isNodeVector() || a.toNodeVector().size() == 0)
302             return "";
303         
304         node = a.toNodeVector()[0].get();
305     }
306
307     if (!node)
308         node = evaluationContext().node.get();
309
310     return Value(node->localName());
311 }
312
313 bool FunNamespaceURI::isConstant() const
314 {
315     return false;
316 }
317
318 Value FunNamespaceURI::doEvaluate() const
319 {
320     Node* node = 0;
321     if (argCount() > 0) {
322         Value a = arg(0)->evaluate();
323         if (!a.isNodeVector() || a.toNodeVector().size() == 0)
324             return "";
325
326         node = a.toNodeVector()[0].get();
327     }
328
329     if (!node)
330         node = evaluationContext().node.get();
331
332     return Value(node->namespaceURI());
333 }
334
335 bool FunName::isConstant() const
336 {
337     return false;
338 }
339
340 Value FunName::doEvaluate() const
341 {
342     Node* node = 0;
343     if (argCount() > 0) {
344         Value a = arg(0)->evaluate();
345         if (!a.isNodeVector() || a.toNodeVector().size() == 0)
346             return "";
347
348         node = a.toNodeVector()[0].get();
349     }
350
351     if (!node)
352         node = evaluationContext().node.get();
353
354     const AtomicString& prefix = node->prefix();
355     return prefix.isEmpty() ? node->localName().domString() : node->prefix() + ":" + node->localName();
356 }
357
358 Value FunCount::doEvaluate() const
359 {
360     Value a = arg(0)->evaluate();
361     
362     if (!a.isNodeVector())
363         return 0.0;
364     
365     return a.toNodeVector().size();
366 }
367
368 bool FunCount::isConstant() const
369 {
370     return false;
371 }
372
373 Value FunString::doEvaluate() const
374 {
375     if (argCount() == 0)
376         return Value(Expression::evaluationContext().node).toString();
377     return arg(0)->evaluate().toString();
378 }
379
380 Value FunConcat::doEvaluate() const
381 {
382     String str = "";
383
384     for (unsigned i = 0; i < argCount(); ++i)
385         str += arg(i)->evaluate().toString();
386
387     return str;
388 }
389
390 Value FunStartsWith::doEvaluate() const
391 {
392     String s1 = arg(0)->evaluate().toString();
393     String s2 = arg(1)->evaluate().toString();
394
395     if (s2.isEmpty())
396         return true;
397
398     return s1.startsWith(s2);
399 }
400
401 Value FunContains::doEvaluate() const
402 {
403     String s1 = arg(0)->evaluate().toString();
404     String s2 = arg(1)->evaluate().toString();
405
406     if (s2.isEmpty()) 
407         return true;
408
409     return s1.contains(s2) != 0;
410 }
411
412 Value FunSubstringBefore::doEvaluate() const
413 {
414     String s1 = arg(0)->evaluate().toString();
415     String s2 = arg(1)->evaluate().toString();
416
417     if (s2.isEmpty())
418         return "";
419
420     int i = s1.find(s2);
421
422     if (i == -1)
423         return "";
424
425     return s1.left(i);
426 }
427
428 Value FunSubstringAfter::doEvaluate() const
429 {
430     String s1 = arg(0)->evaluate().toString();
431     String s2 = arg(1)->evaluate().toString();
432
433     if (s2.isEmpty())
434         return s2;
435
436     int i = s1.find(s2);
437     if (i == -1)
438         return "";
439
440     return s1.substring(i + 1);
441 }
442
443 Value FunSubstring::doEvaluate() const
444 {
445     String s = arg(0)->evaluate().toString();
446     long pos = lround(arg(1)->evaluate().toNumber());
447     bool haveLength = argCount() == 3;
448     long len = -1;
449     if (haveLength)
450         len = lround(arg(2)->evaluate().toNumber());
451
452     if (pos > long(s.length())) 
453         return "";
454
455     if (haveLength && pos < 1) {
456         len -= 1 - pos;
457         pos = 1;
458         if (len < 1)
459             return "";
460     }
461
462     return s.substring(pos - 1, len);
463 }
464
465 Value FunStringLength::doEvaluate() const
466 {
467     if (argCount() == 0)
468         return Value(Expression::evaluationContext().node).toString().length();
469     return arg(0)->evaluate().toString().length();
470 }
471
472 Value FunNormalizeSpace::doEvaluate() const
473 {
474     if (argCount() == 0) {
475         String s = Value(Expression::evaluationContext().node).toString();
476         return Value(s.simplifyWhiteSpace());
477     }
478
479     String s = arg(0)->evaluate().toString();
480     return Value(s.simplifyWhiteSpace());
481 }
482
483 Value FunTranslate::doEvaluate() const
484 {
485     String s1 = arg(0)->evaluate().toString();
486     String s2 = arg(1)->evaluate().toString();
487     String s3 = arg(2)->evaluate().toString();
488     String newString;
489
490     // FIXME: Building a String a character at a time is quite slow.
491     for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
492         UChar ch = s1[i1];
493         int i2 = s2.find(ch);
494         
495         if (i2 == -1)
496             newString += String(&ch, 1);
497         else if ((unsigned)i2 < s3.length()) {
498             UChar c2 = s3[i2];
499             newString += String(&c2, 1);
500         }
501     }
502
503     return newString;
504 }
505
506 Value FunBoolean::doEvaluate() const
507 {
508     return arg(0)->evaluate().toBoolean();
509 }
510
511 Value FunNot::doEvaluate() const
512 {
513     return !arg(0)->evaluate().toBoolean();
514 }
515
516 Value FunTrue::doEvaluate() const
517 {
518     return true;
519 }
520
521 bool FunTrue::isConstant() const
522 {
523     return true;
524 }
525
526 Value FunLang::doEvaluate() const
527 {
528     String lang = arg(0)->evaluate().toString();
529
530     RefPtr<Node> langNode = 0;
531     Node* node = evaluationContext().node.get();
532     String xmsnsURI = node->lookupNamespaceURI("xms");
533     while (node) {
534         NamedAttrMap* attrs = node->attributes();
535         langNode = attrs->getNamedItemNS(xmsnsURI, "lang");
536         if (langNode)
537             break;
538         node = node->parentNode();
539     }
540
541     if (!langNode)
542         return false;
543
544     String langNodeValue = langNode->nodeValue();
545
546     // extract 'en' out of 'en-us'
547     int index = langNodeValue.find('-');
548     if (index != -1)
549         langNodeValue = langNodeValue.left(index);
550
551     return equalIgnoringCase(langNodeValue, lang);
552 }
553
554 bool FunLang::isConstant() const
555 {
556     return false;
557 }
558
559 Value FunFalse::doEvaluate() const
560 {
561     return false;
562 }
563
564 bool FunFalse::isConstant() const
565 {
566     return true;
567 }
568
569 Value FunNumber::doEvaluate() const
570 {
571     return arg(0)->evaluate().toNumber();
572 }
573
574 Value FunSum::doEvaluate() const
575 {
576     Value a = arg(0)->evaluate();
577     if (!a.isNodeVector())
578         return 0.0;
579
580     double sum = 0.0;
581     NodeVector nodes = a.toNodeVector();
582     
583     for (unsigned i = 0; i < nodes.size(); i++)
584         sum += Value(stringValue(nodes[i].get())).toNumber();
585     
586     return sum;
587 }
588
589 Value FunFloor::doEvaluate() const
590 {
591     return floor(arg(0)->evaluate().toNumber());
592 }
593
594 Value FunCeiling::doEvaluate() const
595 {
596     return ceil(arg(0)->evaluate().toNumber());
597 }
598
599 Value FunRound::doEvaluate() const
600 {
601     return round(arg(0)->evaluate().toNumber());
602 }
603
604 static void createFunctionMap()
605 {
606     struct FunctionMapping {
607         const char *name;
608         FunctionRec function;
609     };
610     static const FunctionMapping functions[] = {
611         { "boolean", { &createFunBoolean, 1 } },
612         { "ceiling", { &createFunCeiling, 1 } },
613         { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
614         { "contains", { &createFunContains, 2 } },
615         { "count", { &createFunCount, 1 } },
616         { "false", { &createFunFalse, 0 } },
617         { "floor", { &createFunFloor, 1 } },
618         { "lang", { &createFunLang, 1 } },
619         { "last", { &createFunLast, 0 } },
620         { "local-name", { &createFunLocalName, Interval(0, 1) } },
621         { "name", { &createFunName, Interval(0, 1) } },
622         { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
623         { "normalize-space", { &createFunNormalizeSpace, 1 } },
624         { "not", { &createFunNot, 1 } },
625         { "number", { &createFunNumber, 1 } },
626         { "position", { &createFunPosition, 0 } },
627         { "round", { &createFunRound, 1 } },
628         { "starts-with", { &createFunStartsWith, 2 } },
629         { "string", { &createFunString, Interval(0, 1) } },
630         { "string-length", { &createFunStringLength, 1 } },
631         { "substring", { &createFunSubstring, Interval(2, 3) } },
632         { "substring-after", { &createFunSubstringAfter, 2 } },
633         { "substring-before", { &createFunSubstringBefore, 2 } },
634         { "sum", { &createFunSum, 1 } },
635         { "translate", { &createFunTranslate, 3 } },
636         { "true", { &createFunTrue, 0 } },
637     };
638     const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]);
639
640     functionMap = new HashMap<String, FunctionRec>;
641     for (unsigned i = 0; i < numFunctions; ++i)
642         functionMap->set(functions[i].name, functions[i].function);
643 }
644
645 Function* createFunction(const String& name, const Vector<Expression*>& args)
646 {
647     if (!functionMap)
648         createFunctionMap();
649
650     if (!functionMap->contains(name)) {
651         deleteAllValues(args);
652
653         // Return a dummy function instead of 0.
654         Function* funcTrue = functionMap->get("true").factoryFn();
655         funcTrue->setName("true");
656         return funcTrue;
657     }
658
659     FunctionRec functionRec = functionMap->get(name);
660     if (!functionRec.args.contains(args.size())) {
661         deleteAllValues(args);
662         return 0;
663     }
664
665     Function* function = functionRec.factoryFn();
666     function->setArguments(args);
667     function->setName(name);
668     return function;
669 }
670
671 }
672 }
673
674 #endif // XPATH_SUPPORT