diff options
Diffstat (limited to 'Source/WebCore/xml/XPathPredicate.cpp')
-rw-r--r-- | Source/WebCore/xml/XPathPredicate.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/Source/WebCore/xml/XPathPredicate.cpp b/Source/WebCore/xml/XPathPredicate.cpp new file mode 100644 index 0000000..2a6482f --- /dev/null +++ b/Source/WebCore/xml/XPathPredicate.cpp @@ -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(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, 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 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 lhsBool; + + return 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) |