/*
* 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)