Quantum GIS API Documentation
1.7.4
|
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 }