WebCore/xml/XPathPredicate.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/xml/XPathPredicate.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,282 @@
+/*
+ * 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"
+
+#if ENABLE(XPATH)
+
+#include "XPathPredicate.h"
+
+#include "Node.h"
+#include "XPathFunctions.h"
+#include "XPathUtil.h"
+#include "XPathValue.h"
+#include <math.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+namespace XPath {
+        
+Number::Number(double value)
+    : m_value(value)
+{
+}
+
+Value Number::evaluate() const
+{
+    return m_value;
+}
+
+StringExpression::StringExpression(const String& value)
+    : m_value(value)
+{
+}
+
+Value StringExpression::evaluate() const
+{
+    return m_value;
+}
+
+Value Negative::evaluate() const
+{
+    Value p(subExpr(0)->evaluate());
+    return -p.toNumber();
+}
+
+NumericOp::NumericOp(Opcode opcode, Expression* lhs, Expression* rhs)
+    : m_opcode(opcode)
+{
+    addSubExpression(lhs);
+    addSubExpression(rhs);
+}
+
+Value NumericOp::evaluate() const
+{
+    Value lhs(subExpr(0)->evaluate());
+    Value rhs(subExpr(1)->evaluate());
+    
+    double leftVal = lhs.toNumber();
+    double rightVal = rhs.toNumber();
+
+    switch (m_opcode) {
+        case OP_Add:
+            return leftVal + rightVal;
+        case OP_Sub:
+            return leftVal - rightVal;
+        case OP_Mul:
+            return leftVal * rightVal;
+        case OP_Div:
+            return leftVal / rightVal;
+        case OP_Mod:
+            return fmod(leftVal, rightVal);
+    }
+    ASSERT_NOT_REACHED();
+    return 0.0;
+}
+
+EqTestOp::EqTestOp(Opcode opcode, Expression* lhs, Expression* rhs)
+    : m_opcode(opcode)
+{
+    addSubExpression(lhs);
+    addSubExpression(rhs);
+}
+
+bool EqTestOp::compare(const Value& lhs, const Value& rhs) const
+{
+    if (lhs.isNodeSet()) {
+        const NodeSet& lhsSet = lhs.toNodeSet();
+        if (rhs.isNodeSet()) {
+            // If both objects to be compared are node-sets, then the comparison will be true if and only if
+            // there is a node in the first node-set and a node in the second node-set such that the result of
+            // performing the comparison on the string-values of the two nodes is true.
+            const NodeSet& rhsSet = rhs.toNodeSet();
+            for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
+                for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
+                    if (compare(stringValue(lhsSet[lindex]), stringValue(rhsSet[rindex])))
+                        return true;
+            return false;
+        }
+        if (rhs.isNumber()) {
+            // If one object to be compared is a node-set and the other is a number, then the comparison will be true
+            // if and only if there is a node in the node-set such that the result of performing the comparison on the number
+            // to be compared and on the result of converting the string-value of that node to a number using the number function is true.
+            for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
+                if (compare(Value(stringValue(lhsSet[lindex])).toNumber(), rhs))
+                    return true;
+            return false;
+        }
+        if (rhs.isString()) {
+            // If one object to be compared is a node-set and the other is a string, then the comparison will be true
+            // if and only if there is a node in the node-set such that the result of performing the comparison on
+            // the string-value of the node and the other string is true.
+            for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
+                if (compare(stringValue(lhsSet[lindex]), rhs))
+                    return true;
+            return false;
+        }
+        if (rhs.isBoolean()) {
+            // If one object to be compared is a node-set and the other is a boolean, then the comparison will be true
+            // if and only if the result of performing the comparison on the boolean and on the result of converting
+            // the node-set to a boolean using the boolean function is true.
+            return compare((unsigned long)(lhs.toBoolean()), rhs);
+        }
+        ASSERT(0);
+    }
+    if (rhs.isNodeSet()) {
+        const NodeSet& rhsSet = rhs.toNodeSet();
+        if (lhs.isNumber()) {
+            for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
+                if (compare(lhs, Value(stringValue(rhsSet[rindex])).toNumber()))
+                    return true;
+            return false;
+        }
+        if (lhs.isString()) {
+            for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
+                if (compare(lhs, stringValue(rhsSet[rindex])))
+                    return true;
+            return false;
+        }
+        if (lhs.isBoolean())
+            return compare(lhs, (unsigned long)(rhs.toBoolean()));
+        ASSERT(0);
+    }
+    
+    // Neither side is a NodeSet.
+    switch (m_opcode) {
+        case OP_EQ:
+        case OP_NE:
+            bool equal;
+            if (lhs.isBoolean() || rhs.isBoolean())
+                equal = lhs.toBoolean() == rhs.toBoolean();
+            else if (lhs.isNumber() || rhs.isNumber())
+                equal = lhs.toNumber() == rhs.toNumber();
+            else
+                equal = lhs.toString() == rhs.toString();
+
+            if (m_opcode == OP_EQ)
+                return equal;
+            return !equal;
+        case OP_GT:
+            return lhs.toNumber() > rhs.toNumber();
+        case OP_GE:
+            return lhs.toNumber() >= rhs.toNumber();
+        case OP_LT:
+            return lhs.toNumber() < rhs.toNumber();
+        case OP_LE:
+            return lhs.toNumber() <= rhs.toNumber();
+    }
+    ASSERT(0);
+    return false;
+}
+
+Value EqTestOp::evaluate() const
+{
+    Value lhs(subExpr(0)->evaluate());
+    Value rhs(subExpr(1)->evaluate());
+
+    return (unsigned long)compare(lhs, rhs);
+}
+
+LogicalOp::LogicalOp(Opcode opcode, Expression* lhs, Expression* rhs)
+    : m_opcode(opcode)
+{
+    addSubExpression(lhs);
+    addSubExpression(rhs);
+}
+
+bool LogicalOp::shortCircuitOn() const
+{
+    if (m_opcode == OP_And)
+        return false; //false and foo
+
+    return true;  //true or bar
+}
+
+Value LogicalOp::evaluate() const
+{
+    Value lhs(subExpr(0)->evaluate());
+
+    // This is not only an optimization, http://www.w3.org/TR/xpath
+    // dictates that we must do short-circuit evaluation
+    bool lhsBool = lhs.toBoolean();
+    if (lhsBool == shortCircuitOn())
+        return (unsigned long)lhsBool;
+
+    return (unsigned long)(subExpr(1)->evaluate().toBoolean());
+}
+
+Value Union::evaluate() const
+{
+    Value lhsResult = subExpr(0)->evaluate();
+    Value rhs = subExpr(1)->evaluate();
+    
+    NodeSet& resultSet = lhsResult.modifiableNodeSet();
+    const NodeSet& rhsNodes = rhs.toNodeSet();
+    
+    HashSet<Node*> nodes;
+    for (size_t i = 0; i < resultSet.size(); ++i)
+        nodes.add(resultSet[i]);
+    
+    for (size_t i = 0; i < rhsNodes.size(); ++i) {
+        Node* node = rhsNodes[i];
+        if (nodes.add(node).second)
+            resultSet.append(node);
+    }
+
+    // It is also possible to use merge sort to avoid making the result unsorted;
+    // but this would waste the time in cases when order is not important.
+    resultSet.markSorted(false);
+    return lhsResult;
+}
+
+Predicate::Predicate(Expression* expr)
+    : m_expr(expr)
+{
+}
+
+Predicate::~Predicate()
+{
+    delete m_expr;
+}
+
+bool Predicate::evaluate() const
+{
+    ASSERT(m_expr != 0);
+
+    Value result(m_expr->evaluate());
+
+    // foo[3] means foo[position()=3]
+    if (result.isNumber())
+        return EqTestOp(EqTestOp::OP_EQ, createFunction("position"), new Number(result.toNumber())).evaluate().toBoolean();
+
+    return result.toBoolean();
+}
+
+}
+}
+
+#endif // ENABLE(XPATH)