KHTML
predicate.cpp
Go to the documentation of this file.
00001 /* 00002 * predicate.cc - Copyright 2005 Frerich Raabe <raabe@kde.org> 00003 * Copyright 2010 Maksim Orlovich <maksim@kde.org> 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions 00007 * are met: 00008 * 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 00015 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 00016 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00017 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 00018 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 00019 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 00020 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00021 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00022 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00023 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 00024 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00025 */ 00026 #include "predicate.h" 00027 #include "functions.h" 00028 00029 #include <QString> 00030 00031 #include "xml/dom_nodeimpl.h" 00032 #include "xml/dom_nodelistimpl.h" 00033 #include "kjs/operations.h" 00034 #include "kjs/value.h" 00035 00036 #include <math.h> 00037 00038 using namespace DOM; 00039 using namespace khtml; 00040 using namespace khtml::XPath; 00041 00042 Number::Number( double value ) 00043 : m_value( value ) 00044 { 00045 } 00046 00047 bool Number::isConstant() const 00048 { 00049 return true; 00050 } 00051 00052 QString Number::dump() const 00053 { 00054 return "<number>" + QString::number( m_value ) + "</number>"; 00055 } 00056 00057 Value Number::doEvaluate() const 00058 { 00059 return Value( m_value ); 00060 } 00061 00062 String::String( const DOMString &value ) 00063 : m_value( value ) 00064 { 00065 } 00066 00067 bool String::isConstant() const 00068 { 00069 return true; 00070 } 00071 00072 QString String::dump() const 00073 { 00074 return "<string>" + m_value.string() + "</string>"; 00075 } 00076 00077 Value String::doEvaluate() const 00078 { 00079 return Value( m_value ); 00080 } 00081 00082 Value Negative::doEvaluate() const 00083 { 00084 Value p( subExpr( 0 )->evaluate() ); 00085 return Value( -p.toNumber() ); 00086 } 00087 00088 QString Negative::dump() const 00089 { 00090 return "<negative>" + subExpr( 0 )->dump() + "</number>"; 00091 } 00092 00093 QString BinaryExprBase::dump() const 00094 { 00095 QString s = "<" + opName() + ">"; 00096 s += "<operand>" + subExpr( 0 )->dump() + "</operand>"; 00097 s += "<operand>" + subExpr( 1 )->dump() + "</operand>"; 00098 s += "</" + opName() + ">"; 00099 return s; 00100 } 00101 00102 NumericOp::NumericOp( int _opCode, Expression* lhs, Expression* rhs ) : 00103 opCode( _opCode ) 00104 { 00105 addSubExpression( lhs ); 00106 addSubExpression( rhs ); 00107 } 00108 00109 Value NumericOp::doEvaluate() const 00110 { 00111 Value lhs( subExpr( 0 )->evaluate() ); 00112 Value rhs( subExpr( 1 )->evaluate() ); 00113 double leftVal = lhs.toNumber(), rightVal = rhs.toNumber(); 00114 00115 switch (opCode) { 00116 case OP_Add: 00117 return Value( leftVal + rightVal ); 00118 case OP_Sub: 00119 return Value( leftVal - rightVal ); 00120 case OP_Mul: 00121 return Value( leftVal * rightVal ); 00122 case OP_Div: 00123 if ( rightVal == 0.0 || rightVal == -0.0 ) { 00124 if ( leftVal == 0.0 || leftVal == -0.0) { 00125 return Value(); // 0/0 = NaN 00126 } else { 00127 // +/- Infinity. 00128 if (signbit(leftVal) == signbit(rightVal)) 00129 return Value( KJS::Inf ); 00130 else 00131 return Value( -KJS::Inf ); 00132 } 00133 } else { 00134 return Value( leftVal / rightVal ); 00135 } 00136 case OP_Mod: 00137 if ( rightVal == 0.0 || rightVal == -0.0 ) 00138 return Value(); //Divide by 0; 00139 else 00140 return Value( remainder( leftVal, rightVal ) ); 00141 default: 00142 assert(0); 00143 return Value(); 00144 } 00145 } 00146 00147 QString NumericOp::opName() const 00148 { 00149 switch (opCode) { 00150 case OP_Add: 00151 return QLatin1String( "addition" ); 00152 case OP_Sub: 00153 return QLatin1String( "subtraction" ); 00154 case OP_Mul: 00155 return QLatin1String( "multiplication" ); 00156 case OP_Div: 00157 return QLatin1String( "division" ); 00158 case OP_Mod: 00159 return QLatin1String( "modulo" ); 00160 default: 00161 assert(0); 00162 return QString(); 00163 } 00164 } 00165 00166 RelationOp::RelationOp( int _opCode, Expression* lhs, Expression* rhs ) : 00167 opCode( _opCode ) 00168 { 00169 addSubExpression( lhs ); 00170 addSubExpression( rhs ); 00171 } 00172 00173 static void stringify(const Value& val, WTF::Vector<DOMString>* out) 00174 { 00175 if (val.isString()) { 00176 out->append(val.toString()); 00177 } else { 00178 assert(val.isNodeset()); 00179 00180 const DomNodeList& set = val.toNodeset(); 00181 for (unsigned long i = 0; i < set->length(); ++i) { 00182 DOM::DOMString stringVal = stringValue(set->item(i)); 00183 out->append(stringVal); 00184 } 00185 } 00186 } 00187 00188 static void numify(const Value& val, WTF::Vector<double>* out) 00189 { 00190 if (val.isNumber()) { 00191 out->append(val.toNumber()); 00192 } else { 00193 assert(val.isNodeset()); 00194 00195 const DomNodeList& set = val.toNodeset(); 00196 for (unsigned long i = 0; i < set->length(); ++i) { 00197 DOM::DOMString stringVal = stringValue(set->item(i)); 00198 out->append(Value(stringVal).toNumber()); 00199 } 00200 } 00201 } 00202 00203 Value RelationOp::doEvaluate() const 00204 { 00205 Value lhs( subExpr( 0 )->evaluate() ); 00206 Value rhs( subExpr( 1 )->evaluate() ); 00207 00208 if (lhs.isNodeset() || rhs.isNodeset()) 00209 { 00210 // If both are nodesets, or one is a string our 00211 // comparisons are based on strings. 00212 if ((lhs.isNodeset() && rhs.isNodeset()) || 00213 (lhs.isString() || rhs.isString())) { 00214 00215 WTF::Vector<DOM::DOMString> leftStrings; 00216 WTF::Vector<DOM::DOMString> rightStrings; 00217 00218 stringify(lhs, &leftStrings); 00219 stringify(rhs, &rightStrings); 00220 00221 for (unsigned pl = 0; pl < leftStrings.size(); ++pl) { 00222 for (unsigned pr = 0; pr < rightStrings.size(); ++pr) { 00223 if (compareStrings(leftStrings[pl], rightStrings[pr])) 00224 return Value(true); 00225 } // pr 00226 } // pl 00227 return Value(false); 00228 } 00229 00230 // If one is a number, we do a number-based comparison 00231 if (lhs.isNumber() || rhs.isNumber()) { 00232 WTF::Vector<double> leftNums; 00233 WTF::Vector<double> rightNums; 00234 00235 numify(lhs, &leftNums); 00236 numify(rhs, &rightNums); 00237 00238 for (unsigned pl = 0; pl < leftNums.size(); ++pl) { 00239 for (unsigned pr = 0; pr < rightNums.size(); ++pr) { 00240 if (compareNumbers(leftNums[pl], rightNums[pr])) 00241 return Value(true); 00242 } // pr 00243 } // pl 00244 return Value(false); 00245 } 00246 00247 // Has to be a boolean-based comparison. 00248 // These ones are simpler, since we just convert the nodeset to a bool 00249 assert(lhs.isBoolean() || rhs.isBoolean()); 00250 00251 if (lhs.isNodeset()) 00252 lhs = Value(lhs.toBoolean()); 00253 else 00254 rhs = Value(rhs.toBoolean()); 00255 } // nodeset comparisons 00256 00257 00258 if (opCode == OP_EQ || opCode == OP_NE) { 00259 bool equal; 00260 if ( lhs.isBoolean() || rhs.isBoolean() ) { 00261 equal = ( lhs.toBoolean() == rhs.toBoolean() ); 00262 } else if ( lhs.isNumber() || rhs.isNumber() ) { 00263 equal = ( lhs.toNumber() == rhs.toNumber() ); 00264 } else { 00265 equal = ( lhs.toString() == rhs.toString() ); 00266 } 00267 00268 if ( opCode == OP_EQ ) 00269 return Value( equal ); 00270 else 00271 return Value( !equal ); 00272 00273 } 00274 00275 // For other ops, we always convert to numbers 00276 double leftVal = lhs.toNumber(), rightVal = rhs.toNumber(); 00277 return Value(compareNumbers(leftVal, rightVal)); 00278 } 00279 00280 00281 bool RelationOp::compareNumbers(double leftVal, double rightVal) const 00282 { 00283 switch (opCode) { 00284 case OP_GT: 00285 return leftVal > rightVal; 00286 case OP_GE: 00287 return leftVal >= rightVal; 00288 case OP_LT: 00289 return leftVal < rightVal; 00290 case OP_LE: 00291 return leftVal <= rightVal; 00292 case OP_EQ: 00293 return leftVal == rightVal; 00294 case OP_NE: 00295 return leftVal != rightVal; 00296 default: 00297 assert(0); 00298 return false; 00299 } 00300 } 00301 00302 bool RelationOp::compareStrings(const DOM::DOMString& l, const DOM::DOMString& r) const 00303 { 00304 // String comparisons, as invoked within the nodeset case, are string-based 00305 // only for == and !=. Everything else still goes to numbers. 00306 if (opCode == OP_EQ) 00307 return (l == r); 00308 if (opCode == OP_NE) 00309 return (l != r); 00310 00311 return compareNumbers(Value(l).toNumber(), Value(r).toNumber()); 00312 } 00313 00314 QString RelationOp::opName() const 00315 { 00316 switch (opCode) { 00317 case OP_GT: 00318 return QLatin1String( "relationGT" ); 00319 case OP_GE: 00320 return QLatin1String( "relationGE" ); 00321 case OP_LT: 00322 return QLatin1String( "relationLT" ); 00323 case OP_LE: 00324 return QLatin1String( "relationLE" ); 00325 case OP_EQ: 00326 return QLatin1String( "relationEQ" ); 00327 case OP_NE: 00328 return QLatin1String( "relationNE" ); 00329 default: 00330 assert(0); 00331 return QString(); 00332 } 00333 } 00334 00335 LogicalOp::LogicalOp( int _opCode, Expression* lhs, Expression* rhs ) : 00336 opCode( _opCode ) 00337 { 00338 addSubExpression( lhs ); 00339 addSubExpression( rhs ); 00340 } 00341 00342 bool LogicalOp::shortCircuitOn() const 00343 { 00344 if (opCode == OP_And) 00345 return false; //false and foo 00346 else 00347 return true; //true or bar 00348 } 00349 00350 bool LogicalOp::isConstant() const 00351 { 00352 return subExpr( 0 )->isConstant() && 00353 subExpr( 0 )->evaluate().toBoolean() == shortCircuitOn(); 00354 } 00355 00356 QString LogicalOp::opName() const 00357 { 00358 if ( opCode == OP_And ) 00359 return QLatin1String( "conjunction" ); 00360 else 00361 return QLatin1String( "disjunction" ); 00362 } 00363 00364 Value LogicalOp::doEvaluate() const 00365 { 00366 Value lhs( subExpr( 0 )->evaluate() ); 00367 00368 // This is not only an optimization, http://www.w3.org/TR/xpath 00369 // dictates that we must do short-circuit evaluation 00370 bool lhsBool = lhs.toBoolean(); 00371 if ( lhsBool == shortCircuitOn() ) { 00372 return Value( lhsBool ); 00373 } 00374 00375 return Value( subExpr( 1 )->evaluate().toBoolean() ); 00376 } 00377 00378 QString Union::opName() const 00379 { 00380 return QLatin1String("union"); 00381 } 00382 00383 Value Union::doEvaluate() const 00384 { 00385 Value lhs = subExpr( 0 )->evaluate(); 00386 Value rhs = subExpr( 1 )->evaluate(); 00387 if ( !lhs.isNodeset() || !rhs.isNodeset() ) { 00388 kWarning(6011) << "Union operator '|' works only with nodesets."; 00389 Expression::reportInvalidExpressionErr(); 00390 return Value( new StaticNodeListImpl ); 00391 } 00392 00393 DomNodeList lhsNodes = lhs.toNodeset(); 00394 DomNodeList rhsNodes = rhs.toNodeset(); 00395 DomNodeList result = new StaticNodeListImpl; 00396 00397 for ( unsigned long n = 0; n < lhsNodes->length(); ++n ) 00398 result->append( lhsNodes->item( n ) ); 00399 00400 for ( unsigned long n = 0; n < rhsNodes->length(); ++n ) 00401 result->append( rhsNodes->item( n ) ); 00402 00403 return Value( result ); 00404 } 00405 00406 Predicate::Predicate( Expression *expr ) 00407 : m_expr( expr ) 00408 { 00409 } 00410 00411 Predicate::~Predicate() 00412 { 00413 delete m_expr; 00414 } 00415 00416 bool Predicate::evaluate() const 00417 { 00418 Q_ASSERT( m_expr != 0 ); 00419 00420 Value result( m_expr->evaluate() ); 00421 00422 // foo[3] really means foo[position()=3] 00423 if ( result.isNumber() ) { 00424 return double( Expression::evaluationContext().position ) == result.toNumber(); 00425 } 00426 00427 return result.toBoolean(); 00428 } 00429 00430 void Predicate::optimize() 00431 { 00432 m_expr->optimize(); 00433 } 00434 00435 QString Predicate::dump() const 00436 { 00437 return QString() + "<predicate>" + m_expr->dump() + "</predicate>"; 00438 } 00439 00440 // kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
KDE 4.6 API Reference