|
18 | 18 | #include <QtDebug>
|
19 | 19 | #include <QDomDocument>
|
20 | 20 | #include <QSettings>
|
| 21 | +#include <QDate> |
| 22 | +#include <QRegExp> |
21 | 23 |
|
22 | 24 | #include <math.h>
|
23 | 25 | #include <limits>
|
|
30 | 32 | // from parser
|
31 | 33 | extern QgsExpression::Node* parseExpression( const QString& str, QString& parserErrorMsg );
|
32 | 34 |
|
| 35 | +QgsExpression::Interval::~Interval() {} |
| 36 | + |
| 37 | +QgsExpression::Interval QgsExpression::Interval::invalidInterVal() |
| 38 | +{ |
| 39 | + QgsExpression::Interval inter = QgsExpression::Interval(); |
| 40 | + inter.setValid( false ); |
| 41 | + return inter; |
| 42 | +} |
| 43 | + |
| 44 | +QgsExpression::Interval QgsExpression::Interval::fromString( QString string ) |
| 45 | +{ |
| 46 | + int seconds = 0; |
| 47 | + QRegExp rx( "(\\d?\\.?\\d+\\s+[a-z]+)", Qt::CaseInsensitive ); |
| 48 | + QStringList list; |
| 49 | + int pos = 0; |
| 50 | + |
| 51 | + while (( pos = rx.indexIn( string, pos ) ) != -1 ) |
| 52 | + { |
| 53 | + list << rx.cap( 1 ); |
| 54 | + pos += rx.matchedLength(); |
| 55 | + } |
| 56 | + |
| 57 | + foreach( QString match, list ) |
| 58 | + { |
| 59 | + QStringList split = match.split( QRegExp( "\\s+" ) ); |
| 60 | + bool ok; |
| 61 | + int value = split.at( 0 ).toInt( &ok ); |
| 62 | + if ( !ok ) |
| 63 | + { |
| 64 | + continue; |
| 65 | + } |
| 66 | + |
| 67 | + if ( match.contains( "day", Qt::CaseInsensitive ) || |
| 68 | + match.contains( QObject::tr("day", "Note: Word is part matched in code"), Qt::CaseInsensitive )|| |
| 69 | + match.contains( QObject::tr("days", "Note: Word is part matched in code"), Qt::CaseInsensitive) ) |
| 70 | + seconds += value * QgsExpression::Interval::DAY; |
| 71 | + if ( match.contains( "week", Qt::CaseInsensitive ) || |
| 72 | + match.contains( QObject::tr("week", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 73 | + match.contains( QObject::tr("weeks", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 74 | + seconds += value * QgsExpression::Interval::WEEKS; |
| 75 | + if ( match.contains( "month", Qt::CaseInsensitive ) || |
| 76 | + match.contains( QObject::tr("month", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 77 | + match.contains( QObject::tr("months", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 78 | + seconds += value * QgsExpression::Interval::MONTHS; |
| 79 | + if ( match.contains( "year", Qt::CaseInsensitive ) || |
| 80 | + match.contains( QObject::tr("year", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 81 | + match.contains( QObject::tr("years", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 82 | + seconds += value * QgsExpression::Interval::YEARS; |
| 83 | + if ( match.contains( "second", Qt::CaseInsensitive ) || |
| 84 | + match.contains( QObject::tr("second", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 85 | + match.contains( QObject::tr("seconds", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 86 | + seconds += value; |
| 87 | + if ( match.contains( "minute", Qt::CaseInsensitive ) || |
| 88 | + match.contains( QObject::tr("minute", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 89 | + match.contains( QObject::tr("minutes", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 90 | + seconds += value * QgsExpression::Interval::MINUTE; |
| 91 | + if ( match.contains( "hour", Qt::CaseInsensitive ) || |
| 92 | + match.contains( QObject::tr("hour", "Note: Word is part matched in code"), Qt::CaseInsensitive ) || |
| 93 | + match.contains( QObject::tr("hours", "Note: Word is part matched in code"), Qt::CaseInsensitive ) ) |
| 94 | + seconds += value * QgsExpression::Interval::HOUR; |
| 95 | + } |
| 96 | + |
| 97 | + // If we can't parse the string at all then we just return invalid |
| 98 | + if ( seconds == 0 ) |
| 99 | + return QgsExpression::Interval::invalidInterVal(); |
| 100 | + |
| 101 | + return QgsExpression::Interval( seconds ); |
| 102 | +} |
| 103 | + |
| 104 | +bool QgsExpression::Interval::operator==( const QgsExpression::Interval& other ) const |
| 105 | +{ |
| 106 | + return ( mSeconds == other.mSeconds ); |
| 107 | +} |
33 | 108 |
|
34 | 109 | ///////////////////////////////////////////////
|
35 | 110 | // three-value logic
|
@@ -91,6 +166,26 @@ inline bool isDoubleSafe( const QVariant& v )
|
91 | 166 | return false;
|
92 | 167 | }
|
93 | 168 |
|
| 169 | +inline bool isDateTimeSafe( const QVariant& v ) |
| 170 | +{ |
| 171 | + return v.type() == QVariant::DateTime || v.type() == QVariant::Date || |
| 172 | + v.type() == QVariant::Time; |
| 173 | +} |
| 174 | + |
| 175 | +inline bool isIntervalSafe( const QVariant& v ) |
| 176 | +{ |
| 177 | + if ( v.canConvert<QgsExpression::Interval>() ) |
| 178 | + { |
| 179 | + return true; |
| 180 | + } |
| 181 | + |
| 182 | + if ( v.type() == QVariant::String ) |
| 183 | + { |
| 184 | + return QgsExpression::Interval::fromString( v.toString() ).isValid(); |
| 185 | + } |
| 186 | + return false; |
| 187 | +} |
| 188 | + |
94 | 189 | inline bool isNull( const QVariant& v ) { return v.isNull(); }
|
95 | 190 |
|
96 | 191 | ///////////////////////////////////////////////
|
@@ -167,6 +262,65 @@ static int getIntValue( const QVariant& value, QgsExpression* parent )
|
167 | 262 | }
|
168 | 263 | }
|
169 | 264 |
|
| 265 | +static QDateTime getDateTimeValue( const QVariant& value, QgsExpression* parent ) |
| 266 | +{ |
| 267 | + QDateTime d = value.toDateTime(); |
| 268 | + if ( d.isValid() ) |
| 269 | + { |
| 270 | + return d; |
| 271 | + } |
| 272 | + else |
| 273 | + { |
| 274 | + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) ); |
| 275 | + return QDateTime(); |
| 276 | + } |
| 277 | +} |
| 278 | + |
| 279 | +static QDate getDateValue( const QVariant& value, QgsExpression* parent ) |
| 280 | +{ |
| 281 | + QDate d = value.toDate(); |
| 282 | + if ( d.isValid() ) |
| 283 | + { |
| 284 | + return d; |
| 285 | + } |
| 286 | + else |
| 287 | + { |
| 288 | + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) ); |
| 289 | + return QDate(); |
| 290 | + } |
| 291 | +} |
| 292 | + |
| 293 | +static QTime getTimeValue( const QVariant& value, QgsExpression* parent ) |
| 294 | +{ |
| 295 | + QTime t = value.toTime(); |
| 296 | + if ( t.isValid() ) |
| 297 | + { |
| 298 | + return t; |
| 299 | + } |
| 300 | + else |
| 301 | + { |
| 302 | + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) ); |
| 303 | + return QTime(); |
| 304 | + } |
| 305 | +} |
| 306 | + |
| 307 | +static QgsExpression::Interval getInterval( const QVariant& value, QgsExpression* parent, bool report_error = false ) |
| 308 | +{ |
| 309 | + if ( value.canConvert<QgsExpression::Interval>() ) |
| 310 | + return value.value<QgsExpression::Interval>(); |
| 311 | + |
| 312 | + QgsExpression::Interval inter = QgsExpression::Interval::fromString( value.toString() ); |
| 313 | + if ( inter.isValid() ) |
| 314 | + { |
| 315 | + return inter; |
| 316 | + } |
| 317 | + // If we get here then we can't convert so we just error and return invalid. |
| 318 | + if ( report_error ) |
| 319 | + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Interval" ).arg( value.toString() ) ); |
| 320 | + |
| 321 | + return QgsExpression::Interval::invalidInterVal(); |
| 322 | +} |
| 323 | + |
170 | 324 |
|
171 | 325 | // this handles also NULL values
|
172 | 326 | static TVL getTVLValue( const QVariant& value, QgsExpression* parent )
|
@@ -270,6 +424,12 @@ static QVariant fcnToString( const QVariantList& values, QgsFeature* , QgsExpres
|
270 | 424 | {
|
271 | 425 | return QVariant( getStringValue( values.at( 0 ), parent ) );
|
272 | 426 | }
|
| 427 | + |
| 428 | +static QVariant fcnToDateTime( const QVariantList& values, QgsFeature* , QgsExpression* parent ) |
| 429 | +{ |
| 430 | + return QVariant( getDateTimeValue( values.at( 0 ), parent ) ); |
| 431 | +} |
| 432 | + |
273 | 433 | static QVariant fcnCoalesce( const QVariantList& values, QgsFeature* , QgsExpression* )
|
274 | 434 | {
|
275 | 435 | foreach( const QVariant &value, values )
|
@@ -344,6 +504,140 @@ static QVariant fcnConcat( const QVariantList& values, QgsFeature* , QgsExpressi
|
344 | 504 | return concat;
|
345 | 505 | }
|
346 | 506 |
|
| 507 | +static QVariant fcnNow( const QVariantList&, QgsFeature* , QgsExpression * ) |
| 508 | +{ |
| 509 | + return QVariant( QDateTime::currentDateTime() ); |
| 510 | +} |
| 511 | + |
| 512 | +static QVariant fcnToDate( const QVariantList& values, QgsFeature* , QgsExpression * parent ) |
| 513 | +{ |
| 514 | + return QVariant( getDateValue( values.at( 0 ), parent ) ); |
| 515 | +} |
| 516 | + |
| 517 | +static QVariant fcnToTime( const QVariantList& values, QgsFeature* , QgsExpression * parent ) |
| 518 | +{ |
| 519 | + return QVariant( getTimeValue( values.at( 0 ), parent ) ); |
| 520 | +} |
| 521 | + |
| 522 | +static QVariant fcnToInterval( const QVariantList& values, QgsFeature* , QgsExpression * parent ) |
| 523 | +{ |
| 524 | + return QVariant::fromValue( getInterval( values.at( 0 ), parent ) ); |
| 525 | +} |
| 526 | + |
| 527 | +static QVariant fcnAge( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 528 | +{ |
| 529 | + QDateTime d1 = getDateTimeValue( values.at( 0 ), parent ); |
| 530 | + QDateTime d2 = getDateTimeValue( values.at( 1 ), parent ); |
| 531 | + int seconds = d2.secsTo( d1 ); |
| 532 | + return QVariant::fromValue( QgsExpression::Interval( seconds ) ); |
| 533 | +} |
| 534 | + |
| 535 | +static QVariant fcnDay( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 536 | +{ |
| 537 | + QVariant value = values.at( 0 ); |
| 538 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 539 | + if ( inter.isValid() ) |
| 540 | + { |
| 541 | + return QVariant( inter.days() ); |
| 542 | + } |
| 543 | + else |
| 544 | + { |
| 545 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 546 | + return QVariant( d1.date().day() ); |
| 547 | + } |
| 548 | +} |
| 549 | + |
| 550 | +static QVariant fcnYear( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 551 | +{ |
| 552 | + QVariant value = values.at( 0 ); |
| 553 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 554 | + if ( inter.isValid() ) |
| 555 | + { |
| 556 | + return QVariant( inter.years() ); |
| 557 | + } |
| 558 | + else |
| 559 | + { |
| 560 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 561 | + return QVariant( d1.date().year() ); |
| 562 | + } |
| 563 | +} |
| 564 | + |
| 565 | +static QVariant fcnMonth( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 566 | +{ |
| 567 | + QVariant value = values.at( 0 ); |
| 568 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 569 | + if ( inter.isValid() ) |
| 570 | + { |
| 571 | + return QVariant( inter.months() ); |
| 572 | + } |
| 573 | + else |
| 574 | + { |
| 575 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 576 | + return QVariant( d1.date().month() ); |
| 577 | + } |
| 578 | +} |
| 579 | + |
| 580 | +static QVariant fcnWeek( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 581 | +{ |
| 582 | + QVariant value = values.at( 0 ); |
| 583 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 584 | + if ( inter.isValid() ) |
| 585 | + { |
| 586 | + return QVariant( inter.weeks() ); |
| 587 | + } |
| 588 | + else |
| 589 | + { |
| 590 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 591 | + return QVariant( d1.date().weekNumber() ); |
| 592 | + } |
| 593 | +} |
| 594 | + |
| 595 | +static QVariant fcnHour( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 596 | +{ |
| 597 | + QVariant value = values.at( 0 ); |
| 598 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 599 | + if ( inter.isValid() ) |
| 600 | + { |
| 601 | + return QVariant( inter.hours() ); |
| 602 | + } |
| 603 | + else |
| 604 | + { |
| 605 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 606 | + return QVariant( d1.time().hour() ); |
| 607 | + } |
| 608 | +} |
| 609 | + |
| 610 | +static QVariant fcnMinute( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 611 | +{ |
| 612 | + QVariant value = values.at( 0 ); |
| 613 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 614 | + if ( inter.isValid() ) |
| 615 | + { |
| 616 | + return QVariant( inter.minutes() ); |
| 617 | + } |
| 618 | + else |
| 619 | + { |
| 620 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 621 | + return QVariant( d1.time().minute() ); |
| 622 | + } |
| 623 | +} |
| 624 | + |
| 625 | +static QVariant fcnSeconds( const QVariantList& values, QgsFeature* , QgsExpression *parent ) |
| 626 | +{ |
| 627 | + QVariant value = values.at( 0 ); |
| 628 | + QgsExpression::Interval inter = getInterval( value, parent, false ); |
| 629 | + if ( inter.isValid() ) |
| 630 | + { |
| 631 | + return QVariant( inter.seconds() ); |
| 632 | + } |
| 633 | + else |
| 634 | + { |
| 635 | + QDateTime d1 = getDateTimeValue( value, parent ); |
| 636 | + return QVariant( d1.time().second() ); |
| 637 | + } |
| 638 | +} |
| 639 | + |
| 640 | + |
347 | 641 | #define ENSURE_GEOM_TYPE(f, g, geomtype) if (!f) return QVariant(); \
|
348 | 642 | QgsGeometry* g = f->geometry(); \
|
349 | 643 | if (!g || g->type() != geomtype) return QVariant();
|
@@ -450,7 +744,21 @@ const QList<QgsExpression::FunctionDef> &QgsExpression::BuiltinFunctions()
|
450 | 744 | << FunctionDef( "toint", 1, fcnToInt, QObject::tr( "Conversions" ) )
|
451 | 745 | << FunctionDef( "toreal", 1, fcnToReal, QObject::tr( "Conversions" ) )
|
452 | 746 | << FunctionDef( "tostring", 1, fcnToString, QObject::tr( "Conversions" ) )
|
| 747 | + << FunctionDef( "todatetime", 1, fcnToDateTime, QObject::tr( "Conversions" ) ) |
| 748 | + << FunctionDef( "todate", 1, fcnToDate, QObject::tr( "Conversions" ) ) |
| 749 | + << FunctionDef( "totime", 1, fcnToTime, QObject::tr( "Conversions" ) ) |
| 750 | + << FunctionDef( "tointerval", 1, fcnToInterval, QObject::tr( "Conversions" ) ) |
453 | 751 | << FunctionDef( "coalesce", -1, fcnCoalesce, QObject::tr( "Conversions" ) )
|
| 752 | + // date/time |
| 753 | + << FunctionDef( "$now", 0, fcnNow, QObject::tr( "Date/Time" ) ) |
| 754 | + << FunctionDef( "age", 2, fcnAge, QObject::tr( "Date/Time" ) ) |
| 755 | + << FunctionDef( "year", 1, fcnYear, QObject::tr( "Date/Time" ) ) |
| 756 | + << FunctionDef( "month", 1, fcnMonth, QObject::tr( "Date/Time" ) ) |
| 757 | + << FunctionDef( "week", 1, fcnWeek, QObject::tr( "Date/Time" ) ) |
| 758 | + << FunctionDef( "day", 1, fcnDay, QObject::tr( "Date/Time" ) ) |
| 759 | + << FunctionDef( "hour", 1, fcnHour, QObject::tr( "Date/Time" ) ) |
| 760 | + << FunctionDef( "minute", 1, fcnMinute, QObject::tr( "Date/Time" ) ) |
| 761 | + << FunctionDef( "second", 1, fcnSeconds, QObject::tr( "Date/Time" ) ) |
454 | 762 | // string manipulation
|
455 | 763 | << FunctionDef( "lower", 1, fcnLower, QObject::tr( "String" ) )
|
456 | 764 | << FunctionDef( "upper", 1, fcnUpper, QObject::tr( "String" ) )
|
@@ -905,6 +1213,17 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
|
905 | 1213 | if ( mOp == boDiv && iR == 0 ) return QVariant(); // silently handle division by zero and return NULL
|
906 | 1214 | return QVariant( computeInt( iL, iR ) );
|
907 | 1215 | }
|
| 1216 | + else if ( isDateTimeSafe( vL ) && isIntervalSafe( vR ) ) |
| 1217 | + { |
| 1218 | + QDateTime dL = getDateTimeValue( vL, parent ); ENSURE_NO_EVAL_ERROR; |
| 1219 | + QgsExpression::Interval iL = getInterval( vR, parent ); ENSURE_NO_EVAL_ERROR; |
| 1220 | + if ( mOp == boDiv || mOp == boMul || mOp == boMod ) |
| 1221 | + { |
| 1222 | + parent->setEvalErrorString( QObject::tr("Can't preform /, *, or % on DateTime and Interval") ); |
| 1223 | + return QVariant(); |
| 1224 | + } |
| 1225 | + return QVariant( computeDateTimeFromInterval( dL, &iL ) ); |
| 1226 | + } |
908 | 1227 | else
|
909 | 1228 | {
|
910 | 1229 | // general floating point arithmetic
|
@@ -1068,6 +1387,16 @@ int QgsExpression::NodeBinaryOperator::computeInt( int x, int y )
|
1068 | 1387 | }
|
1069 | 1388 | }
|
1070 | 1389 |
|
| 1390 | +QDateTime QgsExpression::NodeBinaryOperator::computeDateTimeFromInterval( QDateTime d, QgsExpression::Interval *i ) |
| 1391 | +{ |
| 1392 | + switch ( mOp ) |
| 1393 | + { |
| 1394 | + case boPlus: return d.addSecs( i->seconds() ); |
| 1395 | + case boMinus: return d.addSecs( -i->seconds() ); |
| 1396 | + default: Q_ASSERT( false ); return QDateTime(); |
| 1397 | + } |
| 1398 | +} |
| 1399 | + |
1071 | 1400 | double QgsExpression::NodeBinaryOperator::computeDouble( double x, double y )
|
1072 | 1401 | {
|
1073 | 1402 | switch ( mOp )
|
|
0 commit comments