Add expression functions for arrays and maps

This commit is contained in:
Patrick Valsecchi 2016-09-12 10:30:05 +02:00 committed by Patrick Valsecchi
parent cd1d44be9d
commit f622c5bf2c
24 changed files with 561 additions and 4 deletions

2
.gitignore vendored
View File

@ -51,6 +51,8 @@ scripts/astyle.exe
scripts/Debug
scripts/qgisstyle*
scripts/RelWithDebInfo
src/core/qgscontexthelp_texts.cpp
src/core/qgsexpression_texts.cpp
tests/testdata/cache/
tests/testdata/checker360by180.asc.aux.xml
tests/testdata/grass/wgs84/test/.gislock

View File

@ -0,0 +1,12 @@
{
"name": "array",
"type": "function",
"description": "Returns an array containing all the values passed as parameter.",
"variableLenArguments": true,
"arguments": [
{"arg":"value1", "syntaxOnly": true},
{"arg":"value2", "syntaxOnly": true},
{"arg":"value", "descOnly": true, "description":"a value"}],
"examples": [ { "expression":"array(2,10)", "returns":"array: 2, 10"}
]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_append",
"type": "function",
"description": "Returns an array with the given value added at the end.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to add"}],
"examples": [ { "expression":"array_append(array(1,2,3),4)", "returns":"array: 1,2,3,4"}]
}

View File

@ -0,0 +1,12 @@
{
"name": "array_cat",
"type": "function",
"description": "Returns an array containing all the given arrays concatenated.",
"variableLenArguments": true,
"arguments": [
{"arg":"array1", "syntaxOnly": true},
{"arg":"array2", "syntaxOnly": true},
{"arg":"array", "descOnly": true, "description":"an array"}],
"examples": [ { "expression":"array_cat(array(1,2),array(2,3))", "returns":"array: 1,2,2,3"}
]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_contains",
"type": "function",
"description": "Returns true if an array contains the given value.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to search"}],
"examples": [ { "expression":"array_contains(array(1,2,3),2)", "returns":"true"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_find",
"type": "function",
"description": "Returns the index (0 for the first one) of a value within an array. Returns -1 if the value is not found.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to search"}],
"examples": [ { "expression":"array_find(array(1,2,3),2)", "returns":"1"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_get",
"type": "function",
"description": "Returns the Nth value (0 for the first one) of an array.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"index","description":"the index to get (0 based)"}],
"examples": [ { "expression":"array_get(array('a','b','c'),1)", "returns":"'b'"}]
}

View File

@ -0,0 +1,9 @@
{
"name": "array_insert",
"type": "function",
"description": "Returns an array with the given value added at the given position.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"pos","description":"the position where to add (0 based"},
{"arg":"value","description":"the value to add"}],
"examples": [ { "expression":"array_insert(array(1,2,3),1,100)", "returns":"array: 1,100,2,3"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_intersect",
"type": "function",
"description": "Returns true if any element of array1 exists in array2.",
"arguments": [ {"arg":"array1","description":"an array"},
{"arg":"array2","description":"an other array"}],
"examples": [ { "expression":"array_intersect(array(1,2,3,4),array(4,0,2,5))", "returns":"true"}]
}

View File

@ -0,0 +1,7 @@
{
"name": "array_length",
"type": "function",
"description": "Returns the number of elements of an array.",
"arguments": [ {"arg":"array","description":"an array"}],
"examples": [ { "expression":"array_length(array(1,2,3))", "returns":"3"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_prepend",
"type": "function",
"description": "Returns an array with the given value added at the begining.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to add"}],
"examples": [ { "expression":"array_prepend(array(1,2,3),0)", "returns":"array: 0,1,2,3"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_remove_all",
"type": "function",
"description": "Returns an array with all the entries of the given value removed.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to add"}],
"examples": [ { "expression":"array_remove_all(array('a','b','c','b'),'b')", "returns":"array: 'a','c'"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "array_remove_at",
"type": "function",
"description": "Returns an array with the given index removed.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"pos","description":"the position to remove (0 based"}],
"examples": [ { "expression":"array_remove_at(array(1,2,3),1)", "returns":"array: 1,3"}]
}

View File

@ -0,0 +1,15 @@
{
"name": "map",
"type": "function",
"description": "Returns a map containing all the keys and values passed as pair of parameters.",
"variableLenArguments": true,
"arguments": [
{"arg":"key1", "syntaxOnly": true},
{"arg":"value1", "syntaxOnly": true},
{"arg":"key2", "syntaxOnly": true},
{"arg":"value2", "syntaxOnly": true},
{"arg":"key", "descOnly": true, "description":"a key (string)"},
{"arg":"value", "descOnly": true, "description":"a value"}],
"examples": [ { "expression":"map('1','one','2', 'two')", "returns":"map: 1: 'one', 2: 'two'"}
]
}

View File

@ -0,0 +1,7 @@
{
"name": "map_akeys",
"type": "function",
"description": "Returns all the keys of a map as an array.",
"arguments": [ {"arg":"map","description":"a map"}],
"examples": [ { "expression":"map_akeys(map('1','one','2','two'))", "returns":"array: '1', '2'"}]
}

View File

@ -0,0 +1,7 @@
{
"name": "map_avals",
"type": "function",
"description": "Returns all the values of a map as an array.",
"arguments": [ {"arg":"map","description":"a map"}],
"examples": [ { "expression":"map_avals(map('1','one','2','two'))", "returns":"array: 'one', 'two'"}]
}

View File

@ -0,0 +1,12 @@
{
"name": "map_concat",
"type": "function",
"description": "Returns a map containing all the entries of the given map. If two maps contain the same key, the value of the second map is taken.",
"variableLenArguments": true,
"arguments": [
{"arg":"map1", "syntaxOnly": true},
{"arg":"map2", "syntaxOnly": true},
{"arg":"map", "descOnly": true, "description":"a map"}],
"examples": [ { "expression":"map_concat(map('1','one', '2','overriden'),map('2','two', '3','three'))", "returns":"map: 1: 'one, 2: 'two', 3: 'three'"}
]
}

View File

@ -0,0 +1,8 @@
{
"name": "map_delete",
"type": "function",
"description": "Returns a map with the given key and its corresponding value deleted.",
"arguments": [ {"arg":"map","description":"a map"},
{"arg":"key","description":"the key to delete"}],
"examples": [ { "expression":"map_delete(map('1','one','2','two'),'2')", "returns":"map: 1: 'one'"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "map_exist",
"type": "function",
"description": "Returns true if the given key exists in the map.",
"arguments": [ {"arg":"map","description":"a map"},
{"arg":"key","description":"the key to lookup"}],
"examples": [ { "expression":"map_exist(map('1','one','2','two'),'3')", "returns":"false"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "map_get",
"type": "function",
"description": "Returns the value of a map, given it's key.",
"arguments": [ {"arg":"map","description":"a map"},
{"arg":"key","description":"the key to lookup"}],
"examples": [ { "expression":"map_get(map('1','one','2','two'),'2')", "returns":"'two'"}]
}

View File

@ -0,0 +1,9 @@
{
"name": "map_insert",
"type": "function",
"description": "Returns a map with an added key/value.",
"arguments": [ {"arg":"map","description":"a map"},
{"arg":"key","description":"the key to add"},
{"arg":"value","description":"the value to add"}],
"examples": [ { "expression":"map_insert(map('1','one'),'3','three')", "returns":"map: 1: 'one', 3: 'three'"}]
}

View File

@ -365,6 +365,32 @@ static TVL getTVLValue( const QVariant& value, QgsExpression* parent )
return !qgsDoubleNear( x, 0.0 ) ? True : False;
}
static QVariantList getListValue( const QVariant& value, QgsExpression* parent )
{
if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
{
return value.toList();
}
else
{
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
return QVariantList();
}
}
static QVariantMap getMapValue( const QVariant& value, QgsExpression* parent )
{
if ( value.type() == QVariant::Map )
{
return value.toMap();
}
else
{
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
return QVariantMap();
}
}
//////
static QVariant fcnGetVariable( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent )
@ -3156,6 +3182,150 @@ static QVariant fcnGetLayerProperty( const QVariantList& values, const QgsExpres
return QVariant();
}
static QVariant fcnArray( const QVariantList& values, const QgsExpressionContext*, QgsExpression* )
{
return values;
}
static QVariant fcnArrayLength( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return getListValue( values.at( 0 ), parent ).length();
}
static QVariant fcnArrayContains( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return QVariant( getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
}
static QVariant fcnArrayFind( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
}
static QVariant fcnArrayGet( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
const QVariantList list = getListValue( values.at( 0 ), parent );
const int pos = getIntValue( values.at( 1 ), parent );
if ( pos < 0 || pos >= list.length() ) return QVariant();
return list.at( pos );
}
static QVariant convertToSameType( const QVariant& value, QVariant::Type type )
{
QVariant result = value;
result.convert( type );
return result;
}
static QVariant fcnArrayAppend( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list = getListValue( values.at( 0 ), parent );
list.append( values.at( 1 ) );
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayPrepend( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list = getListValue( values.at( 0 ), parent );
list.prepend( values.at( 1 ) );
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayInsert( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list = getListValue( values.at( 0 ), parent );
list.insert( getIntValue( values.at( 1 ), parent ), values.at( 2 ) );
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayRemoveAt( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list = getListValue( values.at( 0 ), parent );
list.removeAt( getIntValue( values.at( 1 ), parent ) );
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayRemoveAll( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list = getListValue( values.at( 0 ), parent );
list.removeAll( values.at( 1 ) );
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayCat( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantList list;
Q_FOREACH ( QVariant cur, values )
{
list += getListValue( cur, parent );
}
return convertToSameType( list , values.at( 0 ).type() );
}
static QVariant fcnArrayIntersect( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
const QVariantList array1 = getListValue( values.at( 0 ), parent );
Q_FOREACH ( QVariant cur, getListValue( values.at( 1 ), parent ) ) if ( array1.contains( cur ) ) return QVariant( true );
return QVariant( false );
}
static QVariant fcnMap( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantMap result;
for ( int i = 0; i + 1 < values.length(); i += 2 )
{
result.insert( getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
}
return result;
}
static QVariant fcnMapGet( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
}
static QVariant fcnMapExist( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
}
static QVariant fcnMapDelete( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantMap map = getMapValue( values.at( 0 ), parent );
map.remove( values.at( 1 ).toString() );
return map;
}
static QVariant fcnMapInsert( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantMap map = getMapValue( values.at( 0 ), parent );
map.insert( values.at( 1 ).toString(), values.at( 2 ) );
return map;
}
static QVariant fcnMapConcat( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QVariantMap result;
Q_FOREACH ( QVariant cur, values )
{
const QVariantMap curMap = getMapValue( cur, parent );
for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
result.insert( it.key(), it.value() );
}
return result;
}
static QVariant fcnMapAKeys( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return QStringList( getMapValue( values.at( 0 ), parent ).keys() );
}
static QVariant fcnMapAVals( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
return getMapValue( values.at( 0 ), parent ).values();
}
bool QgsExpression::registerFunction( QgsExpression::Function* function, bool transferOwnership )
{
int fnIdx = functionIndex( function->name() );
@ -3491,6 +3661,30 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
// feature request
<< new StaticFunction( "eval", 1, fcnEval, "General", QString(), true, QStringList( QgsFeatureRequest::AllAttributes ) )
<< new StaticFunction( "attribute", 2, fcnAttribute, "Record", QString(), false, QStringList( QgsFeatureRequest::AllAttributes ) )
// functions for arrays
<< new StaticFunction( "array", -1, fcnArray, "Arrays" )
<< new StaticFunction( "array_length", 1, fcnArrayLength, "Arrays" )
<< new StaticFunction( "array_contains", ParameterList() << Parameter( "array" ) << Parameter( "value" ), fcnArrayContains, "Arrays" )
<< new StaticFunction( "array_find", ParameterList() << Parameter( "array" ) << Parameter( "value" ), fcnArrayFind, "Arrays" )
<< new StaticFunction( "array_get", ParameterList() << Parameter( "array" ) << Parameter( "pos" ), fcnArrayGet, "Arrays" )
<< new StaticFunction( "array_append", ParameterList() << Parameter( "array" ) << Parameter( "value" ), fcnArrayAppend, "Arrays" )
<< new StaticFunction( "array_prepend", ParameterList() << Parameter( "array" ) << Parameter( "value" ), fcnArrayPrepend, "Arrays" )
<< new StaticFunction( "array_insert", ParameterList() << Parameter( "array" ) << Parameter( "pos" ) << Parameter( "value" ), fcnArrayInsert, "Arrays" )
<< new StaticFunction( "array_remove_at", ParameterList() << Parameter( "array" ) << Parameter( "pos" ), fcnArrayRemoveAt, "Arrays" )
<< new StaticFunction( "array_remove_all", ParameterList() << Parameter( "array" ) << Parameter( "value" ), fcnArrayRemoveAll, "Arrays" )
<< new StaticFunction( "array_cat", -1, fcnArrayCat, "Arrays" )
<< new StaticFunction( "array_intersect", ParameterList() << Parameter( "array1" ) << Parameter( "array2" ), fcnArrayIntersect, "Arrays" )
//functions for maps
<< new StaticFunction( "map", -1, fcnMap, "Maps" )
<< new StaticFunction( "map_get", ParameterList() << Parameter( "map" ) << Parameter( "key" ), fcnMapGet, "Maps" )
<< new StaticFunction( "map_exist", ParameterList() << Parameter( "map" ) << Parameter( "key" ), fcnMapExist, "Maps" )
<< new StaticFunction( "map_delete", ParameterList() << Parameter( "map" ) << Parameter( "key" ), fcnMapDelete, "Maps" )
<< new StaticFunction( "map_insert", ParameterList() << Parameter( "map" ) << Parameter( "key" ) << Parameter( "value" ), fcnMapInsert, "Maps" )
<< new StaticFunction( "map_concat", -1, fcnMapConcat, "Maps" )
<< new StaticFunction( "map_akeys", ParameterList() << Parameter( "map" ), fcnMapAKeys, "Maps" )
<< new StaticFunction( "map_avals", ParameterList() << Parameter( "map" ), fcnMapAVals, "Maps" )
;
QgsExpressionContextUtils::registerContextFunctions();
@ -5088,7 +5282,7 @@ QString QgsExpression::formatPreviewString( const QVariant& value )
break;
}
}
return tr( "<i>&lt;list: %1&gt;</i>" ).arg( listStr );
return tr( "<i>&lt;array: %1&gt;</i>" ).arg( listStr );
}
else
{

View File

@ -92,5 +92,5 @@ Qt::AlignmentFlag QgsKeyValueWidgetFactory::alignmentFlag( QgsVectorLayer *vl, i
unsigned int QgsKeyValueWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
const QgsField field = vl->fields().field( fieldIdx );
return field.type() == QVariant::Map ? 20 : 0;
return field.type() == QVariant::Map && field.subType() != QVariant::Invalid ? 20 : 0;
}

View File

@ -2148,6 +2148,189 @@ class TestQgsExpression: public QObject
QCOMPARE( v4, QVariant( "test value" ) );
}
void eval_string_array()
{
QgsFeature f( 100 );
QgsFields fields;
fields.append( QgsField( "col1" ) );
fields.append( QgsField( "strings", QVariant::StringList, "string[]", 0, 0, QString(), QVariant::String ) );
f.setFields( fields, true );
f.setAttribute( "col1", QString( "test value" ) );
QStringList array;
array << "one" << "two";
f.setAttribute( "strings", array );
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, QgsFields() );
QVariantList builderExpected;
QCOMPARE( QgsExpression( "array()" ).evaluate( &context ), QVariant( ) );
builderExpected << "hello";
QCOMPARE( QgsExpression( "array('hello')" ).evaluate( &context ), QVariant( builderExpected ) );
builderExpected << "world";
QCOMPARE( QgsExpression( "array('hello', 'world')" ).evaluate( &context ), QVariant( builderExpected ) );
QCOMPARE( QgsExpression( "array_length(\"strings\")" ).evaluate( &context ), QVariant( 2 ) );
QCOMPARE( QgsExpression( "array_contains(\"strings\", 'two')" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "array_contains(\"strings\", 'three')" ).evaluate( &context ), QVariant( false ) );
QCOMPARE( QgsExpression( "array_find(\"strings\", 'two')" ).evaluate( &context ), QVariant( 1 ) );
QCOMPARE( QgsExpression( "array_find(\"strings\", 'three')" ).evaluate( &context ), QVariant( -1 ) );
QCOMPARE( QgsExpression( "array_get(\"strings\", 1)" ).evaluate( &context ), QVariant( "two" ) );
QCOMPARE( QgsExpression( "array_get(\"strings\", 2)" ).evaluate( &context ), QVariant() );
QCOMPARE( QgsExpression( "array_get(\"strings\", -1)" ).evaluate( &context ), QVariant() );
QStringList appendExpected = array;
appendExpected << "three";
QCOMPARE( QgsExpression( "array_append(\"strings\", 'three')" ).evaluate( &context ), QVariant( appendExpected ) );
QStringList prependExpected = array;
prependExpected.prepend( "zero" );
QCOMPARE( QgsExpression( "array_prepend(\"strings\", 'zero')" ).evaluate( &context ), QVariant( prependExpected ) );
QStringList insertExpected = array;
insertExpected.insert( 1, "one and a half" );
QCOMPARE( QgsExpression( "array_insert(\"strings\", 1, 'one and a half')" ).evaluate( &context ), QVariant( insertExpected ) );
QStringList removeAtExpected = array;
removeAtExpected.removeAt( 0 );
QCOMPARE( QgsExpression( "array_remove_at(\"strings\", 0)" ).evaluate( &context ), QVariant( removeAtExpected ) );
QStringList removeAllExpected;
removeAllExpected << "a" << "b" << "d";
QCOMPARE( QgsExpression( "array_remove_all(array('a', 'b', 'c', 'd', 'c'), 'c')" ).evaluate( &context ), QVariant( removeAllExpected ) );
QStringList concatExpected = array;
concatExpected << "a" << "b" << "c";
QCOMPARE( QgsExpression( "array_cat(\"strings\", array('a', 'b'), array('c'))" ).evaluate( &context ), QVariant( concatExpected ) );
QCOMPARE( QgsExpression( "array_intersect(array('1', '2', '3', '4'), array('4', '0', '2', '5'))" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "array_intersect(array('1', '2', '3', '4'), array('0', '5'))" ).evaluate( &context ), QVariant( false ) );
}
void eval_int_array()
{
QgsFeature f( 100 );
QgsFields fields;
fields.append( QgsField( "col1" ) );
fields.append( QgsField( "ints", QVariant::List, "int[]", 0, 0, QString(), QVariant::Int ) );
f.setFields( fields, true );
f.setAttribute( "col1", QString( "test value" ) );
QVariantList array;
array << 1 << -2;
f.setAttribute( "ints", array );
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, QgsFields() );
QVariantList builderExpected;
builderExpected << 1;
QCOMPARE( QgsExpression( "array(1)" ).evaluate( &context ), QVariant( builderExpected ) );
builderExpected << 2;
QCOMPARE( QgsExpression( "array(1, 2)" ).evaluate( &context ), QVariant( builderExpected ) );
QCOMPARE( QgsExpression( "array_contains(\"ints\", 1)" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "array_contains(\"ints\", 2)" ).evaluate( &context ), QVariant( false ) );
QCOMPARE( QgsExpression( "array_find(\"ints\", -2)" ).evaluate( &context ), QVariant( 1 ) );
QCOMPARE( QgsExpression( "array_find(\"ints\", 3)" ).evaluate( &context ), QVariant( -1 ) );
QCOMPARE( QgsExpression( "array_get(\"ints\", 1)" ).evaluate( &context ), QVariant( -2 ) );
QCOMPARE( QgsExpression( "array_get(\"ints\", 2)" ).evaluate( &context ), QVariant() );
QCOMPARE( QgsExpression( "array_get(\"ints\", -1)" ).evaluate( &context ), QVariant() );
QVariantList appendExpected = array;
appendExpected << 3;
QCOMPARE( QgsExpression( "array_append(\"ints\", 3)" ).evaluate( &context ), QVariant( appendExpected ) );
QVariantList prependExpected = array;
prependExpected.prepend( 0 );
QCOMPARE( QgsExpression( "array_prepend(\"ints\", 0)" ).evaluate( &context ), QVariant( prependExpected ) );
QVariantList insertExpected = array;
insertExpected.insert( 1, 2 );
QCOMPARE( QgsExpression( "array_insert(\"ints\", 1, 2)" ).evaluate( &context ), QVariant( insertExpected ) );
QVariantList removeAtExpected = array;
removeAtExpected.removeAt( 0 );
QCOMPARE( QgsExpression( "array_remove_at(\"ints\", 0)" ).evaluate( &context ), QVariant( removeAtExpected ) );
QVariantList removeAllExpected;
removeAllExpected << 1 << 2 << 4;
QCOMPARE( QgsExpression( "array_remove_all(array(1, 2, 3, 4, 3), 3)" ).evaluate( &context ), QVariant( removeAllExpected ) );
QCOMPARE( QgsExpression( "array_remove_all(array(1, 2, 3, 4, 3), '3')" ).evaluate( &context ), QVariant( removeAllExpected ) );
QVariantList concatExpected = array;
concatExpected << 56 << 57;
QCOMPARE( QgsExpression( "array_cat(\"ints\", array(56, 57))" ).evaluate( &context ), QVariant( concatExpected ) );
QCOMPARE( QgsExpression( "array_intersect(array(1, 2, 3, 4), array(4, 0, 2, 5))" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "array_intersect(array(1, 2, 3, 4), array(0, 5))" ).evaluate( &context ), QVariant( false ) );
QgsExpression badArray( "array_get('not an array', 0)" );
QCOMPARE( badArray.evaluate( &context ), QVariant() );
QVERIFY( badArray.hasEvalError() );
QCOMPARE( badArray.evalErrorString(), QString( "Cannot convert 'not an array' to array" ) );
}
void eval_map()
{
QgsFeature f( 100 );
QgsFields fields;
fields.append( QgsField( "col1" ) );
fields.append( QgsField( "map", QVariant::Map, "map", 0, 0, QString(), QVariant::String ) );
f.setFields( fields, true );
f.setAttribute( "col1", QString( "test value" ) );
QVariantMap map;
map["1"] = "one";
map["2"] = "two";
f.setAttribute( "map", map );
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, QgsFields() );
QVariantMap builderExpected;
QCOMPARE( QgsExpression( "map()" ).evaluate( &context ), QVariant( ) );
builderExpected["1"] = "hello";
QCOMPARE( QgsExpression( "map('1', 'hello')" ).evaluate( &context ), QVariant( builderExpected ) );
builderExpected["2"] = "world";
QCOMPARE( QgsExpression( "map('1', 'hello', '2', 'world')" ).evaluate( &context ), QVariant( builderExpected ) );
QCOMPARE( QgsExpression( "map('1', 'hello', '2', 'world', 'ignoredOddParam')" ).evaluate( &context ), QVariant( builderExpected ) );
QCOMPARE( QgsExpression( "map_get(\"map\", '2')" ).evaluate( &context ), QVariant( "two" ) );
QCOMPARE( QgsExpression( "map_get(\"map\", '3')" ).evaluate( &context ), QVariant() );
QCOMPARE( QgsExpression( "map_exist(\"map\", '2')" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "map_exist(\"map\", '3')" ).evaluate( &context ), QVariant( false ) );
QVariantMap deleteExpected = map;
deleteExpected.remove( "1" );
QCOMPARE( QgsExpression( "map_delete(\"map\", '1')" ).evaluate( &context ), QVariant( deleteExpected ) );
QCOMPARE( QgsExpression( "map_delete(\"map\", '3')" ).evaluate( &context ), QVariant( map ) );
QVariantMap insertExpected = map;
insertExpected.insert( "3", "three" );
QCOMPARE( QgsExpression( "map_insert(\"map\", '3', 'three')" ).evaluate( &context ), QVariant( insertExpected ) );
QVariantMap concatExpected;
concatExpected["1"] = "one";
concatExpected["2"] = "two";
concatExpected["3"] = "three";
QCOMPARE( QgsExpression( "map_concat(map('1', 'one', '2', 'overriden by next map'), map('2', 'two', '3', 'three'))" ).evaluate( &context ), QVariant( concatExpected ) );
QStringList keysExpected;
keysExpected << "1" << "2";
QCOMPARE( QgsExpression( "map_akeys(\"map\")" ).evaluate( &context ), QVariant( keysExpected ) );
QVariantList valuesExpected;
valuesExpected << "one" << "two";
QCOMPARE( QgsExpression( "map_avals(\"map\")" ).evaluate( &context ), QVariant( valuesExpected ) );
QgsExpression badMap( "map_get('not a map', '0')" );
QCOMPARE( badMap.evaluate( &context ), QVariant() );
QVERIFY( badMap.hasEvalError() );
QCOMPARE( badMap.evalErrorString(), QString( "Cannot convert 'not a map' to map" ) );
}
void expression_from_expression_data()
{
QTest::addColumn<QString>( "string" );
@ -2346,12 +2529,12 @@ class TestQgsExpression: public QObject
QVariantList list;
list << 1 << 2 << 3;
QCOMPARE( QgsExpression::formatPreviewString( QVariant( list ) ), QString( "<i>&lt;list: 1, 2, 3&gt;</i>" ) );
QCOMPARE( QgsExpression::formatPreviewString( QVariant( list ) ), QString( "<i>&lt;array: 1, 2, 3&gt;</i>" ) );
QStringList stringList;
stringList << "One" << "Two" << "A very long string that is going to be truncated";
QCOMPARE( QgsExpression::formatPreviewString( QVariant( stringList ) ),
QString( "<i>&lt;list: 'One', 'Two', 'A very long string that is going to be trunca...&gt;</i>" ) );
QString( "<i>&lt;array: 'One', 'Two', 'A very long string that is going to be trunca...&gt;</i>" ) );
}
};