1 /*
2 * Copyright 2005 Frerich Raabe <raabe@kde.org>
3 * Copyright (C) 2006 Apple Computer, Inc.
4 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29
30 #if ENABLE(XPATH)
31
32 #include "XPathPredicate.h"
33
34 #include "Node.h"
35 #include "XPathFunctions.h"
36 #include "XPathUtil.h"
37 #include "XPathValue.h"
38 #include <math.h>
39 #include <wtf/MathExtras.h>
40
41 namespace WebCore {
42 namespace XPath {
43
Number(double value)44 Number::Number(double value)
45 : m_value(value)
46 {
47 }
48
evaluate() const49 Value Number::evaluate() const
50 {
51 return m_value;
52 }
53
StringExpression(const String & value)54 StringExpression::StringExpression(const String& value)
55 : m_value(value)
56 {
57 }
58
evaluate() const59 Value StringExpression::evaluate() const
60 {
61 return m_value;
62 }
63
evaluate() const64 Value Negative::evaluate() const
65 {
66 Value p(subExpr(0)->evaluate());
67 return -p.toNumber();
68 }
69
NumericOp(Opcode opcode,Expression * lhs,Expression * rhs)70 NumericOp::NumericOp(Opcode opcode, Expression* lhs, Expression* rhs)
71 : m_opcode(opcode)
72 {
73 addSubExpression(lhs);
74 addSubExpression(rhs);
75 }
76
evaluate() const77 Value NumericOp::evaluate() const
78 {
79 Value lhs(subExpr(0)->evaluate());
80 Value rhs(subExpr(1)->evaluate());
81
82 double leftVal = lhs.toNumber();
83 double rightVal = rhs.toNumber();
84
85 switch (m_opcode) {
86 case OP_Add:
87 return leftVal + rightVal;
88 case OP_Sub:
89 return leftVal - rightVal;
90 case OP_Mul:
91 return leftVal * rightVal;
92 case OP_Div:
93 return leftVal / rightVal;
94 case OP_Mod:
95 return fmod(leftVal, rightVal);
96 }
97 ASSERT_NOT_REACHED();
98 return 0.0;
99 }
100
EqTestOp(Opcode opcode,Expression * lhs,Expression * rhs)101 EqTestOp::EqTestOp(Opcode opcode, Expression* lhs, Expression* rhs)
102 : m_opcode(opcode)
103 {
104 addSubExpression(lhs);
105 addSubExpression(rhs);
106 }
107
compare(const Value & lhs,const Value & rhs) const108 bool EqTestOp::compare(const Value& lhs, const Value& rhs) const
109 {
110 if (lhs.isNodeSet()) {
111 const NodeSet& lhsSet = lhs.toNodeSet();
112 if (rhs.isNodeSet()) {
113 // If both objects to be compared are node-sets, then the comparison will be true if and only if
114 // there is a node in the first node-set and a node in the second node-set such that the result of
115 // performing the comparison on the string-values of the two nodes is true.
116 const NodeSet& rhsSet = rhs.toNodeSet();
117 for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
118 for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
119 if (compare(stringValue(lhsSet[lindex]), stringValue(rhsSet[rindex])))
120 return true;
121 return false;
122 }
123 if (rhs.isNumber()) {
124 // If one object to be compared is a node-set and the other is a number, then the comparison will be true
125 // if and only if there is a node in the node-set such that the result of performing the comparison on the number
126 // to be compared and on the result of converting the string-value of that node to a number using the number function is true.
127 for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
128 if (compare(Value(stringValue(lhsSet[lindex])).toNumber(), rhs))
129 return true;
130 return false;
131 }
132 if (rhs.isString()) {
133 // If one object to be compared is a node-set and the other is a string, then the comparison will be true
134 // if and only if there is a node in the node-set such that the result of performing the comparison on
135 // the string-value of the node and the other string is true.
136 for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex)
137 if (compare(stringValue(lhsSet[lindex]), rhs))
138 return true;
139 return false;
140 }
141 if (rhs.isBoolean()) {
142 // If one object to be compared is a node-set and the other is a boolean, then the comparison will be true
143 // if and only if the result of performing the comparison on the boolean and on the result of converting
144 // the node-set to a boolean using the boolean function is true.
145 return compare(lhs.toBoolean(), rhs);
146 }
147 ASSERT(0);
148 }
149 if (rhs.isNodeSet()) {
150 const NodeSet& rhsSet = rhs.toNodeSet();
151 if (lhs.isNumber()) {
152 for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
153 if (compare(lhs, Value(stringValue(rhsSet[rindex])).toNumber()))
154 return true;
155 return false;
156 }
157 if (lhs.isString()) {
158 for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex)
159 if (compare(lhs, stringValue(rhsSet[rindex])))
160 return true;
161 return false;
162 }
163 if (lhs.isBoolean())
164 return compare(lhs, rhs.toBoolean());
165 ASSERT(0);
166 }
167
168 // Neither side is a NodeSet.
169 switch (m_opcode) {
170 case OP_EQ:
171 case OP_NE:
172 bool equal;
173 if (lhs.isBoolean() || rhs.isBoolean())
174 equal = lhs.toBoolean() == rhs.toBoolean();
175 else if (lhs.isNumber() || rhs.isNumber())
176 equal = lhs.toNumber() == rhs.toNumber();
177 else
178 equal = lhs.toString() == rhs.toString();
179
180 if (m_opcode == OP_EQ)
181 return equal;
182 return !equal;
183 case OP_GT:
184 return lhs.toNumber() > rhs.toNumber();
185 case OP_GE:
186 return lhs.toNumber() >= rhs.toNumber();
187 case OP_LT:
188 return lhs.toNumber() < rhs.toNumber();
189 case OP_LE:
190 return lhs.toNumber() <= rhs.toNumber();
191 }
192 ASSERT(0);
193 return false;
194 }
195
evaluate() const196 Value EqTestOp::evaluate() const
197 {
198 Value lhs(subExpr(0)->evaluate());
199 Value rhs(subExpr(1)->evaluate());
200
201 return compare(lhs, rhs);
202 }
203
LogicalOp(Opcode opcode,Expression * lhs,Expression * rhs)204 LogicalOp::LogicalOp(Opcode opcode, Expression* lhs, Expression* rhs)
205 : m_opcode(opcode)
206 {
207 addSubExpression(lhs);
208 addSubExpression(rhs);
209 }
210
shortCircuitOn() const211 bool LogicalOp::shortCircuitOn() const
212 {
213 if (m_opcode == OP_And)
214 return false; //false and foo
215
216 return true; //true or bar
217 }
218
evaluate() const219 Value LogicalOp::evaluate() const
220 {
221 Value lhs(subExpr(0)->evaluate());
222
223 // This is not only an optimization, http://www.w3.org/TR/xpath
224 // dictates that we must do short-circuit evaluation
225 bool lhsBool = lhs.toBoolean();
226 if (lhsBool == shortCircuitOn())
227 return lhsBool;
228
229 return subExpr(1)->evaluate().toBoolean();
230 }
231
evaluate() const232 Value Union::evaluate() const
233 {
234 Value lhsResult = subExpr(0)->evaluate();
235 Value rhs = subExpr(1)->evaluate();
236
237 NodeSet& resultSet = lhsResult.modifiableNodeSet();
238 const NodeSet& rhsNodes = rhs.toNodeSet();
239
240 HashSet<Node*> nodes;
241 for (size_t i = 0; i < resultSet.size(); ++i)
242 nodes.add(resultSet[i]);
243
244 for (size_t i = 0; i < rhsNodes.size(); ++i) {
245 Node* node = rhsNodes[i];
246 if (nodes.add(node).second)
247 resultSet.append(node);
248 }
249
250 // It is also possible to use merge sort to avoid making the result unsorted;
251 // but this would waste the time in cases when order is not important.
252 resultSet.markSorted(false);
253 return lhsResult;
254 }
255
Predicate(Expression * expr)256 Predicate::Predicate(Expression* expr)
257 : m_expr(expr)
258 {
259 }
260
~Predicate()261 Predicate::~Predicate()
262 {
263 delete m_expr;
264 }
265
evaluate() const266 bool Predicate::evaluate() const
267 {
268 ASSERT(m_expr != 0);
269
270 Value result(m_expr->evaluate());
271
272 // foo[3] means foo[position()=3]
273 if (result.isNumber())
274 return EqTestOp(EqTestOp::OP_EQ, createFunction("position"), new Number(result.toNumber())).evaluate().toBoolean();
275
276 return result.toBoolean();
277 }
278
279 }
280 }
281
282 #endif // ENABLE(XPATH)
283