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