• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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;

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal