diff --git a/resources/function_help/json/base_file_name b/resources/function_help/json/base_file_name new file mode 100644 index 00000000000..be308668564 --- /dev/null +++ b/resources/function_help/json/base_file_name @@ -0,0 +1,8 @@ +{ + "name": "base_file_name", + "type": "function", + "description": "Returns the base name of the file without the directory or file suffix.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"base_file_name('/home/qgis/data/country_boundaries.shp')", "returns":"'country_boundaries'"}] +} + diff --git a/resources/function_help/json/file_exists b/resources/function_help/json/file_exists new file mode 100644 index 00000000000..45237dd06a0 --- /dev/null +++ b/resources/function_help/json/file_exists @@ -0,0 +1,8 @@ +{ + "name": "file_exists", + "type": "function", + "description": "Returns true if a file path exists.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"file_exists('/home/qgis/data/country_boundaries.shp')", "returns":"true"}] +} + diff --git a/resources/function_help/json/file_name b/resources/function_help/json/file_name new file mode 100644 index 00000000000..d20cec1ed06 --- /dev/null +++ b/resources/function_help/json/file_name @@ -0,0 +1,8 @@ +{ + "name": "file_name", + "type": "function", + "description": "Returns the name of a file (including the file extension), excluding the directory.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"base_file_name('/home/qgis/data/country_boundaries.shp')", "returns":"'country_boundaries.shp'"}] +} + diff --git a/resources/function_help/json/file_path b/resources/function_help/json/file_path new file mode 100644 index 00000000000..7cc74cf8890 --- /dev/null +++ b/resources/function_help/json/file_path @@ -0,0 +1,8 @@ +{ + "name": "file_path", + "type": "function", + "description": "Returns the directory component of a file path. This does not include the file name.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"base_file_name('/home/qgis/data/country_boundaries.shp')", "returns":"'/home/qgis/data'"}] +} + diff --git a/resources/function_help/json/file_size b/resources/function_help/json/file_size new file mode 100644 index 00000000000..3498841f4c5 --- /dev/null +++ b/resources/function_help/json/file_size @@ -0,0 +1,8 @@ +{ + "name": "file_size", + "type": "function", + "description": "Returns the size (in bytes) of a file.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"file_size('/home/qgis/data/country_boundaries.geojson')", "returns":"5674"}] +} + diff --git a/resources/function_help/json/file_suffix b/resources/function_help/json/file_suffix new file mode 100644 index 00000000000..84038957815 --- /dev/null +++ b/resources/function_help/json/file_suffix @@ -0,0 +1,8 @@ +{ + "name": "file_suffix", + "type": "function", + "description": "Returns the file suffix (extension) from a file path.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"base_file_name('/home/qgis/data/country_boundaries.shp')", "returns":"'shp'"}] +} + diff --git a/resources/function_help/json/is_directory b/resources/function_help/json/is_directory new file mode 100644 index 00000000000..69d46e6d55a --- /dev/null +++ b/resources/function_help/json/is_directory @@ -0,0 +1,9 @@ +{ + "name": "is_directory", + "type": "function", + "description": "Returns true if a path corresponds to a directory.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"is_directory('/home/qgis/data/country_boundaries.shp')", "returns":"false"}, +{ "expression":"is_directory('/home/qgis/data/')", "returns":"true"}] +} + diff --git a/resources/function_help/json/is_file b/resources/function_help/json/is_file new file mode 100644 index 00000000000..f20c2fe9737 --- /dev/null +++ b/resources/function_help/json/is_file @@ -0,0 +1,9 @@ +{ + "name": "is_file", + "type": "function", + "description": "Returns true if a path corresponds to a file.", + "arguments": [ {"arg":"path","description":"a file path"}], + "examples": [ { "expression":"is_file('/home/qgis/data/country_boundaries.shp')", "returns":"true"}, +{ "expression":"is_file('/home/qgis/data/')", "returns":"false"}] +} + diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index ec00029f4fb..3cba764bb7f 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -4782,6 +4782,57 @@ static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContex return QProcessEnvironment::systemEnvironment().value( envVarName ); } +static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).completeBaseName(); +} + +static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).completeSuffix(); +} + +static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo::exists( file ); +} + +static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).fileName(); +} + +static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).isFile(); +} + +static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).isDir(); +} + +static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QDir::toNativeSeparators( QFileInfo( file ).path() ); +} + +static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent ); + return QFileInfo( file ).size(); +} + + + + const QList &QgsExpression::Functions() { // The construction of the list isn't thread-safe, and without the mutex, @@ -5045,6 +5096,24 @@ const QList &QgsExpression::Functions() fncLighter, QStringLiteral( "Color" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) ) + // file info + << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnBaseFileName, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnFileSuffix, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnFileExists, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnFileName, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnPathIsFile, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnPathIsDir, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnFilePath, QStringLiteral( "Files and Paths" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ), + fcnFileSize, QStringLiteral( "Files and Paths" ) ) + // deprecated stuff - hidden from users << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) ); diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index e5cbeb4f4db..5618fceeef7 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -1370,9 +1370,44 @@ class TestQgsExpression: public QObject QTest::newRow( "array_last(array('a', 'b', 'c'))" ) << QStringLiteral( "array_last(array('a', 'b', 'c'))" ) << false << QVariant( "c" ); QTest::newRow( "array_last(array())" ) << QStringLiteral( "array_last(array())" ) << false << QVariant(); - // + // file functions + QTest::newRow( "base_file_name(5)" ) << QStringLiteral( "base_file_name(5)" ) << false << QVariant( "5" ); + QTest::newRow( "base_file_name(NULL)" ) << QStringLiteral( "base_file_name(NULL)" ) << false << QVariant(); + QTest::newRow( "base_file_name('/home/qgis/test.qgs')" ) << QStringLiteral( "base_file_name('/home/qgis/test.qgs')" ) << false << QVariant( "test" ); + QTest::newRow( "base_file_name(points.shp)" ) << QStringLiteral( "base_file_name('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "points" ); + QTest::newRow( "file_exists(NULL)" ) << QStringLiteral( "file_exists(NULL)" ) << false << QVariant(); + QTest::newRow( "file_exists('/home/qgis/test.qgs')" ) << QStringLiteral( "file_exists('/home/qgis/test.qgs')" ) << false << QVariant( false ); + QTest::newRow( "file_exists(points.shp)" ) << QStringLiteral( "file_exists('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( true ); + QTest::newRow( "file_name(5)" ) << QStringLiteral( "file_name(5)" ) << false << QVariant( "5" ); + QTest::newRow( "file_name(NULL)" ) << QStringLiteral( "file_name(NULL)" ) << false << QVariant(); + QTest::newRow( "file_name('/home/qgis/test.qgs')" ) << QStringLiteral( "file_name('/home/qgis/test.qgs')" ) << false << QVariant( "test.qgs" ); + QTest::newRow( "file_name(points.shp)" ) << QStringLiteral( "file_name('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "points.shp" ); + QTest::newRow( "file_path(5)" ) << QStringLiteral( "file_path(5)" ) << false << QVariant( "." ); + QTest::newRow( "file_path(NULL)" ) << QStringLiteral( "file_path(NULL)" ) << false << QVariant(); + QTest::newRow( "file_path('/home/qgis/test.qgs')" ) << QStringLiteral( "file_path('/home/qgis/test.qgs')" ) << false << QVariant( "/home/qgis" ); + QTest::newRow( "file_path(points.shp)" ) << QStringLiteral( "file_path('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( TEST_DATA_DIR ); + QTest::newRow( "file_size(5)" ) << QStringLiteral( "file_size(5)" ) << false << QVariant( 0LL ); + QTest::newRow( "file_size(NULL)" ) << QStringLiteral( "file_size(NULL)" ) << false << QVariant(); + QTest::newRow( "file_size('/home/qgis/test.qgs')" ) << QStringLiteral( "file_size('/home/qgis/test.qgs')" ) << false << QVariant( 0LL ); + QTest::newRow( "file_size(points.shp)" ) << QStringLiteral( "file_size('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( 576LL ); + QTest::newRow( "file_suffix(5)" ) << QStringLiteral( "file_suffix(5)" ) << false << QVariant( "" ); + QTest::newRow( "file_suffix(NULL)" ) << QStringLiteral( "file_suffix(NULL)" ) << false << QVariant(); + QTest::newRow( "file_suffix('/home/qgis/test.qgs')" ) << QStringLiteral( "file_suffix('/home/qgis/test.qgs')" ) << false << QVariant( "qgs" ); + QTest::newRow( "file_suffix(points.shp)" ) << QStringLiteral( "file_suffix('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "shp" ); + QTest::newRow( "is_directory(5)" ) << QStringLiteral( "is_directory(5)" ) << false << QVariant( false ); + QTest::newRow( "is_directory(NULL)" ) << QStringLiteral( "is_directory(NULL)" ) << false << QVariant(); + QTest::newRow( "is_directory('/home/qgis/test.qgs')" ) << QStringLiteral( "is_directory('/home/qgis/test.qgs')" ) << false << QVariant( false ); + QTest::newRow( "is_directory(points.shp)" ) << QStringLiteral( "is_directory('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( false ); + QTest::newRow( "is_directory(valid)" ) << QStringLiteral( "is_directory('%1')" ).arg( TEST_DATA_DIR ) << false << QVariant( true ); + QTest::newRow( "is_file(5)" ) << QStringLiteral( "is_file(5)" ) << false << QVariant( false ); + QTest::newRow( "is_file(NULL)" ) << QStringLiteral( "is_file(NULL)" ) << false << QVariant(); + QTest::newRow( "is_file('/home/qgis/test.qgs')" ) << QStringLiteral( "is_file('/home/qgis/test.qgs')" ) << false << QVariant( false ); + QTest::newRow( "is_file(points.shp)" ) << QStringLiteral( "is_file('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( true ); + QTest::newRow( "is_file(valid)" ) << QStringLiteral( "is_file('%1')" ).arg( TEST_DATA_DIR ) << false << QVariant( false ); + } + void run_evaluation_test( QgsExpression &exp, bool evalError, QVariant &expected ) { if ( exp.hasParserError() )