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