1 /*
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>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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.
15 *
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.
26 */
27
28 #include "config.h"
29 #include "XPathFunctions.h"
30
31 #if ENABLE(XPATH)
32
33 #include "Document.h"
34 #include "Element.h"
35 #include "NamedNodeMap.h"
36 #include "ProcessingInstruction.h"
37 #include "XMLNames.h"
38 #include "XPathUtil.h"
39 #include "XPathValue.h"
40 #include <wtf/MathExtras.h>
41
42 namespace WebCore {
43 namespace XPath {
44
isWhitespace(UChar c)45 static inline bool isWhitespace(UChar c)
46 {
47 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
48 }
49
50
51 #define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
52
53 class Interval {
54 public:
55 static const int Inf = -1;
56
57 Interval();
58 Interval(int value);
59 Interval(int min, int max);
60
61 bool contains(int value) const;
62
63 private:
64 int m_min;
65 int m_max;
66 };
67
68 struct FunctionRec {
69 typedef Function *(*FactoryFn)();
70 FactoryFn factoryFn;
71 Interval args;
72 };
73
74 static HashMap<String, FunctionRec>* functionMap;
75
76 class FunLast : public Function {
77 virtual Value evaluate() const;
resultType() const78 virtual Value::Type resultType() const { return Value::NumberValue; }
79 public:
FunLast()80 FunLast() { setIsContextSizeSensitive(true); }
81 };
82
83 class FunPosition : public Function {
84 virtual Value evaluate() const;
resultType() const85 virtual Value::Type resultType() const { return Value::NumberValue; }
86 public:
FunPosition()87 FunPosition() { setIsContextPositionSensitive(true); }
88 };
89
90 class FunCount : public Function {
91 virtual Value evaluate() const;
resultType() const92 virtual Value::Type resultType() const { return Value::NumberValue; }
93 };
94
95 class FunId : public Function {
96 virtual Value evaluate() const;
resultType() const97 virtual Value::Type resultType() const { return Value::NodeSetValue; }
98 };
99
100 class FunLocalName : public Function {
101 virtual Value evaluate() const;
resultType() const102 virtual Value::Type resultType() const { return Value::StringValue; }
103 public:
FunLocalName()104 FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node.
105 };
106
107 class FunNamespaceURI : public Function {
108 virtual Value evaluate() const;
resultType() const109 virtual Value::Type resultType() const { return Value::StringValue; }
110 public:
FunNamespaceURI()111 FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node.
112 };
113
114 class FunName : public Function {
115 virtual Value evaluate() const;
resultType() const116 virtual Value::Type resultType() const { return Value::StringValue; }
117 public:
FunName()118 FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node.
119 };
120
121 class FunString : public Function {
122 virtual Value evaluate() const;
resultType() const123 virtual Value::Type resultType() const { return Value::StringValue; }
124 public:
FunString()125 FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node.
126 };
127
128 class FunConcat : public Function {
129 virtual Value evaluate() const;
resultType() const130 virtual Value::Type resultType() const { return Value::StringValue; }
131 };
132
133 class FunStartsWith : public Function {
134 virtual Value evaluate() const;
resultType() const135 virtual Value::Type resultType() const { return Value::BooleanValue; }
136 };
137
138 class FunContains : public Function {
139 virtual Value evaluate() const;
resultType() const140 virtual Value::Type resultType() const { return Value::BooleanValue; }
141 };
142
143 class FunSubstringBefore : public Function {
144 virtual Value evaluate() const;
resultType() const145 virtual Value::Type resultType() const { return Value::StringValue; }
146 };
147
148 class FunSubstringAfter : public Function {
149 virtual Value evaluate() const;
resultType() const150 virtual Value::Type resultType() const { return Value::StringValue; }
151 };
152
153 class FunSubstring : public Function {
154 virtual Value evaluate() const;
resultType() const155 virtual Value::Type resultType() const { return Value::StringValue; }
156 };
157
158 class FunStringLength : public Function {
159 virtual Value evaluate() const;
resultType() const160 virtual Value::Type resultType() const { return Value::NumberValue; }
161 public:
FunStringLength()162 FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node.
163 };
164
165 class FunNormalizeSpace : public Function {
166 virtual Value evaluate() const;
resultType() const167 virtual Value::Type resultType() const { return Value::StringValue; }
168 public:
FunNormalizeSpace()169 FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node.
170 };
171
172 class FunTranslate : public Function {
173 virtual Value evaluate() const;
resultType() const174 virtual Value::Type resultType() const { return Value::StringValue; }
175 };
176
177 class FunBoolean : public Function {
178 virtual Value evaluate() const;
resultType() const179 virtual Value::Type resultType() const { return Value::BooleanValue; }
180 };
181
182 class FunNot : public Function {
183 virtual Value evaluate() const;
resultType() const184 virtual Value::Type resultType() const { return Value::BooleanValue; }
185 };
186
187 class FunTrue : public Function {
188 virtual Value evaluate() const;
resultType() const189 virtual Value::Type resultType() const { return Value::BooleanValue; }
190 };
191
192 class FunFalse : public Function {
193 virtual Value evaluate() const;
resultType() const194 virtual Value::Type resultType() const { return Value::BooleanValue; }
195 };
196
197 class FunLang : public Function {
198 virtual Value evaluate() const;
resultType() const199 virtual Value::Type resultType() const { return Value::BooleanValue; }
200 public:
FunLang()201 FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node.
202 };
203
204 class FunNumber : public Function {
205 virtual Value evaluate() const;
resultType() const206 virtual Value::Type resultType() const { return Value::NumberValue; }
207 public:
FunNumber()208 FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node.
209 };
210
211 class FunSum : public Function {
212 virtual Value evaluate() const;
resultType() const213 virtual Value::Type resultType() const { return Value::NumberValue; }
214 };
215
216 class FunFloor : public Function {
217 virtual Value evaluate() const;
resultType() const218 virtual Value::Type resultType() const { return Value::NumberValue; }
219 };
220
221 class FunCeiling : public Function {
222 virtual Value evaluate() const;
resultType() const223 virtual Value::Type resultType() const { return Value::NumberValue; }
224 };
225
226 class FunRound : public Function {
227 virtual Value evaluate() const;
resultType() const228 virtual Value::Type resultType() const { return Value::NumberValue; }
229 public:
230 static double round(double);
231 };
232
233 DEFINE_FUNCTION_CREATOR(FunLast)
DEFINE_FUNCTION_CREATOR(FunPosition)234 DEFINE_FUNCTION_CREATOR(FunPosition)
235 DEFINE_FUNCTION_CREATOR(FunCount)
236 DEFINE_FUNCTION_CREATOR(FunId)
237 DEFINE_FUNCTION_CREATOR(FunLocalName)
238 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
239 DEFINE_FUNCTION_CREATOR(FunName)
240
241 DEFINE_FUNCTION_CREATOR(FunString)
242 DEFINE_FUNCTION_CREATOR(FunConcat)
243 DEFINE_FUNCTION_CREATOR(FunStartsWith)
244 DEFINE_FUNCTION_CREATOR(FunContains)
245 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
246 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
247 DEFINE_FUNCTION_CREATOR(FunSubstring)
248 DEFINE_FUNCTION_CREATOR(FunStringLength)
249 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
250 DEFINE_FUNCTION_CREATOR(FunTranslate)
251
252 DEFINE_FUNCTION_CREATOR(FunBoolean)
253 DEFINE_FUNCTION_CREATOR(FunNot)
254 DEFINE_FUNCTION_CREATOR(FunTrue)
255 DEFINE_FUNCTION_CREATOR(FunFalse)
256 DEFINE_FUNCTION_CREATOR(FunLang)
257
258 DEFINE_FUNCTION_CREATOR(FunNumber)
259 DEFINE_FUNCTION_CREATOR(FunSum)
260 DEFINE_FUNCTION_CREATOR(FunFloor)
261 DEFINE_FUNCTION_CREATOR(FunCeiling)
262 DEFINE_FUNCTION_CREATOR(FunRound)
263
264 #undef DEFINE_FUNCTION_CREATOR
265
266 inline Interval::Interval()
267 : m_min(Inf), m_max(Inf)
268 {
269 }
270
Interval(int value)271 inline Interval::Interval(int value)
272 : m_min(value), m_max(value)
273 {
274 }
275
Interval(int min,int max)276 inline Interval::Interval(int min, int max)
277 : m_min(min), m_max(max)
278 {
279 }
280
contains(int value) const281 inline bool Interval::contains(int value) const
282 {
283 if (m_min == Inf && m_max == Inf)
284 return true;
285
286 if (m_min == Inf)
287 return value <= m_max;
288
289 if (m_max == Inf)
290 return value >= m_min;
291
292 return value >= m_min && value <= m_max;
293 }
294
setArguments(const Vector<Expression * > & args)295 void Function::setArguments(const Vector<Expression*>& args)
296 {
297 ASSERT(!subExprCount());
298
299 // Some functions use context node as implicit argument, so when explicit arguments are added, they may no longer be context node sensitive.
300 if (m_name != "lang" && !args.isEmpty())
301 setIsContextNodeSensitive(false);
302
303 Vector<Expression*>::const_iterator end = args.end();
304 for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
305 addSubExpression(*it);
306 }
307
evaluate() const308 Value FunLast::evaluate() const
309 {
310 return Expression::evaluationContext().size;
311 }
312
evaluate() const313 Value FunPosition::evaluate() const
314 {
315 return Expression::evaluationContext().position;
316 }
317
evaluate() const318 Value FunId::evaluate() const
319 {
320 Value a = arg(0)->evaluate();
321 Vector<UChar> idList; // A whitespace-separated list of IDs
322
323 if (a.isNodeSet()) {
324 const NodeSet& nodes = a.toNodeSet();
325 for (size_t i = 0; i < nodes.size(); ++i) {
326 String str = stringValue(nodes[i]);
327 idList.append(str.characters(), str.length());
328 idList.append(' ');
329 }
330 } else {
331 String str = a.toString();
332 idList.append(str.characters(), str.length());
333 }
334
335 Document* contextDocument = evaluationContext().node->document();
336 NodeSet result;
337 HashSet<Node*> resultSet;
338
339 size_t startPos = 0;
340 size_t length = idList.size();
341 while (true) {
342 while (startPos < length && isWhitespace(idList[startPos]))
343 ++startPos;
344
345 if (startPos == length)
346 break;
347
348 size_t endPos = startPos;
349 while (endPos < length && !isWhitespace(idList[endPos]))
350 ++endPos;
351
352 // If there are several nodes with the same id, id() should return the first one.
353 // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
354 Node* node = contextDocument->getElementById(String(&idList[startPos], endPos - startPos));
355 if (node && resultSet.add(node).second)
356 result.append(node);
357
358 startPos = endPos;
359 }
360
361 result.markSorted(false);
362
363 return Value(result, Value::adopt);
364 }
365
expandedNameLocalPart(Node * node)366 static inline String expandedNameLocalPart(Node* node)
367 {
368 // The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes.
369 ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet.
370 if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
371 return static_cast<ProcessingInstruction*>(node)->target();
372 return node->localName().string();
373 }
374
expandedName(Node * node)375 static inline String expandedName(Node* node)
376 {
377 const AtomicString& prefix = node->prefix();
378 return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node);
379 }
380
evaluate() const381 Value FunLocalName::evaluate() const
382 {
383 if (argCount() > 0) {
384 Value a = arg(0)->evaluate();
385 if (!a.isNodeSet())
386 return "";
387
388 Node* node = a.toNodeSet().firstNode();
389 return node ? expandedNameLocalPart(node) : "";
390 }
391
392 return expandedNameLocalPart(evaluationContext().node.get());
393 }
394
evaluate() const395 Value FunNamespaceURI::evaluate() const
396 {
397 if (argCount() > 0) {
398 Value a = arg(0)->evaluate();
399 if (!a.isNodeSet())
400 return "";
401
402 Node* node = a.toNodeSet().firstNode();
403 return node ? node->namespaceURI().string() : "";
404 }
405
406 return evaluationContext().node->namespaceURI().string();
407 }
408
evaluate() const409 Value FunName::evaluate() const
410 {
411 if (argCount() > 0) {
412 Value a = arg(0)->evaluate();
413 if (!a.isNodeSet())
414 return "";
415
416 Node* node = a.toNodeSet().firstNode();
417 return node ? expandedName(node) : "";
418 }
419
420 return expandedName(evaluationContext().node.get());
421 }
422
evaluate() const423 Value FunCount::evaluate() const
424 {
425 Value a = arg(0)->evaluate();
426
427 return double(a.toNodeSet().size());
428 }
429
evaluate() const430 Value FunString::evaluate() const
431 {
432 if (!argCount())
433 return Value(Expression::evaluationContext().node.get()).toString();
434 return arg(0)->evaluate().toString();
435 }
436
evaluate() const437 Value FunConcat::evaluate() const
438 {
439 Vector<UChar, 1024> result;
440
441 unsigned count = argCount();
442 for (unsigned i = 0; i < count; ++i) {
443 String str(arg(i)->evaluate().toString());
444 result.append(str.characters(), str.length());
445 }
446
447 return String(result.data(), result.size());
448 }
449
evaluate() const450 Value FunStartsWith::evaluate() const
451 {
452 String s1 = arg(0)->evaluate().toString();
453 String s2 = arg(1)->evaluate().toString();
454
455 if (s2.isEmpty())
456 return true;
457
458 return s1.startsWith(s2);
459 }
460
evaluate() const461 Value FunContains::evaluate() const
462 {
463 String s1 = arg(0)->evaluate().toString();
464 String s2 = arg(1)->evaluate().toString();
465
466 if (s2.isEmpty())
467 return true;
468
469 return s1.contains(s2) != 0;
470 }
471
evaluate() const472 Value FunSubstringBefore::evaluate() const
473 {
474 String s1 = arg(0)->evaluate().toString();
475 String s2 = arg(1)->evaluate().toString();
476
477 if (s2.isEmpty())
478 return "";
479
480 int i = s1.find(s2);
481
482 if (i == -1)
483 return "";
484
485 return s1.left(i);
486 }
487
evaluate() const488 Value FunSubstringAfter::evaluate() const
489 {
490 String s1 = arg(0)->evaluate().toString();
491 String s2 = arg(1)->evaluate().toString();
492
493 int i = s1.find(s2);
494 if (i == -1)
495 return "";
496
497 return s1.substring(i + s2.length());
498 }
499
evaluate() const500 Value FunSubstring::evaluate() const
501 {
502 String s = arg(0)->evaluate().toString();
503 long pos = static_cast<long>(FunRound::round(arg(1)->evaluate().toNumber()));
504 bool haveLength = argCount() == 3;
505 long len = -1;
506 if (haveLength) {
507 double doubleLen = arg(2)->evaluate().toNumber();
508 if (isnan(doubleLen))
509 return "";
510 len = static_cast<long>(FunRound::round(doubleLen));
511 }
512
513 if (pos > long(s.length()))
514 return "";
515
516 if (haveLength && pos < 1) {
517 len -= 1 - pos;
518 pos = 1;
519 if (len < 1)
520 return "";
521 }
522
523 return s.substring(pos - 1, len);
524 }
525
evaluate() const526 Value FunStringLength::evaluate() const
527 {
528 if (!argCount())
529 return Value(Expression::evaluationContext().node.get()).toString().length();
530 return arg(0)->evaluate().toString().length();
531 }
532
evaluate() const533 Value FunNormalizeSpace::evaluate() const
534 {
535 if (!argCount()) {
536 String s = Value(Expression::evaluationContext().node.get()).toString();
537 return s.simplifyWhiteSpace();
538 }
539
540 String s = arg(0)->evaluate().toString();
541 return s.simplifyWhiteSpace();
542 }
543
evaluate() const544 Value FunTranslate::evaluate() const
545 {
546 String s1 = arg(0)->evaluate().toString();
547 String s2 = arg(1)->evaluate().toString();
548 String s3 = arg(2)->evaluate().toString();
549 String newString;
550
551 // FIXME: Building a String a character at a time is quite slow.
552 for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
553 UChar ch = s1[i1];
554 int i2 = s2.find(ch);
555
556 if (i2 == -1)
557 newString += String(&ch, 1);
558 else if ((unsigned)i2 < s3.length()) {
559 UChar c2 = s3[i2];
560 newString += String(&c2, 1);
561 }
562 }
563
564 return newString;
565 }
566
evaluate() const567 Value FunBoolean::evaluate() const
568 {
569 return arg(0)->evaluate().toBoolean();
570 }
571
evaluate() const572 Value FunNot::evaluate() const
573 {
574 return !arg(0)->evaluate().toBoolean();
575 }
576
evaluate() const577 Value FunTrue::evaluate() const
578 {
579 return true;
580 }
581
evaluate() const582 Value FunLang::evaluate() const
583 {
584 String lang = arg(0)->evaluate().toString();
585
586 Attribute* languageAttribute = 0;
587 Node* node = evaluationContext().node.get();
588 while (node) {
589 NamedNodeMap* attrs = node->attributes();
590 if (attrs)
591 languageAttribute = attrs->getAttributeItem(XMLNames::langAttr);
592 if (languageAttribute)
593 break;
594 node = node->parentNode();
595 }
596
597 if (!languageAttribute)
598 return false;
599
600 String langValue = languageAttribute->value();
601 while (true) {
602 if (equalIgnoringCase(langValue, lang))
603 return true;
604
605 // Remove suffixes one by one.
606 int index = langValue.reverseFind('-');
607 if (index == -1)
608 break;
609 langValue = langValue.left(index);
610 }
611
612 return false;
613 }
614
evaluate() const615 Value FunFalse::evaluate() const
616 {
617 return false;
618 }
619
evaluate() const620 Value FunNumber::evaluate() const
621 {
622 if (!argCount())
623 return Value(Expression::evaluationContext().node.get()).toNumber();
624 return arg(0)->evaluate().toNumber();
625 }
626
evaluate() const627 Value FunSum::evaluate() const
628 {
629 Value a = arg(0)->evaluate();
630 if (!a.isNodeSet())
631 return 0.0;
632
633 double sum = 0.0;
634 const NodeSet& nodes = a.toNodeSet();
635 // To be really compliant, we should sort the node-set, as floating point addition is not associative.
636 // However, this is unlikely to ever become a practical issue, and sorting is slow.
637
638 for (unsigned i = 0; i < nodes.size(); i++)
639 sum += Value(stringValue(nodes[i])).toNumber();
640
641 return sum;
642 }
643
evaluate() const644 Value FunFloor::evaluate() const
645 {
646 return floor(arg(0)->evaluate().toNumber());
647 }
648
evaluate() const649 Value FunCeiling::evaluate() const
650 {
651 return ceil(arg(0)->evaluate().toNumber());
652 }
653
round(double val)654 double FunRound::round(double val)
655 {
656 if (!isnan(val) && !isinf(val)) {
657 if (signbit(val) && val >= -0.5)
658 val *= 0; // negative zero
659 else
660 val = floor(val + 0.5);
661 }
662 return val;
663 }
664
evaluate() const665 Value FunRound::evaluate() const
666 {
667 return round(arg(0)->evaluate().toNumber());
668 }
669
createFunctionMap()670 static void createFunctionMap()
671 {
672 struct FunctionMapping {
673 const char *name;
674 FunctionRec function;
675 };
676 static const FunctionMapping functions[] = {
677 { "boolean", { &createFunBoolean, 1 } },
678 { "ceiling", { &createFunCeiling, 1 } },
679 { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
680 { "contains", { &createFunContains, 2 } },
681 { "count", { &createFunCount, 1 } },
682 { "false", { &createFunFalse, 0 } },
683 { "floor", { &createFunFloor, 1 } },
684 { "id", { &createFunId, 1 } },
685 { "lang", { &createFunLang, 1 } },
686 { "last", { &createFunLast, 0 } },
687 { "local-name", { &createFunLocalName, Interval(0, 1) } },
688 { "name", { &createFunName, Interval(0, 1) } },
689 { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
690 { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
691 { "not", { &createFunNot, 1 } },
692 { "number", { &createFunNumber, Interval(0, 1) } },
693 { "position", { &createFunPosition, 0 } },
694 { "round", { &createFunRound, 1 } },
695 { "starts-with", { &createFunStartsWith, 2 } },
696 { "string", { &createFunString, Interval(0, 1) } },
697 { "string-length", { &createFunStringLength, Interval(0, 1) } },
698 { "substring", { &createFunSubstring, Interval(2, 3) } },
699 { "substring-after", { &createFunSubstringAfter, 2 } },
700 { "substring-before", { &createFunSubstringBefore, 2 } },
701 { "sum", { &createFunSum, 1 } },
702 { "translate", { &createFunTranslate, 3 } },
703 { "true", { &createFunTrue, 0 } },
704 };
705 const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]);
706
707 functionMap = new HashMap<String, FunctionRec>;
708 for (unsigned i = 0; i < numFunctions; ++i)
709 functionMap->set(functions[i].name, functions[i].function);
710 }
711
createFunction(const String & name,const Vector<Expression * > & args)712 Function* createFunction(const String& name, const Vector<Expression*>& args)
713 {
714 if (!functionMap)
715 createFunctionMap();
716
717 HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name);
718 FunctionRec* functionRec = 0;
719
720 if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->second)->args.contains(args.size()))
721 return 0;
722
723 Function* function = functionRec->factoryFn();
724 function->setArguments(args);
725 function->setName(name);
726 return function;
727 }
728
729 }
730 }
731
732 #endif // ENABLE(XPATH)
733