diff --git a/python/core/qgsfileutils.sip b/python/core/qgsfileutils.sip index c5016dfea5f..54ae1eb8291 100644 --- a/python/core/qgsfileutils.sip +++ b/python/core/qgsfileutils.sip @@ -27,6 +27,45 @@ class QgsFileUtils Return the human size from bytes %End + + static QStringList extensionsFromFilter( const QString &filter ); +%Docstring +Returns a list of the extensions contained within a file ``filter`` string. +E.g. a ``filter`` of "GeoTIFF Files (*.tiff *.tif)" would return a list +containing "tiff", "tif". The initial '.' is stripped off the extension. + +.. seealso:: :py:func:`ensureFileNameHasExtension()` + +.. seealso:: :py:func:`addExtensionFromFilter()` +%End + + static QString ensureFileNameHasExtension( const QString &fileName, const QStringList &extensions ); +%Docstring +Ensures that a ``fileName`` ends with an extension from the provided list of +``extensions``. + +E.g. if extensions contains "tif" and "tiff", then a ``fileName`` of +"d:/my_file" will return "d:/my_file.tif". A ``fileName`` of +"d:/my_file.TIFF" or "d:/my_file.TIF" will be returned unchanged. + +.. seealso:: :py:func:`extensionsFromFilter()` + +.. seealso:: :py:func:`addExtensionFromFilter()` +%End + + static QString addExtensionFromFilter( const QString &fileName, const QString &filter ); +%Docstring +Ensures that a ``fileName`` ends with an extension from the specified ``filter`` +string. + +E.g. a ``fileName`` of "d:/my_file" with a filter of "GeoTIFF Files (*.tiff *.tif)" +will return "d:/my_file.tif", where as ``fileName`` of "d:/my_file.TIFF" or "d:/my_file.TIF" +will be returned unchanged. + +.. seealso:: :py:func:`extensionsFromFilter()` + +.. seealso:: :py:func:`ensureFileNameHasExtension()` +%End }; /************************************************************************ diff --git a/src/core/qgsfileutils.cpp b/src/core/qgsfileutils.cpp index 9b28c286b35..d1dd350d40f 100644 --- a/src/core/qgsfileutils.cpp +++ b/src/core/qgsfileutils.cpp @@ -1,5 +1,6 @@ #include "qgsfileutils.h" #include +#include QString QgsFileUtils::representFileSize( qint64 bytes ) { @@ -16,3 +17,55 @@ QString QgsFileUtils::representFileSize( qint64 bytes ) } return QString( "%1 %2" ).arg( QString::number( bytes ), unit ); } + +QStringList QgsFileUtils::extensionsFromFilter( const QString &filter ) +{ + const QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]+)" ) ); + QStringList extensions; + QRegularExpressionMatchIterator matches = rx.globalMatch( filter ); + + while ( matches.hasNext() ) + { + const QRegularExpressionMatch match = matches.next(); + if ( match.hasMatch() ) + { + QStringList newExtensions = match.capturedTexts(); + newExtensions.pop_front(); // remove whole match + extensions.append( newExtensions ); + } + } + return extensions; +} + +QString QgsFileUtils::ensureFileNameHasExtension( const QString &f, const QStringList &extensions ) +{ + if ( extensions.empty() || f.isEmpty() ) + return f; + + QString fileName = f; + bool hasExt = false; + for ( const QString &extension : qgis::as_const( extensions ) ) + { + const QString extWithDot = extension.startsWith( '.' ) ? extension : '.' + extension; + if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) ) + { + hasExt = true; + break; + } + } + + if ( !hasExt ) + { + const QString extension = extensions.at( 0 ); + const QString extWithDot = extension.startsWith( '.' ) ? extension : '.' + extension; + fileName += extWithDot; + } + + return fileName; +} + +QString QgsFileUtils::addExtensionFromFilter( const QString &fileName, const QString &filter ) +{ + const QStringList extensions = extensionsFromFilter( filter ); + return ensureFileNameHasExtension( fileName, extensions ); +} diff --git a/src/core/qgsfileutils.h b/src/core/qgsfileutils.h index dc0d982867b..1a3455cad4a 100644 --- a/src/core/qgsfileutils.h +++ b/src/core/qgsfileutils.h @@ -35,6 +35,41 @@ class CORE_EXPORT QgsFileUtils */ static QString representFileSize( qint64 bytes ); + + /** + * Returns a list of the extensions contained within a file \a filter string. + * E.g. a \a filter of "GeoTIFF Files (*.tiff *.tif)" would return a list + * containing "tiff", "tif". The initial '.' is stripped off the extension. + * \see ensureFileNameHasExtension() + * \see addExtensionFromFilter() + */ + static QStringList extensionsFromFilter( const QString &filter ); + + /** + * Ensures that a \a fileName ends with an extension from the provided list of + * \a extensions. + * + * E.g. if extensions contains "tif" and "tiff", then a \a fileName of + * "d:/my_file" will return "d:/my_file.tif". A \a fileName of + * "d:/my_file.TIFF" or "d:/my_file.TIF" will be returned unchanged. + * + * \see extensionsFromFilter() + * \see addExtensionFromFilter() + */ + static QString ensureFileNameHasExtension( const QString &fileName, const QStringList &extensions ); + + /** + * Ensures that a \a fileName ends with an extension from the specified \a filter + * string. + * + * E.g. a \a fileName of "d:/my_file" with a filter of "GeoTIFF Files (*.tiff *.tif)" + * will return "d:/my_file.tif", where as \a fileName of "d:/my_file.TIFF" or "d:/my_file.TIF" + * will be returned unchanged. + * + * \see extensionsFromFilter() + * \see ensureFileNameHasExtension() + */ + static QString addExtensionFromFilter( const QString &fileName, const QString &filter ); }; #endif // QGSFILEUTILS_H diff --git a/tests/src/python/test_qgsfileutils.py b/tests/src/python/test_qgsfileutils.py new file mode 100644 index 00000000000..b2d7fdb8499 --- /dev/null +++ b/tests/src/python/test_qgsfileutils.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsFileUtils. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '18/12/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import QgsFileUtils +from qgis.testing import unittest + + +class TestQgsFileUtils(unittest.TestCase): + + def testExtensionsFromFilter(self): + self.assertEqual(QgsFileUtils.extensionsFromFilter(''), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('bad'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('*'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('*.'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('Tiff files'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('(*.)'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('PNG Files (*.png)'), ['png']) + self.assertEqual(QgsFileUtils.extensionsFromFilter('PNG Files (*.PNG)'), ['PNG']) + self.assertEqual(QgsFileUtils.extensionsFromFilter('Geotiff Files (*.tiff *.tif)'), ['tiff', 'tif']) + + def testEnsureFileNameHasExtension(self): + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', ['']), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', []), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', []), 'test') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', ['.tif']), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', ['.tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', ['tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', []), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['bmp']), 'test.tif.bmp') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['tiff']), 'test.tif.tiff') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['tiff', 'tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['TIFF', 'TIF']), 'test.tif') + + def testAddExtensionFromFilter(self): + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'TIFF Files (*.tif)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'TIFF Files (*.tif)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', ''), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'BMP Files (*.bmp)'), 'test.tif.bmp') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.tiff)'), 'test.tif.tiff') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.tif *.tiff)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.TIF *.TIFF)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'All Files (*.*)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'All Files (*.*)'), 'test') + + +if __name__ == '__main__': + unittest.main()