Warning, file /frameworks/khtml/src/xpath/functions.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * functions.cc - Copyright 2005 Frerich Raabe <raabe@kde.org>
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  *
0008  * 1. Redistributions of source code must retain the above copyright
0009  *    notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *    notice, this list of conditions and the following disclaimer in the
0012  *    documentation and/or other materials provided with the distribution.
0013  *
0014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0015  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0016  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0017  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0018  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0019  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0020  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0021  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0023  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0024  */
0025 #include "functions.h"
0026 
0027 #include "xml/dom_docimpl.h"
0028 #include "xml/dom_nodeimpl.h"
0029 #include "xml/dom_nodelistimpl.h"
0030 #include "xml/dom_elementimpl.h"
0031 #include "kjs/operations.h"
0032 
0033 #include <QtDebug>
0034 
0035 #include <math.h>
0036 
0037 using namespace DOM;
0038 
0039 namespace khtml
0040 {
0041 namespace XPath
0042 {
0043 
0044 #define DEFINE_FUNCTION_CREATOR(Class) \
0045     static Function *create##Class() { return new Class; }
0046 
0047 class Interval
0048 {
0049 public:
0050     static const int Inf = -1;
0051 
0052     Interval();
0053     Interval(int value);
0054     Interval(int min, int max);
0055 
0056     bool contains(int value) const;
0057 
0058     QString asString() const;
0059 
0060 private:
0061     int m_min;
0062     int m_max;
0063 };
0064 
0065 class FunLast : public Function
0066 {
0067 public:
0068     bool isConstant() const override;
0069 
0070 private:
0071     Value doEvaluate() const override;
0072 };
0073 
0074 class FunPosition : public Function
0075 {
0076 public:
0077     bool isConstant() const override;
0078 
0079 private:
0080     Value doEvaluate() const override;
0081 };
0082 
0083 class FunCount : public Function
0084 {
0085 public:
0086     bool isConstant() const override;
0087 
0088 private:
0089     Value doEvaluate() const override;
0090 };
0091 
0092 // Base for various node-property functions, that have
0093 // the same node picking logic. It passes the proper node,
0094 // if any, or otherwise returns an empty string by itself
0095 class NodeFunction : public Function
0096 {
0097 private:
0098     Value doEvaluate() const override;
0099     virtual Value evaluateOnNode(DOM::NodeImpl *node) const = 0;
0100 };
0101 
0102 class FunLocalName : public NodeFunction
0103 {
0104 public:
0105     bool isConstant() const override;
0106 
0107 private:
0108     Value evaluateOnNode(DOM::NodeImpl *node) const override;
0109 };
0110 
0111 class FunNamespaceURI : public NodeFunction
0112 {
0113 public:
0114     bool isConstant() const override;
0115 
0116 private:
0117     Value evaluateOnNode(DOM::NodeImpl *node) const override;
0118 };
0119 
0120 class FunName : public NodeFunction
0121 {
0122 public:
0123     bool isConstant() const override;
0124 
0125 private:
0126     Value evaluateOnNode(DOM::NodeImpl *node) const override;
0127 };
0128 
0129 class FunId : public Function
0130 {
0131 private:
0132     Value doEvaluate() const override;
0133 };
0134 
0135 class FunString : public Function
0136 {
0137 private:
0138     Value doEvaluate() const override;
0139 };
0140 
0141 class FunConcat : public Function
0142 {
0143 private:
0144     Value doEvaluate() const override;
0145 };
0146 
0147 class FunStartsWith : public Function
0148 {
0149 private:
0150     Value doEvaluate() const override;
0151 };
0152 
0153 class FunContains : public Function
0154 {
0155 private:
0156     Value doEvaluate() const override;
0157 };
0158 
0159 class FunSubstringBefore : public Function
0160 {
0161 private:
0162     Value doEvaluate() const override;
0163 };
0164 
0165 class FunSubstringAfter : public Function
0166 {
0167 private:
0168     Value doEvaluate() const override;
0169 };
0170 
0171 class FunSubstring : public Function
0172 {
0173 private:
0174     Value doEvaluate() const override;
0175 };
0176 
0177 class FunStringLength : public Function
0178 {
0179 private:
0180     Value doEvaluate() const override;
0181 };
0182 
0183 class FunNormalizeSpace : public Function
0184 {
0185 private:
0186     Value doEvaluate() const override;
0187 };
0188 
0189 class FunTranslate : public Function
0190 {
0191 private:
0192     Value doEvaluate() const override;
0193 };
0194 
0195 class FunBoolean : public Function
0196 {
0197 private:
0198     Value doEvaluate() const override;
0199 };
0200 
0201 class FunNot : public Function
0202 {
0203 private:
0204     Value doEvaluate() const override;
0205 };
0206 
0207 class FunTrue : public Function
0208 {
0209 public:
0210     bool isConstant() const override;
0211 
0212 private:
0213     Value doEvaluate() const override;
0214 };
0215 
0216 class FunFalse : public Function
0217 {
0218 public:
0219     bool isConstant() const override;
0220 
0221 private:
0222     Value doEvaluate() const override;
0223 };
0224 
0225 class FunLang : public Function
0226 {
0227 public:
0228     bool isConstant() const override;
0229 
0230 private:
0231     Value doEvaluate() const override;
0232 };
0233 
0234 class FunNumber : public Function
0235 {
0236 private:
0237     Value doEvaluate() const override;
0238 };
0239 
0240 class FunSum : public Function
0241 {
0242 private:
0243     Value doEvaluate() const override;
0244 };
0245 
0246 class FunFloor : public Function
0247 {
0248 private:
0249     Value doEvaluate() const override;
0250 };
0251 
0252 class FunCeiling : public Function
0253 {
0254 private:
0255     Value doEvaluate() const override;
0256 };
0257 
0258 class FunRound : public Function
0259 {
0260 private:
0261     Value doEvaluate() const override;
0262 };
0263 
0264 DEFINE_FUNCTION_CREATOR(FunLast)
0265 DEFINE_FUNCTION_CREATOR(FunPosition)
0266 DEFINE_FUNCTION_CREATOR(FunCount)
0267 DEFINE_FUNCTION_CREATOR(FunLocalName)
0268 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
0269 DEFINE_FUNCTION_CREATOR(FunName)
0270 DEFINE_FUNCTION_CREATOR(FunId)
0271 
0272 DEFINE_FUNCTION_CREATOR(FunString)
0273 DEFINE_FUNCTION_CREATOR(FunConcat)
0274 DEFINE_FUNCTION_CREATOR(FunStartsWith)
0275 DEFINE_FUNCTION_CREATOR(FunContains)
0276 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
0277 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
0278 DEFINE_FUNCTION_CREATOR(FunSubstring)
0279 DEFINE_FUNCTION_CREATOR(FunStringLength)
0280 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
0281 DEFINE_FUNCTION_CREATOR(FunTranslate)
0282 
0283 DEFINE_FUNCTION_CREATOR(FunBoolean)
0284 DEFINE_FUNCTION_CREATOR(FunNot)
0285 DEFINE_FUNCTION_CREATOR(FunTrue)
0286 DEFINE_FUNCTION_CREATOR(FunFalse)
0287 DEFINE_FUNCTION_CREATOR(FunLang)
0288 
0289 DEFINE_FUNCTION_CREATOR(FunNumber)
0290 DEFINE_FUNCTION_CREATOR(FunSum)
0291 DEFINE_FUNCTION_CREATOR(FunFloor)
0292 DEFINE_FUNCTION_CREATOR(FunCeiling)
0293 DEFINE_FUNCTION_CREATOR(FunRound)
0294 
0295 #undef DEFINE_FUNCTION_CREATOR
0296 
0297 Interval::Interval()
0298     : m_min(Inf),
0299       m_max(Inf)
0300 {
0301 }
0302 
0303 Interval::Interval(int value)
0304     : m_min(value),
0305       m_max(value)
0306 {
0307 }
0308 
0309 Interval::Interval(int min, int max)
0310     : m_min(min),
0311       m_max(max)
0312 {
0313 }
0314 
0315 bool Interval::contains(int value) const
0316 {
0317     if (m_min == Inf && m_max == Inf) {
0318         return true;
0319     }
0320 
0321     if (m_min == Inf) {
0322         return value <= m_max;
0323     }
0324 
0325     if (m_max == Inf) {
0326         return value >= m_min;
0327     }
0328 
0329     return value >= m_min && value <= m_max;
0330 }
0331 
0332 QString Interval::asString() const
0333 {
0334     QString s = "[";
0335 
0336     if (m_min == Inf) {
0337         s += "-Infinity";
0338     } else {
0339         s += QString::number(m_min);
0340     }
0341 
0342     s += "..";
0343 
0344     if (m_max == Inf) {
0345         s += "Infinity";
0346     } else {
0347         s += QString::number(m_max);
0348     }
0349 
0350     s += "]";
0351 
0352     return s;
0353 }
0354 
0355 void Function::setArguments(const QList<Expression *> &args)
0356 {
0357     foreach (Expression *arg, args) {
0358         addSubExpression(arg);
0359     }
0360 }
0361 
0362 void Function::setName(const DOM::DOMString &name)
0363 {
0364     m_name = name;
0365 }
0366 
0367 QString Function::dump() const
0368 {
0369     if (argCount() == 0) {
0370         return QString("<function name=\"%1\"/>").arg(name().string());
0371     }
0372 
0373     QString s = QString("<function name=\"%1\">").arg(name().string());
0374     for (unsigned int i = 0; i < argCount(); ++i) {
0375         s += "<operand>" + arg(i)->dump() + "</operand>";
0376     }
0377     s += "</function>";
0378     return s;
0379 }
0380 
0381 Expression *Function::arg(int i)
0382 {
0383     return subExpr(i);
0384 }
0385 
0386 const Expression *Function::arg(int i) const
0387 {
0388     return subExpr(i);
0389 }
0390 
0391 unsigned int Function::argCount() const
0392 {
0393     return subExprCount();
0394 }
0395 
0396 DOM::DOMString Function::name() const
0397 {
0398     return m_name;
0399 }
0400 
0401 Value FunLast::doEvaluate() const
0402 {
0403     return Value(double(Expression::evaluationContext().size));
0404 }
0405 
0406 bool FunLast::isConstant() const
0407 {
0408     return false;
0409 }
0410 
0411 Value FunPosition::doEvaluate() const
0412 {
0413     return Value(double(Expression::evaluationContext().position));
0414 }
0415 
0416 bool FunPosition::isConstant() const
0417 {
0418     return false;
0419 }
0420 
0421 Value NodeFunction::doEvaluate() const
0422 {
0423     NodeImpl *node = nullptr;
0424     if (argCount() > 0) {
0425         Value a = arg(0)->evaluate();
0426         if (a.isNodeset() && a.toNodeset()->length()) {
0427             node = a.toNodeset()->first();
0428         }
0429     } else {
0430         // no argument -> default to context node
0431         node = evaluationContext().node;
0432     }
0433 
0434     if (!node) {
0435         return Value(DOMString());
0436     }
0437 
0438     return evaluateOnNode(node);
0439 }
0440 
0441 bool FunLocalName::isConstant() const
0442 {
0443     return false;
0444 }
0445 
0446 Value FunLocalName::evaluateOnNode(DOM::NodeImpl *node) const
0447 {
0448     DOM::DOMString n;
0449     switch (node->nodeType()) {
0450     case Node::PROCESSING_INSTRUCTION_NODE:
0451         n = node->nodeName(); // target name
0452         break;
0453     default:
0454         n = node->localName();
0455     }
0456     return Value(n);
0457 }
0458 
0459 bool FunNamespaceURI::isConstant() const
0460 {
0461     return false;
0462 }
0463 
0464 Value FunNamespaceURI::evaluateOnNode(DOM::NodeImpl *node) const
0465 {
0466     return Value(node->namespaceURI());
0467 }
0468 
0469 Value FunId::doEvaluate() const
0470 {
0471     Value a = arg(0)->evaluate();
0472 
0473     WTF::Vector<DOM::DOMString> ids;
0474 
0475     QString queryString; // whitespace-separated IDs
0476     if (a.isNodeset()) {
0477         DomNodeList set = a.toNodeset();
0478         for (unsigned long i = 0; i < set->length(); ++i) {
0479             queryString += stringValue(set->item(i)).string() + QLatin1Char(' ');
0480         }
0481     } else {
0482         queryString = a.toString().string();
0483     }
0484 
0485     QStringList qids = queryString.simplified().split(' ');
0486     for (int i = 0; i < qids.size(); ++i) {
0487         ids.append(DOM::DOMString(qids[i]));
0488     }
0489 
0490     DomNodeList out = new StaticNodeListImpl();
0491     DOM::DocumentImpl *doc = Expression::evaluationContext().node->document();
0492 
0493     for (unsigned i = 0; i < ids.size(); ++i) {
0494         DOM::ElementImpl *e = doc->getElementById(ids[i]);
0495 
0496         if (e) {
0497             out->append(e);
0498         }
0499     }
0500 
0501     return Value(out);
0502 }
0503 
0504 bool FunName::isConstant() const
0505 {
0506     return false;
0507 }
0508 
0509 Value FunName::evaluateOnNode(DOM::NodeImpl *node) const
0510 {
0511     DOM::DOMString n;
0512     switch (node->nodeType()) {
0513     case Node::TEXT_NODE:
0514     case Node::CDATA_SECTION_NODE:
0515     case Node::COMMENT_NODE:
0516     case Node::DOCUMENT_NODE:
0517         // All of these have an empty XPath name
0518         break;
0519     case Node::ELEMENT_NODE: {
0520         n = static_cast<DOM::ElementImpl *>(node)->nonCaseFoldedTagName();
0521         break;
0522     }
0523     default:
0524         n = node->nodeName();
0525     }
0526     return Value(n);
0527 }
0528 
0529 Value FunCount::doEvaluate() const
0530 {
0531     Value a = arg(0)->evaluate();
0532     if (!a.isNodeset()) {
0533         Expression::reportInvalidExpressionErr();
0534         qCWarning(KHTML_LOG) << "count() expects <nodeset>";
0535         return Value();
0536     }
0537     a.toNodeset()->normalizeUpto(StaticNodeListImpl::AxisOrder);
0538 
0539     return Value(double(a.toNodeset()->length()));
0540 }
0541 
0542 bool FunCount::isConstant() const
0543 {
0544     return false;
0545 }
0546 
0547 Value FunString::doEvaluate() const
0548 {
0549     if (argCount() == 0) {
0550         DOMString s = Value(Expression::evaluationContext().node).toString();
0551         return Value(s);
0552     }
0553     return Value(arg(0)->evaluate().toString());
0554 }
0555 
0556 Value FunConcat::doEvaluate() const
0557 {
0558     QString str;
0559     for (unsigned int i = 0; i < argCount(); ++i) {
0560         str.append(arg(i)->evaluate().toString().string());
0561     }
0562     return Value(DOMString(str));
0563 }
0564 
0565 Value FunStartsWith::doEvaluate() const
0566 {
0567     DOMString s1 = arg(0)->evaluate().toString();
0568     DOMString s2 = arg(1)->evaluate().toString();
0569 
0570     if (s2.isEmpty()) {
0571         return Value(true);
0572     }
0573 
0574     return Value(s1.startsWith(s2));
0575 }
0576 
0577 Value FunContains::doEvaluate() const
0578 {
0579     QString s1 = arg(0)->evaluate().toString().string();
0580     QString s2 = arg(1)->evaluate().toString().string();
0581 
0582     if (s2.isEmpty()) {
0583         return Value(true);
0584     }
0585 
0586     return Value(s1.contains(s2));
0587 }
0588 
0589 Value FunSubstringBefore::doEvaluate() const
0590 {
0591     QString s1 = arg(0)->evaluate().toString().string();
0592     QString s2 = arg(1)->evaluate().toString().string();
0593 
0594     if (s2.isEmpty()) {
0595         return Value(DOMString());
0596     }
0597 
0598     int i = s1.indexOf(s2);
0599     if (i == -1) {
0600         return Value(DOMString());
0601     }
0602 
0603     return Value(DOMString(s1.left(i)));
0604 }
0605 
0606 Value FunSubstringAfter::doEvaluate() const
0607 {
0608     QString s1 = arg(0)->evaluate().toString().string();
0609     QString s2 = arg(1)->evaluate().toString().string();
0610 
0611     if (s2.isEmpty()) {
0612         return Value(s1);
0613     }
0614 
0615     int i = s1.indexOf(s2);
0616     if (i == -1) {
0617         return Value(DOMString());
0618     }
0619 
0620     return Value(DOMString(s1.mid(i + s2.length())));
0621 }
0622 
0623 Value FunSubstring::doEvaluate() const
0624 {
0625     QString s = arg(0)->evaluate().toString().string();
0626     long pos = long(qRound(arg(1)->evaluate().toNumber()));
0627     bool haveLength = argCount() == 3;
0628     long len = -1;
0629     if (haveLength) {
0630         len = long(qRound(arg(2)->evaluate().toNumber()));
0631     }
0632 
0633     if (pos > long(s.length())) {
0634         return Value(DOMString());
0635     }
0636 
0637     if (haveLength && pos < 1) {
0638         len -= 1 - pos;
0639         pos = 1;
0640         if (len < 1) {
0641             return Value(DOMString());
0642         }
0643     }
0644 
0645     return Value(DOMString(s.mid(pos - 1, len)));
0646 }
0647 
0648 Value FunStringLength::doEvaluate() const
0649 {
0650     if (argCount() == 0) {
0651         DOMString s = Value(Expression::evaluationContext().node).toString();
0652         return Value(double(s.length()));
0653     }
0654 
0655     return Value(double(arg(0)->evaluate().toString().length()));
0656 }
0657 
0658 Value FunNormalizeSpace::doEvaluate() const
0659 {
0660     if (argCount() == 0) {
0661         DOMString s = Value(Expression::evaluationContext().node).toString();
0662         return Value(DOMString(s.string().simplified()));
0663     }
0664 
0665     QString s = arg(0)->evaluate().toString().string();
0666     s = s.simplified();
0667     return Value(DOMString(s));
0668 }
0669 
0670 Value FunTranslate::doEvaluate() const
0671 {
0672     QString s1 = arg(0)->evaluate().toString().string();
0673     QString s2 = arg(1)->evaluate().toString().string();
0674     QString s3 = arg(2)->evaluate().toString().string();
0675     QString newString;
0676 
0677     for (int i1 = 0; i1 < s1.length(); ++i1) {
0678         QChar ch = s1[ i1 ];
0679         int i2 = s2.indexOf(ch);
0680         if (i2 == -1) {
0681             newString += ch;
0682         } else if (i2 < s3.length()) {
0683             newString += s3[ i2 ];
0684         }
0685     }
0686 
0687     return Value(DOMString(newString));
0688 }
0689 
0690 Value FunBoolean::doEvaluate() const
0691 {
0692     return Value(arg(0)->evaluate().toBoolean());
0693 }
0694 
0695 Value FunNot::doEvaluate() const
0696 {
0697     return Value(!arg(0)->evaluate().toBoolean());
0698 }
0699 
0700 Value FunTrue::doEvaluate() const
0701 {
0702     return Value(true);
0703 }
0704 
0705 bool FunTrue::isConstant() const
0706 {
0707     return true;
0708 }
0709 
0710 #ifdef __GNUC__
0711 #warning "This looks bogus"
0712 #endif
0713 
0714 Value FunLang::doEvaluate() const
0715 {
0716     QString lang = arg(0)->evaluate().toString().string();
0717 
0718     NodeImpl *node = evaluationContext().node;
0719 
0720     DOMString langNodeValue;
0721 
0722     while (node) {
0723         if (node->isElementNode()) {
0724             langNodeValue = static_cast<ElementImpl *>(node)->getAttribute("xml:lang");
0725             if (!langNodeValue.isNull()) {
0726                 break;
0727             }
0728         }
0729         node = xpathParentNode(node);
0730     }
0731 
0732     if (langNodeValue.isNull()) {
0733         return Value(false);
0734     }
0735 
0736     // extract 'en' out of 'en-us'
0737     QString langNodeValueString = langNodeValue.string();
0738     QString langNodeBaseString = langNodeValueString.left(langNodeValueString.indexOf('-'));
0739 
0740     return Value(langNodeValueString.toLower() == lang.toLower() ||
0741                  langNodeBaseString.toLower()  == lang.toLower());
0742 }
0743 
0744 bool FunLang::isConstant() const
0745 {
0746     return false;
0747 }
0748 
0749 Value FunFalse::doEvaluate() const
0750 {
0751     return Value(false);
0752 }
0753 
0754 bool FunFalse::isConstant() const
0755 {
0756     return true;
0757 }
0758 
0759 Value FunNumber::doEvaluate() const
0760 {
0761     Value vi;
0762     if (argCount() == 0) {
0763         // Spec'd: convert context node to singleton nodeset, call
0764         // string on that --> that's just stringValue on that node.
0765         // then we call number on that string
0766         vi = Value(stringValue(evaluationContext().node));
0767     } else {
0768         vi = arg(0)->evaluate();
0769     }
0770 
0771     return Value(vi.toNumber());
0772 }
0773 
0774 Value FunSum::doEvaluate() const
0775 {
0776     Value a = arg(0)->evaluate();
0777     if (!a.isNodeset()) {
0778         Expression::reportInvalidExpressionErr();
0779         qCWarning(KHTML_LOG) << "sum() expects <nodeset>";
0780         return Value(0.0);
0781     }
0782 
0783     double sum = 0.0;
0784     const DomNodeList nodes = a.toNodeset();
0785     for (unsigned long n = 0; n < nodes->length(); ++n) {
0786         NodeImpl *node = nodes->item(n);
0787         sum += Value(stringValue(node)).toNumber();
0788     }
0789     return Value(sum);
0790 }
0791 
0792 Value FunFloor::doEvaluate() const
0793 {
0794     const double num = arg(0)->evaluate().toNumber();
0795 
0796     if (KJS::isNaN(num) || KJS::isInf(num)) {
0797         return Value(num);
0798     }
0799 
0800     return Value(floor(num));
0801 }
0802 
0803 Value FunCeiling::doEvaluate() const
0804 {
0805     const double num = arg(0)->evaluate().toNumber();
0806 
0807     if (KJS::isNaN(num) || KJS::isInf(num)) {
0808         return Value(num);
0809     }
0810 
0811     return Value(ceil(num));
0812 }
0813 
0814 Value FunRound::doEvaluate() const
0815 {
0816     return Value(double(qRound(arg(0)->evaluate().toNumber())));
0817 }
0818 
0819 struct FunctionLibrary::FunctionRec {
0820     typedef Function *(*FactoryFn)();
0821 
0822     FactoryFn factoryFn;
0823     Interval args;
0824 };
0825 
0826 struct FunctionMapping {
0827     const char *name;
0828     FunctionLibrary::FunctionRec function;
0829 };
0830 
0831 static FunctionMapping functions[] = {
0832     { "last", { &createFunLast, 0 } },
0833     { "last", { &createFunLast, 0 } },
0834     { "position", { &createFunPosition, 0 } },
0835     { "count", { &createFunCount, 1 } },
0836     { "sum", { &createFunSum, 1 } },
0837     { "local-name", { &createFunLocalName, Interval(0, 1) } },
0838     { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
0839     { "id",   { &createFunId, 1 } },
0840     { "name", { &createFunName, Interval(0, 1) } },
0841 
0842     { "string", { &createFunString, Interval(0, 1) } },
0843     { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
0844     { "starts-with", { &createFunStartsWith, 2 } },
0845     { "contains", { &createFunContains, 2 } },
0846     { "substring-before", { &createFunSubstringBefore, 2 } },
0847     { "substring-after", { &createFunSubstringAfter, 2 } },
0848     { "substring", { &createFunSubstring, Interval(2, 3) } },
0849     { "string-length", { &createFunStringLength, Interval(0, 1) } },
0850     { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
0851     { "translate", { &createFunTranslate, 3 } },
0852 
0853     { "boolean", { &createFunBoolean, 1 } },
0854     { "not", { &createFunNot, 1 } },
0855     { "true", { &createFunTrue, 0 } },
0856     { "false", { &createFunFalse, 0 } },
0857     { "lang", { &createFunLang, 1 } },
0858 
0859     { "number", { &createFunNumber, Interval(0, 1) } },
0860     { "floor", { &createFunFloor, 1 } },
0861     { "ceiling", { &createFunCeiling, 1 } },
0862     { "round", { &createFunRound, 1 } }
0863 };
0864 static const unsigned int numFunctions = sizeof(functions) / sizeof(functions[ 0 ]);
0865 
0866 FunctionLibrary &FunctionLibrary::self()
0867 {
0868     static FunctionLibrary instance;
0869     return instance;
0870 }
0871 
0872 FunctionLibrary::FunctionLibrary()
0873 {
0874     for (unsigned int i = 0; i < numFunctions; ++i) {
0875         m_functionDict.insert(functions[ i ].name, functions[ i ].function);
0876     }
0877 }
0878 
0879 Function *FunctionLibrary::getFunction(const DOM::DOMString &name,
0880                                        const QList<Expression *> &args) const
0881 {
0882     if (!m_functionDict.contains(name)) {
0883         qCWarning(KHTML_LOG) << "Function '" << name << "' not supported by this implementation.";
0884 
0885         return nullptr;
0886     }
0887 
0888     FunctionRec functionRec = m_functionDict[ name ];
0889     if (!functionRec.args.contains(args.count())) {
0890         qCWarning(KHTML_LOG) << "Function '" << name << "' requires " << functionRec.args.asString() << " arguments, but " << args.count() << " given.";
0891         return nullptr;
0892     }
0893 
0894     Function *function = functionRec.factoryFn();
0895     function->setArguments(args);
0896     function->setName(name);
0897     return function;
0898 }
0899 
0900 } //namespace XPath
0901 } //namespace khtml
0902