Quantum GIS API Documentation  1.7.4
src/core/qgssearchtreenode.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                           qgssearchtreenode.cpp
00003                   Implementation for evaluating parsed tree
00004                           --------------------
00005     begin                : 2005-07-26
00006     copyright            : (C) 2005 by Martin Dobias
00007     email                : won.der at centrum.sk
00008 ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 /* $Id$ */
00019 
00020 #include "qgslogger.h"
00021 #include "qgsdistancearea.h"
00022 #include "qgsfield.h"
00023 #include "qgsgeometry.h"
00024 #include "qgssearchtreenode.h"
00025 #include <QRegExp>
00026 #include <QObject>
00027 #include <QSet>
00028 #include <QSettings>
00029 #include <iostream>
00030 
00031 #include <cmath>
00032 
00033 #define EVAL_STR(x) (x.length() ? x : "(empty)")
00034 
00035 QgsSearchTreeNode::QgsSearchTreeNode( QgsSearchTreeNode::Type t )
00036 {
00037   Q_ASSERT( t == tNodeList );
00038   mType  = t;
00039   mLeft  = NULL;
00040   mRight = NULL;
00041 
00042   init();
00043 }
00044 
00045 QgsSearchTreeNode::QgsSearchTreeNode( double number )
00046 {
00047   mType   = tNumber;
00048   mNumber = number;
00049   mLeft   = NULL;
00050   mRight  = NULL;
00051 
00052   init();
00053 }
00054 
00055 
00056 QgsSearchTreeNode::QgsSearchTreeNode( Operator op,
00057                                       QgsSearchTreeNode* left,
00058                                       QgsSearchTreeNode* right )
00059 {
00060   mType  = tOperator;
00061   mOp    = op;
00062   mLeft  = left;
00063   mRight = right;
00064 
00065   init();
00066 }
00067 
00068 
00069 QgsSearchTreeNode::QgsSearchTreeNode( QString text, bool isColumnRef )
00070 {
00071   mLeft  = NULL;
00072   mRight = NULL;
00073 
00074   if ( isColumnRef )
00075   {
00076     mType = tColumnRef;
00077     mText = text;
00078     if ( text.at( 0 ) == '\"' )
00079     {
00080       // column reference is quoted
00081       stripColRef();
00082     }
00083   }
00084   else
00085   {
00086     mType = tString;
00087     mText = text;
00088     stripText();
00089   }
00090 
00091   init();
00092 }
00093 
00094 QgsSearchTreeNode::QgsSearchTreeNode( const QgsSearchTreeNode& node )
00095 {
00096   mType = node.mType;
00097   mOp = node.mOp;
00098   mNumber = node.mNumber;
00099   mText = node.mText;
00100 
00101   // recursively copy children
00102   if ( node.mLeft )
00103     mLeft =  new QgsSearchTreeNode( *node.mLeft );
00104   else
00105     mLeft = NULL;
00106 
00107   if ( node.mRight )
00108     mRight = new QgsSearchTreeNode( *node.mRight );
00109   else
00110     mRight = NULL;
00111 
00112   foreach( QgsSearchTreeNode * lnode, node.mNodeList )
00113   {
00114     mNodeList.append( new QgsSearchTreeNode( *lnode ) );
00115   }
00116 
00117   init();
00118 }
00119 
00120 
00121 QgsSearchTreeNode::~QgsSearchTreeNode()
00122 {
00123   // delete children
00124 
00125   if ( mLeft )
00126     delete mLeft;
00127 
00128   if ( mRight )
00129     delete mRight;
00130 
00131   while ( !mNodeList.isEmpty() )
00132     delete mNodeList.takeFirst();
00133 
00134   delete mCalc;
00135 }
00136 
00137 
00138 void QgsSearchTreeNode::init()
00139 {
00140   mCalc = NULL;
00141 
00142   if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER ) )
00143   {
00144     //initialize QgsDistanceArea
00145     mCalc = new QgsDistanceArea;
00146     mCalc->setProjectionsEnabled( false );
00147     QSettings settings;
00148     QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString();
00149     mCalc->setEllipsoid( ellipsoid );
00150   }
00151   else if ( mType == tOperator && mOp == opROWNUM )
00152   {
00153     // initialize row number to a sane value
00154     mNumber = 0;
00155   }
00156 }
00157 
00158 void QgsSearchTreeNode::stripText()
00159 {
00160   // strip single quotes on start,end
00161   mText = mText.mid( 1, mText.length() - 2 );
00162 
00163   // make single "single quotes" from double "single quotes"
00164   mText.replace( QRegExp( "''" ), "'" );
00165 
00166   // strip \n \' etc.
00167   int index = 0;
00168   while (( index = mText.indexOf( '\\', index ) ) != -1 )
00169   {
00170     mText.remove( index, 1 ); // delete backslash
00171     QChar chr;
00172     switch ( mText[index].toLatin1() ) // evaluate backslashed character
00173     {
00174       case 'n':  chr = '\n'; break;
00175       case 't':  chr = '\t'; break;
00176       case '\\': chr = '\\'; break;
00177       case '\'': chr = '\''; break;
00178       default: chr = '?'; break;
00179     }
00180     mText[index++] = chr; // set new character and push index +1
00181   }
00182 
00183 }
00184 
00185 void QgsSearchTreeNode::stripColRef()
00186 {
00187   // strip double quotes on start,end
00188   mText = mText.mid( 1, mText.length() - 2 );
00189 
00190   // make single "double quotes" from double "double quotes"
00191   mText.replace( QRegExp( "\"\"" ), "\"" );
00192 }
00193 
00194 QString QgsSearchTreeNode::quotedColumnRef( QString name )
00195 {
00196   return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) );
00197 }
00198 
00199 
00200 QString QgsSearchTreeNode::makeSearchString()
00201 {
00202   QString str;
00203   if ( mType == tOperator )
00204   {
00205     if ( mOp == opSQRT || mOp == opSIN || mOp == opCOS || mOp == opTAN ||
00206          mOp == opASIN || mOp == opACOS || mOp == opATAN ||
00207          mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING ||
00208          mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN ||
00209          mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR )
00210     {
00211       // functions
00212       switch ( mOp )
00213       {
00214         case opSQRT: str += "sqrt"; break;
00215         case opSIN: str += "sin"; break;
00216         case opCOS: str += "cos"; break;
00217         case opTAN: str += "tan"; break;
00218         case opASIN: str += "asin"; break;
00219         case opACOS: str += "acos"; break;
00220         case opATAN: str += "atan"; break;
00221         case opTOINT: str += "to int"; break;
00222         case opTOREAL: str += "to real"; break;
00223         case opTOSTRING: str += "to string"; break;
00224         case opLOWER: str += "lower"; break;
00225         case opUPPER: str += "upper"; break;
00226         case opATAN2: str += "atan2"; break;
00227         case opSTRLEN: str += "length"; break;
00228         case opREPLACE: str += "replace"; break;
00229         case opSUBSTR: str += "substr"; break;
00230         default: str += "?";
00231       }
00232       // currently all functions take one parameter
00233       str += QString( "(%1)" ).arg( mLeft->makeSearchString() );
00234     }
00235     else if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opROWNUM || mOp == opID || mOp == opX || mOp == opY )
00236     {
00237       // special nullary opeators
00238       switch ( mOp )
00239       {
00240         case opLENGTH: str += "$length"; break;
00241         case opAREA: str += "$area"; break;
00242         case opPERIMETER: str += "$perimeter"; break;
00243         case opROWNUM: str += "$rownum"; break;
00244         case opX: str += "$x"; break;
00245         case opY: str += "$y"; break;
00246         case opID: str += "$id"; break;
00247         default: str += "?";
00248       }
00249     }
00250     else if ( mOp == opNOT )
00251     {
00252       // unary NOT operator
00253       str += "(NOT " + mLeft->makeSearchString() + ")";
00254     }
00255     else
00256     {
00257       // the rest of operator using infix notation
00258       str += "(";
00259       if ( mLeft )
00260       {
00261         str += mLeft->makeSearchString();
00262       }
00263       switch ( mOp )
00264       {
00265         case opAND: str += " AND "; break;
00266         case opOR: str += " OR "; break;
00267 
00268         case opPLUS:  str += "+"; break;
00269         case opMINUS: str += "-"; break;
00270         case opMUL:   str += "*"; break;
00271         case opMOD:   str += "%"; break;
00272         case opDIV:   str += "/"; break;
00273         case opPOW:   str += "^"; break;
00274 
00275         case opEQ: str += " = "; break;
00276         case opNE: str += " != "; break;
00277         case opGT: str += " > "; break;
00278         case opLT: str += " < "; break;
00279         case opGE: str += " >= "; break;
00280         case opLE: str += " <= "; break;
00281 
00282         case opISNULL: str += " IS NULL"; break;
00283         case opISNOTNULL: str += " IS NOT NULL"; break;
00284 
00285         case opRegexp: str += " ~ "; break;
00286         case opLike: str += " LIKE "; break;
00287         case opILike: str += " ILIKE "; break;
00288         case opIN: str += " IN "; break;
00289         case opNOTIN: str += " NOT IN "; break;
00290 
00291         case opCONCAT: str += " || "; break;
00292 
00293         default: str += " ? ";
00294       }
00295 
00296       if ( mRight )
00297       {
00298         str += mRight->makeSearchString();
00299       }
00300       str += ")";
00301     }
00302   }
00303   else if ( mType == tNumber )
00304   {
00305     str += QString::number( mNumber );
00306   }
00307   else if ( mType == tString || mType == tColumnRef )
00308   {
00309     str += mText;
00310   }
00311   else if ( mType == tNodeList )
00312   {
00313     QStringList items;
00314     foreach( QgsSearchTreeNode * node, mNodeList )
00315     {
00316       items << node->makeSearchString();
00317     }
00318 
00319     str += "(" + items.join( "," ) + ")";
00320   }
00321   else // unknown type
00322   {
00323     str += "unknown_node_type:";
00324     str += QString::number( mType );
00325   }
00326 
00327   return str;
00328 }
00329 
00330 QStringList QgsSearchTreeNode::referencedColumns()
00331 {
00332   QList<QgsSearchTreeNode*> columnNodeList = columnRefNodes();
00333   QSet<QString> columnStringSet;
00334 
00335   QList<QgsSearchTreeNode*>::const_iterator nodeIt = columnNodeList.constBegin();
00336   for ( ; nodeIt != columnNodeList.constEnd(); ++nodeIt )
00337   {
00338     columnStringSet.insert(( *nodeIt )->columnRef() );
00339   }
00340   return columnStringSet.toList();
00341 }
00342 
00343 QList<QgsSearchTreeNode*> QgsSearchTreeNode::columnRefNodes()
00344 {
00345   QList<QgsSearchTreeNode*> nodeList;
00346   if ( mType == tOperator )
00347   {
00348     if ( mLeft )
00349     {
00350       nodeList += mLeft->columnRefNodes();
00351     }
00352     if ( mRight )
00353     {
00354       nodeList += mRight->columnRefNodes();
00355     }
00356   }
00357   else if ( mType == tColumnRef )
00358   {
00359     nodeList.push_back( this );
00360   }
00361   return nodeList;
00362 }
00363 
00364 bool QgsSearchTreeNode::needsGeometry()
00365 {
00366   if ( mType == tOperator )
00367   {
00368     if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
00369       return true;
00370 
00371     if ( mLeft && mLeft->needsGeometry() )
00372       return true;
00373     if ( mRight && mRight->needsGeometry() )
00374       return true;
00375     return false;
00376   }
00377   else
00378   {
00379     return false;
00380   }
00381 }
00382 
00383 bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, const QgsAttributeMap &attributes, QgsGeometry* geom )
00384 {
00385   QgsFeature f;
00386   f.setAttributeMap( attributes );
00387   if ( geom )
00388     f.setGeometry( *geom );
00389   return checkAgainst( fields, f );
00390 }
00391 
00392 bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
00393 {
00394   QgsDebugMsgLevel( "checkAgainst: " + makeSearchString(), 2 );
00395 
00396   mError = "";
00397 
00398   // this error should be caught when checking syntax, but for sure...
00399   if ( mType != tOperator )
00400   {
00401     mError = QObject::tr( "Expected operator, got scalar value!" );
00402     return false;
00403   }
00404 
00405   QgsSearchTreeValue value1, value2;
00406   int res;
00407 
00408   switch ( mOp )
00409   {
00410     case opNOT:
00411       return !mLeft->checkAgainst( fields, f );
00412 
00413     case opAND:
00414       if ( !mLeft->checkAgainst( fields, f ) )
00415         return false;
00416       return mRight->checkAgainst( fields, f );
00417 
00418     case opOR:
00419       if ( mLeft->checkAgainst( fields, f ) )
00420         return true;
00421       return mRight->checkAgainst( fields, f );
00422 
00423     case opISNULL:
00424     case opISNOTNULL:
00425       if ( !getValue( value1, mLeft, fields, f ) )
00426         return false;
00427 
00428       return ( mOp == opISNULL ) == value1.isNull();
00429 
00430     case opEQ:
00431     case opNE:
00432     case opGT:
00433     case opLT:
00434     case opGE:
00435     case opLE:
00436       if ( !getValue( value1, mLeft, fields, f ) || !getValue( value2, mRight, fields, f ) )
00437         return false;
00438 
00439       if ( value1.isNull() || value2.isNull() )
00440       {
00441         // NULL values never match
00442         return false;
00443       }
00444 
00445       res = QgsSearchTreeValue::compare( value1, value2 );
00446 
00447       switch ( mOp )
00448       {
00449         case opEQ: return res == 0;
00450         case opNE: return res != 0;
00451         case opGT: return res >  0;
00452         case opLT: return res <  0;
00453         case opGE: return res >= 0;
00454         case opLE: return res <= 0;
00455         default:
00456           mError = QObject::tr( "Unexpected state when evaluating operator!" );
00457           return false;
00458       }
00459 
00460     case opIN:
00461     case opNOTIN:
00462     {
00463       if ( !getValue( value1, mLeft, fields, f ) ||
00464            !mRight || mRight->type() != tNodeList )
00465       {
00466         return false;
00467       }
00468 
00469       foreach( QgsSearchTreeNode * node, mRight->mNodeList )
00470       {
00471         if ( !getValue( value2, node, fields, f ) )
00472         {
00473           mError = QObject::tr( "Could not retrieve value of list value" );
00474           return false;
00475         }
00476 
00477         res = QgsSearchTreeValue::compare( value1, value2 );
00478 
00479         if ( res == 0 )
00480         {
00481           // found
00482           return mOp == opIN;
00483         }
00484       }
00485 
00486       return mOp == opNOTIN;
00487     }
00488 
00489     case opRegexp:
00490     case opLike:
00491     case opILike:
00492     {
00493       if ( !getValue( value1, mLeft, fields, f ) ||
00494            !getValue( value2, mRight, fields, f ) )
00495         return false;
00496 
00497       // value1 is string to be matched
00498       // value2 is regular expression
00499 
00500       // XXX does it make sense to use regexp on numbers?
00501       // in what format should they be?
00502       if ( value1.isNumeric() || value2.isNumeric() )
00503       {
00504         mError = QObject::tr( "Regular expressions on numeric values don't make sense. Use comparison instead." );
00505         return false;
00506       }
00507 
00508       // TODO: reuse QRegExp
00509 
00510       QString str = value2.string();
00511       if ( mOp == opLike || mOp == opILike ) // change from LIKE syntax to regexp
00512       {
00513         // XXX escape % and _  ???
00514         str.replace( "%", ".*" );
00515         str.replace( "_", "." );
00516         return QRegExp( str, mOp == opLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( value1.string() );
00517       }
00518       else
00519       {
00520         return QRegExp( str ).indexIn( value1.string() ) != -1;
00521       }
00522     }
00523 
00524     default:
00525       mError = QObject::tr( "Unknown operator: %1" ).arg( mOp );
00526   }
00527 
00528   return false;
00529 }
00530 
00531 bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value,
00532                                   QgsSearchTreeNode* node,
00533                                   const QgsFieldMap &fields,
00534                                   const QgsAttributeMap &attributes,
00535                                   QgsGeometry* geom )
00536 {
00537   QgsFeature f;
00538   f.setAttributeMap( attributes );
00539   if ( geom )
00540     f.setGeometry( *geom );
00541   return getValue( value, node, fields, f );
00542 }
00543 
00544 bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value,
00545                                   QgsSearchTreeNode* node,
00546                                   const QgsFieldMap& fields,
00547                                   QgsFeature &f )
00548 {
00549   value = node->valueAgainst( fields, f );
00550   if ( value.isError() )
00551   {
00552     switch (( int ) value.number() )
00553     {
00554       case 1:
00555         mError = QObject::tr( "Referenced column wasn't found: %1" ).arg( value.string() );
00556         break;
00557       case 2:
00558         mError = QObject::tr( "Division by zero." );
00559         break;
00560 
00561         // these should never happen (no need to translate)
00562       case 3:
00563         mError = QObject::tr( "Unknown operator: %1" ).arg( value.string() );
00564         break;
00565       case 4:
00566         mError = QObject::tr( "Unknown token: %1" ).arg( value.string() );
00567         break;
00568       default:
00569         mError = QObject::tr( "Unknown error!" );
00570         break;
00571     }
00572     return false;
00573   }
00574   return true;
00575 }
00576 
00577 QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields,
00578     const QgsAttributeMap &attributes,
00579     QgsGeometry* geom )
00580 {
00581   QgsFeature f;
00582   f.setAttributeMap( attributes );
00583   if ( geom )
00584     f.setGeometry( *geom );
00585   return valueAgainst( fields, f );
00586 }
00587 
00588 QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, QgsFeature &f )
00589 {
00590   QgsDebugMsgLevel( "valueAgainst: " + makeSearchString(), 2 );
00591 
00592   switch ( mType )
00593   {
00594     case tNumber:
00595       QgsDebugMsgLevel( "number: " + QString::number( mNumber ), 2 );
00596       return QgsSearchTreeValue( mNumber );
00597 
00598     case tString:
00599       QgsDebugMsgLevel( "text: " + EVAL_STR( mText ), 2 );
00600       return QgsSearchTreeValue( mText );
00601 
00602     case tColumnRef:
00603     {
00604       QgsDebugMsgLevel( "column (" + mText.toLower() + "): ", 2 );
00605       // find field index for the column
00606       QgsFieldMap::const_iterator it;
00607       for ( it = fields.begin(); it != fields.end(); it++ )
00608       {
00609         if ( QString::compare( it->name(), mText, Qt::CaseInsensitive ) == 0 )
00610           break;
00611       }
00612 
00613       if ( it == fields.end() )
00614       {
00615         // report missing column if not found
00616         QgsDebugMsgLevel( "ERROR!", 2 );
00617         return QgsSearchTreeValue( 1, mText );
00618       }
00619 
00620       // get the value
00621       QVariant val = f.attributeMap()[it.key()];
00622       if ( val.isNull() )
00623       {
00624         QgsDebugMsgLevel( "   NULL", 2 );
00625         return QgsSearchTreeValue();
00626       }
00627       else if ( val.type() == QVariant::Bool || val.type() == QVariant::Int || val.type() == QVariant::Double )
00628       {
00629         QgsDebugMsgLevel( "   number: " + QString::number( val.toDouble() ), 2 );
00630         return QgsSearchTreeValue( val.toDouble() );
00631       }
00632       else
00633       {
00634         QgsDebugMsgLevel( "   text: " + EVAL_STR( val.toString() ), 2 );
00635         return QgsSearchTreeValue( val.toString() );
00636       }
00637 
00638     }
00639 
00640     // arithmetic operators
00641     case tOperator:
00642     {
00643       QgsSearchTreeValue value1, value2, value3;
00644       if ( mLeft )
00645       {
00646         if ( mLeft->type() != tNodeList )
00647         {
00648           if ( !getValue( value1, mLeft, fields, f ) ) return value1;
00649         }
00650         else
00651         {
00652           if ( mLeft->mNodeList.size() > 0 && !getValue( value1, mLeft->mNodeList[0], fields, f ) ) return value1;
00653           if ( mLeft->mNodeList.size() > 1 && !getValue( value2, mLeft->mNodeList[1], fields, f ) ) return value2;
00654           if ( mLeft->mNodeList.size() > 2 && !getValue( value3, mLeft->mNodeList[2], fields, f ) ) return value3;
00655         }
00656       }
00657       if ( mRight )
00658       {
00659         Q_ASSERT( !mLeft || mLeft->type() != tNodeList );
00660         if ( !getValue( value2, mRight, fields, f ) ) return value2;
00661       }
00662 
00663       if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
00664       {
00665         if ( !f.geometry() )
00666         {
00667           return QgsSearchTreeValue( 2, "Geometry is 0" );
00668         }
00669 
00670         //check that we don't use area for lines or length for polygons
00671         if ( mOp == opLENGTH && f.geometry()->type() == QGis::Line )
00672         {
00673           return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
00674         }
00675         if ( mOp == opAREA && f.geometry()->type() == QGis::Polygon )
00676         {
00677           return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
00678         }
00679         if ( mOp == opPERIMETER && f.geometry()->type() == QGis::Polygon )
00680         {
00681           return QgsSearchTreeValue( mCalc->measurePerimeter( f.geometry() ) );
00682         }
00683         if ( mOp == opX && f.geometry()->type() == QGis::Point )
00684         {
00685           return QgsSearchTreeValue( f.geometry()->asPoint().x() );
00686         }
00687         if ( mOp == opY && f.geometry()->type() == QGis::Point )
00688         {
00689           return QgsSearchTreeValue( f.geometry()->asPoint().y() );
00690         }
00691         return QgsSearchTreeValue( 0 );
00692       }
00693 
00694       if ( mOp == opID )
00695       {
00696         return QgsSearchTreeValue( f.id() );
00697       }
00698 
00699       if ( mOp == opROWNUM )
00700       {
00701         // the row number has to be previously set by the caller using setCurrentRowNumber
00702         return QgsSearchTreeValue( mNumber );
00703       }
00704 
00705       //string operations with one argument
00706       if ( !mRight && !value1.isNumeric() )
00707       {
00708         if ( mOp == opTOINT )
00709         {
00710           return QgsSearchTreeValue( value1.string().toInt() );
00711         }
00712         else if ( mOp == opTOREAL )
00713         {
00714           return QgsSearchTreeValue( value1.string().toDouble() );
00715         }
00716       }
00717 
00718       //don't convert to numbers in case of string concatenation
00719       if ( mLeft && mRight && !value1.isNumeric() && !value2.isNumeric() )
00720       {
00721         // TODO: concatenation using '+' operator should be removed in favor of '||' operator
00722         // because it may lead to surprising behavior if numbers are stored in a string
00723         if ( mOp == opPLUS )
00724         {
00725           return QgsSearchTreeValue( value1.string() + value2.string() );
00726         }
00727       }
00728 
00729       // string concatenation ||
00730       if ( mLeft && mRight && mOp == opCONCAT )
00731       {
00732         if ( value1.isNumeric() && value2.isNumeric() )
00733         {
00734           return QgsSearchTreeValue( 5, "Operator doesn't match the argument types." );
00735         }
00736         else
00737         {
00738           QString arg1 = value1.isNumeric() ? QString::number( value1.number() ) : value1.string();
00739           QString arg2 = value2.isNumeric() ? QString::number( value2.number() ) : value2.string();
00740           return QgsSearchTreeValue( arg1 + arg2 );
00741         }
00742       }
00743 
00744       // string operations
00745       switch ( mOp )
00746       {
00747         case opLOWER:
00748           return QgsSearchTreeValue( value1.string().toLower() );
00749         case opUPPER:
00750           return QgsSearchTreeValue( value1.string().toUpper() );
00751         case opSTRLEN:
00752           return QgsSearchTreeValue( value1.string().length() );
00753         case opREPLACE:
00754           return QgsSearchTreeValue( value1.string().replace( value2.string(), value3.string() ) );
00755         case opSUBSTR:
00756           return QgsSearchTreeValue( value1.string().mid( value2.number() - 1, value3.number() ) );
00757         default:
00758           break;
00759       }
00760 
00761       // for other operators, convert strings to numbers if needed
00762       double val1, val2;
00763       if ( value1.isNumeric() )
00764         val1 = value1.number();
00765       else
00766         val1 = value1.string().toDouble();
00767       if ( value2.isNumeric() )
00768         val2 = value2.number();
00769       else
00770         val2 = value2.string().toDouble();
00771 
00772       switch ( mOp )
00773       {
00774         case opPLUS:
00775           return QgsSearchTreeValue( val1 + val2 );
00776         case opMINUS:
00777           return QgsSearchTreeValue( val1 - val2 );
00778         case opMUL:
00779           return QgsSearchTreeValue( val1 * val2 );
00780         case opMOD:
00781           // NOTE: we _might_ support float operators, like postgresql does
00782           // see 83c94a886c059 commit in postgresql git repo for more info
00783           return QgsSearchTreeValue( int(val1) % int(val2) );
00784         case opDIV:
00785           if ( val2 == 0 )
00786             return QgsSearchTreeValue( 2, "" ); // division by zero
00787           else
00788             return QgsSearchTreeValue( val1 / val2 );
00789         case opPOW:
00790           if (( val1 == 0 && val2 < 0 ) || ( val2 < 0 && ( val2 - floor( val2 ) ) > 0 ) )
00791           {
00792             return QgsSearchTreeValue( 4, "Error in power function" );
00793           }
00794           return QgsSearchTreeValue( pow( val1, val2 ) );
00795         case opSQRT:
00796           return QgsSearchTreeValue( sqrt( val1 ) );
00797         case opSIN:
00798           return QgsSearchTreeValue( sin( val1 ) );
00799         case opCOS:
00800           return QgsSearchTreeValue( cos( val1 ) );
00801         case opTAN:
00802           return QgsSearchTreeValue( tan( val1 ) );
00803         case opASIN:
00804           return QgsSearchTreeValue( asin( val1 ) );
00805         case opACOS:
00806           return QgsSearchTreeValue( acos( val1 ) );
00807         case opATAN:
00808           return QgsSearchTreeValue( atan( val1 ) );
00809         case opATAN2:
00810           return QgsSearchTreeValue( atan2( val1, val2 ) );
00811         case opTOINT:
00812           return QgsSearchTreeValue( int( val1 ) );
00813         case opTOREAL:
00814           return QgsSearchTreeValue( val1 );
00815         case opTOSTRING:
00816           return QgsSearchTreeValue( QString::number( val1 ) );
00817 
00818         default:
00819           return QgsSearchTreeValue( 3, QString::number( mOp ) ); // unknown operator
00820       }
00821     }
00822 
00823     default:
00824       return QgsSearchTreeValue( 4, QString::number( mType ) ); // unknown token
00825   }
00826 }
00827 
00828 
00829 void QgsSearchTreeNode::setCurrentRowNumber( int rownum )
00830 {
00831   if ( mType == tOperator )
00832   {
00833     if ( mOp == opROWNUM )
00834       mNumber = rownum;
00835     else
00836     {
00837       // propagate the new row number to children
00838       if ( mLeft )
00839         mLeft->setCurrentRowNumber( rownum );
00840       if ( mRight )
00841         mRight->setCurrentRowNumber( rownum );
00842     }
00843   }
00844 }
00845 
00846 void QgsSearchTreeNode::append( QgsSearchTreeNode *node )
00847 {
00848   Q_ASSERT( mType == tNodeList );
00849   mNodeList.append( node );
00850 }
00851 
00852 void QgsSearchTreeNode::append( QList<QgsSearchTreeNode *> nodes )
00853 {
00854   foreach( QgsSearchTreeNode * node, nodes )
00855   {
00856     mNodeList.append( node );
00857   }
00858 }
00859 
00860 int QgsSearchTreeValue::compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2, Qt::CaseSensitivity cs )
00861 {
00862   if ( value1.isNumeric() || value2.isNumeric() )
00863   {
00864     // numeric comparison
00865 
00866     // convert to numbers if needed
00867     double val1, val2;
00868     if ( value1.isNumeric() )
00869       val1 = value1.number();
00870     else
00871       val1 = value1.string().toDouble();
00872     if ( value2.isNumeric() )
00873       val2 = value2.number();
00874     else
00875       val2 = value2.string().toDouble();
00876 
00877     QgsDebugMsgLevel( "NUM_COMP: " + QString::number( val1 ) + " ~ " + QString::number( val2 ), 2 );
00878 
00879     if ( val1 < val2 )
00880       return -1;
00881     else if ( val1 > val2 )
00882       return 1;
00883     else
00884       return 0;
00885   }
00886   else
00887   {
00888     // string comparison
00889     return value1.string().compare( value2.string(), cs );
00890   }
00891 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines