Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgssearchtreenode.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssearchtreenode.cpp
3  Implementation for evaluating parsed tree
4  --------------------
5  begin : 2005-07-26
6  copyright : (C) 2005 by Martin Dobias
7  email : won.der at centrum.sk
8 ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 /* $Id$ */
19 
20 #include "qgslogger.h"
21 #include "qgsdistancearea.h"
22 #include "qgsfield.h"
23 #include "qgsgeometry.h"
24 #include "qgssearchtreenode.h"
25 #include <QRegExp>
26 #include <QObject>
27 #include <QSet>
28 #include <QSettings>
29 #include <iostream>
30 
31 #include <cmath>
32 
33 #define EVAL_STR(x) (x.length() ? x : "(empty)")
34 
36 {
37  Q_ASSERT( t == tNodeList );
38  mType = t;
39  mLeft = NULL;
40  mRight = NULL;
41 
42  init();
43 }
44 
46 {
47  mType = tNumber;
48  mNumber = number;
49  mLeft = NULL;
50  mRight = NULL;
51 
52  init();
53 }
54 
55 
57  QgsSearchTreeNode* left,
58  QgsSearchTreeNode* right )
59 {
60  mType = tOperator;
61  mOp = op;
62  mLeft = left;
63  mRight = right;
64 
65  init();
66 }
67 
68 
69 QgsSearchTreeNode::QgsSearchTreeNode( QString text, bool isColumnRef )
70 {
71  mLeft = NULL;
72  mRight = NULL;
73 
74  if ( isColumnRef )
75  {
76  mType = tColumnRef;
77  mText = text;
78  if ( text.at( 0 ) == '\"' )
79  {
80  // column reference is quoted
81  stripColRef();
82  }
83  }
84  else
85  {
86  mType = tString;
87  mText = text;
88  stripText();
89  }
90 
91  init();
92 }
93 
95 {
96  mType = node.mType;
97  mOp = node.mOp;
98  mNumber = node.mNumber;
99  mText = node.mText;
100 
101  // recursively copy children
102  if ( node.mLeft )
103  mLeft = new QgsSearchTreeNode( *node.mLeft );
104  else
105  mLeft = NULL;
106 
107  if ( node.mRight )
108  mRight = new QgsSearchTreeNode( *node.mRight );
109  else
110  mRight = NULL;
111 
112  foreach( QgsSearchTreeNode * lnode, node.mNodeList )
113  {
114  mNodeList.append( new QgsSearchTreeNode( *lnode ) );
115  }
116 
117  init();
118 }
119 
120 
122 {
123  // delete children
124 
125  if ( mLeft )
126  delete mLeft;
127 
128  if ( mRight )
129  delete mRight;
130 
131  while ( !mNodeList.isEmpty() )
132  delete mNodeList.takeFirst();
133 
134  delete mCalc;
135 }
136 
137 
139 {
140  mCalc = NULL;
141 
142  if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER ) )
143  {
144  //initialize QgsDistanceArea
145  mCalc = new QgsDistanceArea;
146  mCalc->setProjectionsEnabled( false );
147  QSettings settings;
148  QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString();
149  mCalc->setEllipsoid( ellipsoid );
150  }
151  else if ( mType == tOperator && mOp == opROWNUM )
152  {
153  // initialize row number to a sane value
154  mNumber = 0;
155  }
156 }
157 
159 {
160  // strip single quotes on start,end
161  mText = mText.mid( 1, mText.length() - 2 );
162 
163  // make single "single quotes" from double "single quotes"
164  mText.replace( QRegExp( "''" ), "'" );
165 
166  // strip \n \' etc.
167  int index = 0;
168  while (( index = mText.indexOf( '\\', index ) ) != -1 )
169  {
170  mText.remove( index, 1 ); // delete backslash
171  QChar chr;
172  switch ( mText[index].toLatin1() ) // evaluate backslashed character
173  {
174  case 'n': chr = '\n'; break;
175  case 't': chr = '\t'; break;
176  case '\\': chr = '\\'; break;
177  case '\'': chr = '\''; break;
178  default: chr = '?'; break;
179  }
180  mText[index++] = chr; // set new character and push index +1
181  }
182 
183 }
184 
186 {
187  // strip double quotes on start,end
188  mText = mText.mid( 1, mText.length() - 2 );
189 
190  // make single "double quotes" from double "double quotes"
191  mText.replace( QRegExp( "\"\"" ), "\"" );
192 }
193 
194 QString QgsSearchTreeNode::quotedColumnRef( QString name )
195 {
196  return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) );
197 }
198 
199 
201 {
202  QString str;
203  if ( mType == tOperator )
204  {
205  if ( mOp == opSQRT || mOp == opSIN || mOp == opCOS || mOp == opTAN ||
206  mOp == opASIN || mOp == opACOS || mOp == opATAN ||
207  mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING ||
208  mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN ||
209  mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR )
210  {
211  // functions
212  switch ( mOp )
213  {
214  case opSQRT: str += "sqrt"; break;
215  case opSIN: str += "sin"; break;
216  case opCOS: str += "cos"; break;
217  case opTAN: str += "tan"; break;
218  case opASIN: str += "asin"; break;
219  case opACOS: str += "acos"; break;
220  case opATAN: str += "atan"; break;
221  case opTOINT: str += "to int"; break;
222  case opTOREAL: str += "to real"; break;
223  case opTOSTRING: str += "to string"; break;
224  case opLOWER: str += "lower"; break;
225  case opUPPER: str += "upper"; break;
226  case opATAN2: str += "atan2"; break;
227  case opSTRLEN: str += "length"; break;
228  case opREPLACE: str += "replace"; break;
229  case opSUBSTR: str += "substr"; break;
230  default: str += "?";
231  }
232  // currently all functions take one parameter
233  str += QString( "(%1)" ).arg( mLeft->makeSearchString() );
234  }
235  else if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opROWNUM || mOp == opID || mOp == opX || mOp == opY )
236  {
237  // special nullary opeators
238  switch ( mOp )
239  {
240  case opLENGTH: str += "$length"; break;
241  case opAREA: str += "$area"; break;
242  case opPERIMETER: str += "$perimeter"; break;
243  case opROWNUM: str += "$rownum"; break;
244  case opX: str += "$x"; break;
245  case opY: str += "$y"; break;
246  case opID: str += "$id"; break;
247  default: str += "?";
248  }
249  }
250  else if ( mOp == opNOT )
251  {
252  // unary NOT operator
253  str += "(NOT " + mLeft->makeSearchString() + ")";
254  }
255  else
256  {
257  // the rest of operator using infix notation
258  str += "(";
259  if ( mLeft )
260  {
261  str += mLeft->makeSearchString();
262  }
263  switch ( mOp )
264  {
265  case opAND: str += " AND "; break;
266  case opOR: str += " OR "; break;
267 
268  case opPLUS: str += "+"; break;
269  case opMINUS: str += "-"; break;
270  case opMUL: str += "*"; break;
271  case opMOD: str += "%"; break;
272  case opDIV: str += "/"; break;
273  case opPOW: str += "^"; break;
274 
275  case opEQ: str += " = "; break;
276  case opNE: str += " != "; break;
277  case opGT: str += " > "; break;
278  case opLT: str += " < "; break;
279  case opGE: str += " >= "; break;
280  case opLE: str += " <= "; break;
281 
282  case opISNULL: str += " IS NULL"; break;
283  case opISNOTNULL: str += " IS NOT NULL"; break;
284 
285  case opRegexp: str += " ~ "; break;
286  case opLike: str += " LIKE "; break;
287  case opILike: str += " ILIKE "; break;
288  case opIN: str += " IN "; break;
289  case opNOTIN: str += " NOT IN "; break;
290 
291  case opCONCAT: str += " || "; break;
292 
293  default: str += " ? ";
294  }
295 
296  if ( mRight )
297  {
298  str += mRight->makeSearchString();
299  }
300  str += ")";
301  }
302  }
303  else if ( mType == tNumber )
304  {
305  str += QString::number( mNumber );
306  }
307  else if ( mType == tString || mType == tColumnRef )
308  {
309  str += mText;
310  }
311  else if ( mType == tNodeList )
312  {
313  QStringList items;
314  foreach( QgsSearchTreeNode * node, mNodeList )
315  {
316  items << node->makeSearchString();
317  }
318 
319  str += "(" + items.join( "," ) + ")";
320  }
321  else // unknown type
322  {
323  str += "unknown_node_type:";
324  str += QString::number( mType );
325  }
326 
327  return str;
328 }
329 
331 {
332  QList<QgsSearchTreeNode*> columnNodeList = columnRefNodes();
333  QSet<QString> columnStringSet;
334 
335  QList<QgsSearchTreeNode*>::const_iterator nodeIt = columnNodeList.constBegin();
336  for ( ; nodeIt != columnNodeList.constEnd(); ++nodeIt )
337  {
338  columnStringSet.insert(( *nodeIt )->columnRef() );
339  }
340  return columnStringSet.toList();
341 }
342 
343 QList<QgsSearchTreeNode*> QgsSearchTreeNode::columnRefNodes()
344 {
345  QList<QgsSearchTreeNode*> nodeList;
346  if ( mType == tOperator )
347  {
348  if ( mLeft )
349  {
350  nodeList += mLeft->columnRefNodes();
351  }
352  if ( mRight )
353  {
354  nodeList += mRight->columnRefNodes();
355  }
356  }
357  else if ( mType == tColumnRef )
358  {
359  nodeList.push_back( this );
360  }
361  return nodeList;
362 }
363 
365 {
366  if ( mType == tOperator )
367  {
368  if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
369  return true;
370 
371  if ( mLeft && mLeft->needsGeometry() )
372  return true;
373  if ( mRight && mRight->needsGeometry() )
374  return true;
375  return false;
376  }
377  else
378  {
379  return false;
380  }
381 }
382 
383 bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, const QgsAttributeMap &attributes, QgsGeometry* geom )
384 {
385  QgsFeature f;
386  f.setAttributeMap( attributes );
387  if ( geom )
388  f.setGeometry( *geom );
389  return checkAgainst( fields, f );
390 }
391 
393 {
394  QgsDebugMsgLevel( "checkAgainst: " + makeSearchString(), 2 );
395 
396  mError = "";
397 
398  // this error should be caught when checking syntax, but for sure...
399  if ( mType != tOperator )
400  {
401  mError = QObject::tr( "Expected operator, got scalar value!" );
402  return false;
403  }
404 
405  QgsSearchTreeValue value1, value2;
406  int res;
407 
408  switch ( mOp )
409  {
410  case opNOT:
411  return !mLeft->checkAgainst( fields, f );
412 
413  case opAND:
414  if ( !mLeft->checkAgainst( fields, f ) )
415  return false;
416  return mRight->checkAgainst( fields, f );
417 
418  case opOR:
419  if ( mLeft->checkAgainst( fields, f ) )
420  return true;
421  return mRight->checkAgainst( fields, f );
422 
423  case opISNULL:
424  case opISNOTNULL:
425  if ( !getValue( value1, mLeft, fields, f ) )
426  return false;
427 
428  return ( mOp == opISNULL ) == value1.isNull();
429 
430  case opEQ:
431  case opNE:
432  case opGT:
433  case opLT:
434  case opGE:
435  case opLE:
436  if ( !getValue( value1, mLeft, fields, f ) || !getValue( value2, mRight, fields, f ) )
437  return false;
438 
439  if ( value1.isNull() || value2.isNull() )
440  {
441  // NULL values never match
442  return false;
443  }
444 
445  res = QgsSearchTreeValue::compare( value1, value2 );
446 
447  switch ( mOp )
448  {
449  case opEQ: return res == 0;
450  case opNE: return res != 0;
451  case opGT: return res > 0;
452  case opLT: return res < 0;
453  case opGE: return res >= 0;
454  case opLE: return res <= 0;
455  default:
456  mError = QObject::tr( "Unexpected state when evaluating operator!" );
457  return false;
458  }
459 
460  case opIN:
461  case opNOTIN:
462  {
463  if ( !getValue( value1, mLeft, fields, f ) ||
464  !mRight || mRight->type() != tNodeList )
465  {
466  return false;
467  }
468 
469  foreach( QgsSearchTreeNode * node, mRight->mNodeList )
470  {
471  if ( !getValue( value2, node, fields, f ) )
472  {
473  mError = QObject::tr( "Could not retrieve value of list value" );
474  return false;
475  }
476 
477  res = QgsSearchTreeValue::compare( value1, value2 );
478 
479  if ( res == 0 )
480  {
481  // found
482  return mOp == opIN;
483  }
484  }
485 
486  return mOp == opNOTIN;
487  }
488 
489  case opRegexp:
490  case opLike:
491  case opILike:
492  {
493  if ( !getValue( value1, mLeft, fields, f ) ||
494  !getValue( value2, mRight, fields, f ) )
495  return false;
496 
497  // value1 is string to be matched
498  // value2 is regular expression
499 
500  // XXX does it make sense to use regexp on numbers?
501  // in what format should they be?
502  if ( value1.isNumeric() || value2.isNumeric() )
503  {
504  mError = QObject::tr( "Regular expressions on numeric values don't make sense. Use comparison instead." );
505  return false;
506  }
507 
508  // TODO: reuse QRegExp
509 
510  QString str = value2.string();
511  if ( mOp == opLike || mOp == opILike ) // change from LIKE syntax to regexp
512  {
513  // XXX escape % and _ ???
514  str.replace( "%", ".*" );
515  str.replace( "_", "." );
516  return QRegExp( str, mOp == opLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( value1.string() );
517  }
518  else
519  {
520  return QRegExp( str ).indexIn( value1.string() ) != -1;
521  }
522  }
523 
524  default:
525  mError = QObject::tr( "Unknown operator: %1" ).arg( mOp );
526  }
527 
528  return false;
529 }
530 
532  QgsSearchTreeNode* node,
533  const QgsFieldMap &fields,
534  const QgsAttributeMap &attributes,
535  QgsGeometry* geom )
536 {
537  QgsFeature f;
538  f.setAttributeMap( attributes );
539  if ( geom )
540  f.setGeometry( *geom );
541  return getValue( value, node, fields, f );
542 }
543 
545  QgsSearchTreeNode* node,
546  const QgsFieldMap& fields,
547  QgsFeature &f )
548 {
549  value = node->valueAgainst( fields, f );
550  if ( value.isError() )
551  {
552  switch (( int ) value.number() )
553  {
554  case 1:
555  mError = QObject::tr( "Referenced column wasn't found: %1" ).arg( value.string() );
556  break;
557  case 2:
558  mError = QObject::tr( "Division by zero." );
559  break;
560 
561  // these should never happen (no need to translate)
562  case 3:
563  mError = QObject::tr( "Unknown operator: %1" ).arg( value.string() );
564  break;
565  case 4:
566  mError = QObject::tr( "Unknown token: %1" ).arg( value.string() );
567  break;
568  default:
569  mError = QObject::tr( "Unknown error!" );
570  break;
571  }
572  return false;
573  }
574  return true;
575 }
576 
578  const QgsAttributeMap &attributes,
579  QgsGeometry* geom )
580 {
581  QgsFeature f;
582  f.setAttributeMap( attributes );
583  if ( geom )
584  f.setGeometry( *geom );
585  return valueAgainst( fields, f );
586 }
587 
589 {
590  QgsDebugMsgLevel( "valueAgainst: " + makeSearchString(), 2 );
591 
592  switch ( mType )
593  {
594  case tNumber:
595  QgsDebugMsgLevel( "number: " + QString::number( mNumber ), 2 );
596  return QgsSearchTreeValue( mNumber );
597 
598  case tString:
599  QgsDebugMsgLevel( "text: " + EVAL_STR( mText ), 2 );
600  return QgsSearchTreeValue( mText );
601 
602  case tColumnRef:
603  {
604  QgsDebugMsgLevel( "column (" + mText.toLower() + "): ", 2 );
605  // find field index for the column
606  QgsFieldMap::const_iterator it;
607  for ( it = fields.begin(); it != fields.end(); it++ )
608  {
609  if ( QString::compare( it->name(), mText, Qt::CaseInsensitive ) == 0 )
610  break;
611  }
612 
613  if ( it == fields.end() )
614  {
615  // report missing column if not found
616  QgsDebugMsgLevel( "ERROR!", 2 );
617  return QgsSearchTreeValue( 1, mText );
618  }
619 
620  // get the value
621  QVariant val = f.attributeMap()[it.key()];
622  if ( val.isNull() )
623  {
624  QgsDebugMsgLevel( " NULL", 2 );
625  return QgsSearchTreeValue();
626  }
627  else if ( val.type() == QVariant::Bool || val.type() == QVariant::Int || val.type() == QVariant::Double )
628  {
629  QgsDebugMsgLevel( " number: " + QString::number( val.toDouble() ), 2 );
630  return QgsSearchTreeValue( val.toDouble() );
631  }
632  else
633  {
634  QgsDebugMsgLevel( " text: " + EVAL_STR( val.toString() ), 2 );
635  return QgsSearchTreeValue( val.toString() );
636  }
637 
638  }
639 
640  // arithmetic operators
641  case tOperator:
642  {
643  QgsSearchTreeValue value1, value2, value3;
644  if ( mLeft )
645  {
646  if ( mLeft->type() != tNodeList )
647  {
648  if ( !getValue( value1, mLeft, fields, f ) ) return value1;
649  }
650  else
651  {
652  if ( mLeft->mNodeList.size() > 0 && !getValue( value1, mLeft->mNodeList[0], fields, f ) ) return value1;
653  if ( mLeft->mNodeList.size() > 1 && !getValue( value2, mLeft->mNodeList[1], fields, f ) ) return value2;
654  if ( mLeft->mNodeList.size() > 2 && !getValue( value3, mLeft->mNodeList[2], fields, f ) ) return value3;
655  }
656  }
657  if ( mRight )
658  {
659  Q_ASSERT( !mLeft || mLeft->type() != tNodeList );
660  if ( !getValue( value2, mRight, fields, f ) ) return value2;
661  }
662 
663  if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
664  {
665  if ( !f.geometry() )
666  {
667  return QgsSearchTreeValue( 2, "Geometry is 0" );
668  }
669 
670  //check that we don't use area for lines or length for polygons
671  if ( mOp == opLENGTH && f.geometry()->type() == QGis::Line )
672  {
673  return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
674  }
675  if ( mOp == opAREA && f.geometry()->type() == QGis::Polygon )
676  {
677  return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
678  }
679  if ( mOp == opPERIMETER && f.geometry()->type() == QGis::Polygon )
680  {
682  }
683  if ( mOp == opX && f.geometry()->type() == QGis::Point )
684  {
685  return QgsSearchTreeValue( f.geometry()->asPoint().x() );
686  }
687  if ( mOp == opY && f.geometry()->type() == QGis::Point )
688  {
689  return QgsSearchTreeValue( f.geometry()->asPoint().y() );
690  }
691  return QgsSearchTreeValue( 0 );
692  }
693 
694  if ( mOp == opID )
695  {
696  return QgsSearchTreeValue( f.id() );
697  }
698 
699  if ( mOp == opROWNUM )
700  {
701  // the row number has to be previously set by the caller using setCurrentRowNumber
702  return QgsSearchTreeValue( mNumber );
703  }
704 
705  //string operations with one argument
706  if ( !mRight && !value1.isNumeric() )
707  {
708  if ( mOp == opTOINT )
709  {
710  return QgsSearchTreeValue( value1.string().toInt() );
711  }
712  else if ( mOp == opTOREAL )
713  {
714  return QgsSearchTreeValue( value1.string().toDouble() );
715  }
716  }
717 
718  //don't convert to numbers in case of string concatenation
719  if ( mLeft && mRight && !value1.isNumeric() && !value2.isNumeric() )
720  {
721  // TODO: concatenation using '+' operator should be removed in favor of '||' operator
722  // because it may lead to surprising behavior if numbers are stored in a string
723  if ( mOp == opPLUS )
724  {
725  return QgsSearchTreeValue( value1.string() + value2.string() );
726  }
727  }
728 
729  // string concatenation ||
730  if ( mLeft && mRight && mOp == opCONCAT )
731  {
732  if ( value1.isNumeric() && value2.isNumeric() )
733  {
734  return QgsSearchTreeValue( 5, "Operator doesn't match the argument types." );
735  }
736  else
737  {
738  QString arg1 = value1.isNumeric() ? QString::number( value1.number() ) : value1.string();
739  QString arg2 = value2.isNumeric() ? QString::number( value2.number() ) : value2.string();
740  return QgsSearchTreeValue( arg1 + arg2 );
741  }
742  }
743 
744  // string operations
745  switch ( mOp )
746  {
747  case opLOWER:
748  return QgsSearchTreeValue( value1.string().toLower() );
749  case opUPPER:
750  return QgsSearchTreeValue( value1.string().toUpper() );
751  case opSTRLEN:
752  return QgsSearchTreeValue( value1.string().length() );
753  case opREPLACE:
754  return QgsSearchTreeValue( value1.string().replace( value2.string(), value3.string() ) );
755  case opSUBSTR:
756  return QgsSearchTreeValue( value1.string().mid( value2.number() - 1, value3.number() ) );
757  default:
758  break;
759  }
760 
761  // for other operators, convert strings to numbers if needed
762  double val1, val2;
763  if ( value1.isNumeric() )
764  val1 = value1.number();
765  else
766  val1 = value1.string().toDouble();
767  if ( value2.isNumeric() )
768  val2 = value2.number();
769  else
770  val2 = value2.string().toDouble();
771 
772  switch ( mOp )
773  {
774  case opPLUS:
775  return QgsSearchTreeValue( val1 + val2 );
776  case opMINUS:
777  return QgsSearchTreeValue( val1 - val2 );
778  case opMUL:
779  return QgsSearchTreeValue( val1 * val2 );
780  case opMOD:
781  // NOTE: we _might_ support float operators, like postgresql does
782  // see 83c94a886c059 commit in postgresql git repo for more info
783  return QgsSearchTreeValue( int(val1) % int(val2) );
784  case opDIV:
785  if ( val2 == 0 )
786  return QgsSearchTreeValue( 2, "" ); // division by zero
787  else
788  return QgsSearchTreeValue( val1 / val2 );
789  case opPOW:
790  if (( val1 == 0 && val2 < 0 ) || ( val2 < 0 && ( val2 - floor( val2 ) ) > 0 ) )
791  {
792  return QgsSearchTreeValue( 4, "Error in power function" );
793  }
794  return QgsSearchTreeValue( pow( val1, val2 ) );
795  case opSQRT:
796  return QgsSearchTreeValue( sqrt( val1 ) );
797  case opSIN:
798  return QgsSearchTreeValue( sin( val1 ) );
799  case opCOS:
800  return QgsSearchTreeValue( cos( val1 ) );
801  case opTAN:
802  return QgsSearchTreeValue( tan( val1 ) );
803  case opASIN:
804  return QgsSearchTreeValue( asin( val1 ) );
805  case opACOS:
806  return QgsSearchTreeValue( acos( val1 ) );
807  case opATAN:
808  return QgsSearchTreeValue( atan( val1 ) );
809  case opATAN2:
810  return QgsSearchTreeValue( atan2( val1, val2 ) );
811  case opTOINT:
812  return QgsSearchTreeValue( int( val1 ) );
813  case opTOREAL:
814  return QgsSearchTreeValue( val1 );
815  case opTOSTRING:
816  return QgsSearchTreeValue( QString::number( val1 ) );
817 
818  default:
819  return QgsSearchTreeValue( 3, QString::number( mOp ) ); // unknown operator
820  }
821  }
822 
823  default:
824  return QgsSearchTreeValue( 4, QString::number( mType ) ); // unknown token
825  }
826 }
827 
828 
830 {
831  if ( mType == tOperator )
832  {
833  if ( mOp == opROWNUM )
834  mNumber = rownum;
835  else
836  {
837  // propagate the new row number to children
838  if ( mLeft )
839  mLeft->setCurrentRowNumber( rownum );
840  if ( mRight )
841  mRight->setCurrentRowNumber( rownum );
842  }
843  }
844 }
845 
847 {
848  Q_ASSERT( mType == tNodeList );
849  mNodeList.append( node );
850 }
851 
852 void QgsSearchTreeNode::append( QList<QgsSearchTreeNode *> nodes )
853 {
854  foreach( QgsSearchTreeNode * node, nodes )
855  {
856  mNodeList.append( node );
857  }
858 }
859 
860 int QgsSearchTreeValue::compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2, Qt::CaseSensitivity cs )
861 {
862  if ( value1.isNumeric() || value2.isNumeric() )
863  {
864  // numeric comparison
865 
866  // convert to numbers if needed
867  double val1, val2;
868  if ( value1.isNumeric() )
869  val1 = value1.number();
870  else
871  val1 = value1.string().toDouble();
872  if ( value2.isNumeric() )
873  val2 = value2.number();
874  else
875  val2 = value2.string().toDouble();
876 
877  QgsDebugMsgLevel( "NUM_COMP: " + QString::number( val1 ) + " ~ " + QString::number( val2 ), 2 );
878 
879  if ( val1 < val2 )
880  return -1;
881  else if ( val1 > val2 )
882  return 1;
883  else
884  return 0;
885  }
886  else
887  {
888  // string comparison
889  return value1.string().compare( value2.string(), cs );
890  }
891 }