Skip to content
Permalink
Browse files

Add lazy evaluation support to expression functions

Add if(cond, true, false) function using lazy evaluation feature
  • Loading branch information
NathanW2 committed Dec 13, 2014
1 parent fd15a16 commit d61bf1f70f5ad079407a0c9da5bb1ef8d09013f7
Showing with 55 additions and 8 deletions.
  1. +42 −4 src/core/qgsexpression.cpp
  2. +11 −4 src/core/qgsexpression.h
  3. +2 −0 tests/src/core/testqgsexpression.cpp
@@ -338,6 +338,15 @@ static QgsFeature getFeature( const QVariant& value, QgsExpression* parent )
return 0;
}

static QgsExpression::Node* getNode( const QVariant& value, QgsExpression* parent )
{
if ( value.canConvert<QgsExpression::Node*>() )
return value.value<QgsExpression::Node*>();

parent->setEvalErrorString( "Cannot convert to Node" );
return 0;
}

// this handles also NULL values
static TVL getTVLValue( const QVariant& value, QgsExpression* parent )
{
@@ -1355,6 +1364,27 @@ static QVariant fcnColorRgb( const QVariantList &values, const QgsFeature *, Qgs
return QString( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
}

static QVariant fcnIf( const QVariantList &values, const QgsFeature *f, QgsExpression *parent )
{
QgsExpression::Node* node = getNode( values.at( 0 ), parent );
ENSURE_NO_EVAL_ERROR;
QVariant value = node->eval( parent, f );
ENSURE_NO_EVAL_ERROR;
if ( value.toBool() ) {
node = getNode( values.at( 1 ), parent );
ENSURE_NO_EVAL_ERROR;
value = node->eval( parent, f );
ENSURE_NO_EVAL_ERROR;
}
else {
node = getNode( values.at( 2 ), parent );
ENSURE_NO_EVAL_ERROR;
value = node->eval( parent, f );
ENSURE_NO_EVAL_ERROR;
}
return value;
}

static QVariant fncColorRgba( const QVariantList &values, const QgsFeature *, QgsExpression *parent )
{
int red = getIntValue( values.at( 0 ), parent );
@@ -1660,6 +1690,7 @@ const QList<QgsExpression::Function*> &QgsExpression::Functions()
<< new StaticFunction( "totime", 1, fcnToTime, "Conversions" )
<< new StaticFunction( "tointerval", 1, fcnToInterval, "Conversions" )
<< new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals" )
<< new StaticFunction( "if", 3, fcnIf, "Conditionals", "", False, QStringList(), true )
<< new StaticFunction( "regexp_match", 2, fcnRegexpMatch, "Conditionals" )
<< new StaticFunction( "$now", 0, fcnNow, "Date and Time" )
<< new StaticFunction( "age", 2, fcnAge, "Date and Time" )
@@ -2533,10 +2564,17 @@ QVariant QgsExpression::NodeFunction::eval( QgsExpression* parent, const QgsFeat
{
foreach ( Node* n, mArgs->list() )
{
QVariant v = n->eval( parent, f );
ENSURE_NO_EVAL_ERROR;
if ( isNull( v ) && fd->name() != "coalesce" )
return QVariant(); // all "normal" functions return NULL, when any parameter is NULL (so coalesce is abnormal)
QVariant v;
if ( fd->lazyEval() ) {
// Pass in the node for the function to eval as it needs.
v = QVariant::fromValue( n );
}
else {
v = n->eval( parent, f );
ENSURE_NO_EVAL_ERROR;
if ( isNull( v ) && fd->name() != "coalesce" )
return QVariant(); // all "normal" functions return NULL, when any parameter is NULL (so coalesce is abnormal)
}
argValues.append( v );
}
}
@@ -282,15 +282,20 @@ class CORE_EXPORT QgsExpression
class CORE_EXPORT Function
{
public:
Function( QString fnname, int params, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList() )
: mName( fnname ), mParams( params ), mUsesGeometry( usesGeometry ), mGroup( group ), mHelpText( helpText ), mReferencedColumns( referencedColumns ) {}
Function( QString fnname, int params, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList(), bool lazyEval = false )
: mName( fnname ), mParams( params ), mUsesGeometry( usesGeometry ), mGroup( group ), mHelpText( helpText ), mReferencedColumns( referencedColumns ), mLazyEval( lazyEval ) {}
/** The name of the function. */
QString name() { return mName; }
/** The number of parameters this function takes. */
int params() { return mParams; }
/** Does this function use a geometry object. */
bool usesgeometry() { return mUsesGeometry; }

/** True if this function should use lazy evaluation. Lazy evaluation functions take QgsExpression::Node objects
* rather than the node results when called. You can use node->eval(parent, feature) to evaluate the node and return the result
* Functions are non lazy default and will be given the node return value when called **/
bool lazyEval() { return mLazyEval; }

virtual QStringList referencedColumns() const { return mReferencedColumns; }

/** The group the function belongs to. */
@@ -315,13 +320,14 @@ class CORE_EXPORT QgsExpression
QString mGroup;
QString mHelpText;
QStringList mReferencedColumns;
bool mLazyEval;
};

class StaticFunction : public Function
{
public:
StaticFunction( QString fnname, int params, FcnEval fcn, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList() )
: Function( fnname, params, group, helpText, usesGeometry, referencedColumns ), mFnc( fcn ) {}
StaticFunction( QString fnname, int params, FcnEval fcn, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList(), bool lazyEval = false )
: Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval ), mFnc( fcn ) {}

virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent )
{
@@ -680,5 +686,6 @@ class CORE_EXPORT QgsExpression
};

Q_DECLARE_METATYPE( QgsExpression::Interval );
Q_DECLARE_METATYPE( QgsExpression::Node* );

#endif // QGSEXPRESSION_H
@@ -377,6 +377,8 @@ class TestQgsExpression: public QObject
QTest::newRow( "regexp match invalid" ) << "regexp_match('abc DEF','[[[')" << true << QVariant();
QTest::newRow( "regexp match escaped" ) << "regexp_match('abc DEF','\\\\s[A-Z]+')" << false << QVariant( 1 );
QTest::newRow( "regexp match false" ) << "regexp_match('abc DEF','\\\\s[a-z]+')" << false << QVariant( 0 );
QTest::newRow( "if true" ) << "if(1=1, 1, 0)" << false << QVariant( 1 );
QTest::newRow( "if false" ) << "if(1=2, 1, 0)" << false << QVariant( 0 );

// Datetime functions
QTest::newRow( "to date" ) << "todate('2012-06-28')" << false << QVariant( QDate( 2012, 6, 28 ) );

0 comments on commit d61bf1f

Please sign in to comment.
You can’t perform that action at this time.