summaryrefslogtreecommitdiffstats
path: root/WebCore/xml/XPathFunctions.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/xml/XPathFunctions.cpp
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/xml/XPathFunctions.cpp')
-rw-r--r--WebCore/xml/XPathFunctions.cpp683
1 files changed, 683 insertions, 0 deletions
diff --git a/WebCore/xml/XPathFunctions.cpp b/WebCore/xml/XPathFunctions.cpp
new file mode 100644
index 0000000..841b436
--- /dev/null
+++ b/WebCore/xml/XPathFunctions.cpp
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2005 Frerich Raabe <raabe@kde.org>
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "XPathFunctions.h"
+
+#if ENABLE(XPATH)
+
+#include "Document.h"
+#include "Element.h"
+#include "NamedAttrMap.h"
+#include "XMLNames.h"
+#include "XPathUtil.h"
+#include "XPathValue.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+namespace XPath {
+
+static inline bool isWhitespace(UChar c)
+{
+ return c == ' ' || c == '\n' || c == '\r' || c == '\t';
+}
+
+
+#define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; }
+
+class Interval {
+public:
+ static const int Inf = -1;
+
+ Interval();
+ Interval(int value);
+ Interval(int min, int max);
+
+ bool contains(int value) const;
+
+private:
+ int m_min;
+ int m_max;
+};
+
+struct FunctionRec {
+ typedef Function *(*FactoryFn)();
+ FactoryFn factoryFn;
+ Interval args;
+};
+
+static HashMap<String, FunctionRec>* functionMap;
+
+class FunLast : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunPosition : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunCount : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunId : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunLocalName : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunNamespaceURI : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunName : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunString : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunConcat : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunStartsWith : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunContains : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunSubstringBefore : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunSubstringAfter : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunSubstring : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunStringLength : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunNormalizeSpace : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunTranslate : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunBoolean : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunNot : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunTrue : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunFalse : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunLang : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunNumber : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunSum : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunFloor : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunCeiling : public Function {
+ virtual Value evaluate() const;
+};
+
+class FunRound : public Function {
+ virtual Value evaluate() const;
+public:
+ static double round(double);
+};
+
+DEFINE_FUNCTION_CREATOR(FunLast)
+DEFINE_FUNCTION_CREATOR(FunPosition)
+DEFINE_FUNCTION_CREATOR(FunCount)
+DEFINE_FUNCTION_CREATOR(FunId)
+DEFINE_FUNCTION_CREATOR(FunLocalName)
+DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
+DEFINE_FUNCTION_CREATOR(FunName)
+
+DEFINE_FUNCTION_CREATOR(FunString)
+DEFINE_FUNCTION_CREATOR(FunConcat)
+DEFINE_FUNCTION_CREATOR(FunStartsWith)
+DEFINE_FUNCTION_CREATOR(FunContains)
+DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
+DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
+DEFINE_FUNCTION_CREATOR(FunSubstring)
+DEFINE_FUNCTION_CREATOR(FunStringLength)
+DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
+DEFINE_FUNCTION_CREATOR(FunTranslate)
+
+DEFINE_FUNCTION_CREATOR(FunBoolean)
+DEFINE_FUNCTION_CREATOR(FunNot)
+DEFINE_FUNCTION_CREATOR(FunTrue)
+DEFINE_FUNCTION_CREATOR(FunFalse)
+DEFINE_FUNCTION_CREATOR(FunLang)
+
+DEFINE_FUNCTION_CREATOR(FunNumber)
+DEFINE_FUNCTION_CREATOR(FunSum)
+DEFINE_FUNCTION_CREATOR(FunFloor)
+DEFINE_FUNCTION_CREATOR(FunCeiling)
+DEFINE_FUNCTION_CREATOR(FunRound)
+
+#undef DEFINE_FUNCTION_CREATOR
+
+inline Interval::Interval()
+ : m_min(Inf), m_max(Inf)
+{
+}
+
+inline Interval::Interval(int value)
+ : m_min(value), m_max(value)
+{
+}
+
+inline Interval::Interval(int min, int max)
+ : m_min(min), m_max(max)
+{
+}
+
+inline bool Interval::contains(int value) const
+{
+ if (m_min == Inf && m_max == Inf)
+ return true;
+
+ if (m_min == Inf)
+ return value <= m_max;
+
+ if (m_max == Inf)
+ return value >= m_min;
+
+ return value >= m_min && value <= m_max;
+}
+
+void Function::setArguments(const Vector<Expression*>& args)
+{
+ Vector<Expression*>::const_iterator end = args.end();
+
+ for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++)
+ addSubExpression(*it);
+}
+
+Value FunLast::evaluate() const
+{
+ return Expression::evaluationContext().size;
+}
+
+Value FunPosition::evaluate() const
+{
+ return Expression::evaluationContext().position;
+}
+
+Value FunId::evaluate() const
+{
+ Value a = arg(0)->evaluate();
+ Vector<UChar> idList; // A whitespace-separated list of IDs
+
+ if (a.isNodeSet()) {
+ const NodeSet& nodes = a.toNodeSet();
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ String str = stringValue(nodes[i]);
+ idList.append(str.characters(), str.length());
+ idList.append(' ');
+ }
+ } else {
+ String str = a.toString();
+ idList.append(str.characters(), str.length());
+ }
+
+ Document* contextDocument = evaluationContext().node->document();
+ NodeSet result;
+ HashSet<Node*> resultSet;
+
+ size_t startPos = 0;
+ size_t length = idList.size();
+ while (true) {
+ while (startPos < length && isWhitespace(idList[startPos]))
+ ++startPos;
+
+ if (startPos == length)
+ break;
+
+ size_t endPos = startPos;
+ while (endPos < length && !isWhitespace(idList[endPos]))
+ ++endPos;
+
+ // If there are several nodes with the same id, id() should return the first one.
+ // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
+ Node* node = contextDocument->getElementById(String(&idList[startPos], endPos - startPos));
+ if (node && resultSet.add(node).second)
+ result.append(node);
+
+ startPos = endPos;
+ }
+
+ result.markSorted(false);
+
+ return Value(result, Value::adopt);
+}
+
+Value FunLocalName::evaluate() const
+{
+ Node* node = 0;
+ if (argCount() > 0) {
+ Value a = arg(0)->evaluate();
+ if (!a.isNodeSet())
+ return "";
+
+ node = a.toNodeSet().firstNode();
+ if (!node)
+ return "";
+ }
+
+ if (!node)
+ node = evaluationContext().node.get();
+
+ return node->localName().string();
+}
+
+Value FunNamespaceURI::evaluate() const
+{
+ Node* node = 0;
+ if (argCount() > 0) {
+ Value a = arg(0)->evaluate();
+ if (!a.isNodeSet())
+ return "";
+
+ node = a.toNodeSet().firstNode();
+ if (!node)
+ return "";
+ }
+
+ if (!node)
+ node = evaluationContext().node.get();
+
+ return node->namespaceURI().string();
+}
+
+Value FunName::evaluate() const
+{
+ Node* node = 0;
+ if (argCount() > 0) {
+ Value a = arg(0)->evaluate();
+ if (!a.isNodeSet())
+ return "";
+
+ node = a.toNodeSet().firstNode();
+ if (!node)
+ return "";
+ }
+
+ if (!node)
+ node = evaluationContext().node.get();
+
+ const AtomicString& prefix = node->prefix();
+ return prefix.isEmpty() ? node->localName().string() : prefix + ":" + node->localName();
+}
+
+Value FunCount::evaluate() const
+{
+ Value a = arg(0)->evaluate();
+
+ if (!a.isNodeSet())
+ return 0.0;
+
+ return double(a.toNodeSet().size());
+}
+
+Value FunString::evaluate() const
+{
+ if (!argCount())
+ return Value(Expression::evaluationContext().node.get()).toString();
+ return arg(0)->evaluate().toString();
+}
+
+Value FunConcat::evaluate() const
+{
+ Vector<UChar, 1024> result;
+
+ unsigned count = argCount();
+ for (unsigned i = 0; i < count; ++i) {
+ String str(arg(i)->evaluate().toString());
+ result.append(str.characters(), str.length());
+ }
+
+ return String(result.data(), result.size());
+}
+
+Value FunStartsWith::evaluate() const
+{
+ String s1 = arg(0)->evaluate().toString();
+ String s2 = arg(1)->evaluate().toString();
+
+ if (s2.isEmpty())
+ return true;
+
+ return s1.startsWith(s2);
+}
+
+Value FunContains::evaluate() const
+{
+ String s1 = arg(0)->evaluate().toString();
+ String s2 = arg(1)->evaluate().toString();
+
+ if (s2.isEmpty())
+ return true;
+
+ return s1.contains(s2) != 0;
+}
+
+Value FunSubstringBefore::evaluate() const
+{
+ String s1 = arg(0)->evaluate().toString();
+ String s2 = arg(1)->evaluate().toString();
+
+ if (s2.isEmpty())
+ return "";
+
+ int i = s1.find(s2);
+
+ if (i == -1)
+ return "";
+
+ return s1.left(i);
+}
+
+Value FunSubstringAfter::evaluate() const
+{
+ String s1 = arg(0)->evaluate().toString();
+ String s2 = arg(1)->evaluate().toString();
+
+ int i = s1.find(s2);
+ if (i == -1)
+ return "";
+
+ return s1.substring(i + s2.length());
+}
+
+Value FunSubstring::evaluate() const
+{
+ String s = arg(0)->evaluate().toString();
+ long pos = static_cast<long>(FunRound::round(arg(1)->evaluate().toNumber()));
+ bool haveLength = argCount() == 3;
+ long len = -1;
+ if (haveLength) {
+ double doubleLen = arg(2)->evaluate().toNumber();
+ if (isnan(doubleLen))
+ return "";
+ len = static_cast<long>(FunRound::round(doubleLen));
+ }
+
+ if (pos > long(s.length()))
+ return "";
+
+ if (haveLength && pos < 1) {
+ len -= 1 - pos;
+ pos = 1;
+ if (len < 1)
+ return "";
+ }
+
+ return s.substring(pos - 1, len);
+}
+
+Value FunStringLength::evaluate() const
+{
+ if (!argCount())
+ return Value(Expression::evaluationContext().node.get()).toString().length();
+ return arg(0)->evaluate().toString().length();
+}
+
+Value FunNormalizeSpace::evaluate() const
+{
+ if (!argCount()) {
+ String s = Value(Expression::evaluationContext().node.get()).toString();
+ return s.simplifyWhiteSpace();
+ }
+
+ String s = arg(0)->evaluate().toString();
+ return s.simplifyWhiteSpace();
+}
+
+Value FunTranslate::evaluate() const
+{
+ String s1 = arg(0)->evaluate().toString();
+ String s2 = arg(1)->evaluate().toString();
+ String s3 = arg(2)->evaluate().toString();
+ String newString;
+
+ // FIXME: Building a String a character at a time is quite slow.
+ for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
+ UChar ch = s1[i1];
+ int i2 = s2.find(ch);
+
+ if (i2 == -1)
+ newString += String(&ch, 1);
+ else if ((unsigned)i2 < s3.length()) {
+ UChar c2 = s3[i2];
+ newString += String(&c2, 1);
+ }
+ }
+
+ return newString;
+}
+
+Value FunBoolean::evaluate() const
+{
+ return arg(0)->evaluate().toBoolean();
+}
+
+Value FunNot::evaluate() const
+{
+ return !arg(0)->evaluate().toBoolean();
+}
+
+Value FunTrue::evaluate() const
+{
+ return true;
+}
+
+Value FunLang::evaluate() const
+{
+ String lang = arg(0)->evaluate().toString();
+
+ RefPtr<Node> langNode = 0;
+ Node* node = evaluationContext().node.get();
+ while (node) {
+ NamedAttrMap* attrs = node->attributes();
+ if (attrs)
+ langNode = attrs->getNamedItemNS(XMLNames::xmlNamespaceURI, "lang");
+ if (langNode)
+ break;
+ node = node->parentNode();
+ }
+
+ if (!langNode)
+ return false;
+
+ String langNodeValue = langNode->nodeValue();
+ while (true) {
+ if (equalIgnoringCase(langNodeValue, lang))
+ return true;
+
+ // Remove suffixes one by one.
+ int index = langNodeValue.reverseFind('-');
+ if (index == -1)
+ break;
+ langNodeValue = langNodeValue.left(index);
+ }
+
+ return false;
+}
+
+Value FunFalse::evaluate() const
+{
+ return false;
+}
+
+Value FunNumber::evaluate() const
+{
+ if (!argCount())
+ return Value(Expression::evaluationContext().node.get()).toNumber();
+ return arg(0)->evaluate().toNumber();
+}
+
+Value FunSum::evaluate() const
+{
+ Value a = arg(0)->evaluate();
+ if (!a.isNodeSet())
+ return 0.0;
+
+ double sum = 0.0;
+ const NodeSet& nodes = a.toNodeSet();
+ // To be really compliant, we should sort the node-set, as floating point addition is not associative.
+ // However, this is unlikely to ever become a practical issue, and sorting is slow.
+
+ for (unsigned i = 0; i < nodes.size(); i++)
+ sum += Value(stringValue(nodes[i])).toNumber();
+
+ return sum;
+}
+
+Value FunFloor::evaluate() const
+{
+ return floor(arg(0)->evaluate().toNumber());
+}
+
+Value FunCeiling::evaluate() const
+{
+ return ceil(arg(0)->evaluate().toNumber());
+}
+
+double FunRound::round(double val)
+{
+ if (!isnan(val) && !isinf(val)) {
+ if (signbit(val) && val >= -0.5)
+ val *= 0; // negative zero
+ else
+ val = floor(val + 0.5);
+ }
+ return val;
+}
+
+Value FunRound::evaluate() const
+{
+ return round(arg(0)->evaluate().toNumber());
+}
+
+static void createFunctionMap()
+{
+ struct FunctionMapping {
+ const char *name;
+ FunctionRec function;
+ };
+ static const FunctionMapping functions[] = {
+ { "boolean", { &createFunBoolean, 1 } },
+ { "ceiling", { &createFunCeiling, 1 } },
+ { "concat", { &createFunConcat, Interval(2, Interval::Inf) } },
+ { "contains", { &createFunContains, 2 } },
+ { "count", { &createFunCount, 1 } },
+ { "false", { &createFunFalse, 0 } },
+ { "floor", { &createFunFloor, 1 } },
+ { "id", { &createFunId, 1 } },
+ { "lang", { &createFunLang, 1 } },
+ { "last", { &createFunLast, 0 } },
+ { "local-name", { &createFunLocalName, Interval(0, 1) } },
+ { "name", { &createFunName, Interval(0, 1) } },
+ { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } },
+ { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } },
+ { "not", { &createFunNot, 1 } },
+ { "number", { &createFunNumber, Interval(0, 1) } },
+ { "position", { &createFunPosition, 0 } },
+ { "round", { &createFunRound, 1 } },
+ { "starts-with", { &createFunStartsWith, 2 } },
+ { "string", { &createFunString, Interval(0, 1) } },
+ { "string-length", { &createFunStringLength, Interval(0, 1) } },
+ { "substring", { &createFunSubstring, Interval(2, 3) } },
+ { "substring-after", { &createFunSubstringAfter, 2 } },
+ { "substring-before", { &createFunSubstringBefore, 2 } },
+ { "sum", { &createFunSum, 1 } },
+ { "translate", { &createFunTranslate, 3 } },
+ { "true", { &createFunTrue, 0 } },
+ };
+ const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]);
+
+ functionMap = new HashMap<String, FunctionRec>;
+ for (unsigned i = 0; i < numFunctions; ++i)
+ functionMap->set(functions[i].name, functions[i].function);
+}
+
+Function* createFunction(const String& name, const Vector<Expression*>& args)
+{
+ if (!functionMap)
+ createFunctionMap();
+
+ HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name);
+ FunctionRec* functionRec = 0;
+
+ if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->second)->args.contains(args.size()))
+ return 0;
+
+ Function* function = functionRec->factoryFn();
+ function->setArguments(args);
+ function->setName(name);
+ return function;
+}
+
+}
+}
+
+#endif // ENABLE(XPATH)