diff --git a/python/core/auto_generated/qgsstringutils.sip.in b/python/core/auto_generated/qgsstringutils.sip.in index e15e92568d7..c405da4fe7a 100644 --- a/python/core/auto_generated/qgsstringutils.sip.in +++ b/python/core/auto_generated/qgsstringutils.sip.in @@ -315,6 +315,17 @@ Convert simple HTML to markdown. Only br, b and link are supported. :return: String formatted as markdown .. versionadded:: 3.10 +%End + + static QString qRegExpEscape( const QString &string ); +%Docstring +Returns an escaped string matching the behavior of QRegExp.escape. + +:param string: String to escape + +:return: Escaped string + +.. versionadded:: 3.22 %End }; diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index c4df56c83dc..a5e82f49cab 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -499,14 +499,13 @@ QSet QgsExpression::referencedVariables( const QString &text ) int index = 0; while ( index < text.size() ) { - QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" ); - - int pos = rx.indexIn( text, index ); - if ( pos < 0 ) + const thread_local QRegularExpression rx( "\\[%([^\\]]+)%\\]" ); + const QRegularExpressionMatch match = rx.match( text ); + if ( !match.hasMatch() ) break; - index = pos + rx.matchedLength(); - QString to_replace = rx.cap( 1 ).trimmed(); + index = match.capturedStart() + match.capturedLength(); + QString to_replace = match.captured( 1 ).trimmed(); QgsExpression exp( to_replace ); variables.unite( exp.referencedVariables() ); diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index 7848960e2bc..56ff2887f92 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -19,6 +19,7 @@ #include "qgsgeometry.h" #include "qgsfeaturerequest.h" +#include "qgsstringutils.h" #include @@ -531,39 +532,39 @@ QVariant QgsExpressionNodeBinaryOperator::evalNode( QgsExpression *parent, const ENSURE_NO_EVAL_ERROR QString regexp = QgsExpressionUtils::getStringValue( vR, parent ); ENSURE_NO_EVAL_ERROR - // TODO: cache QRegExp in case that regexp is a literal string (i.e. it will stay constant) + // TODO: cache QRegularExpression in case that regexp is a literal string (i.e. it will stay constant) bool matches; if ( mOp == boLike || mOp == boILike || mOp == boNotLike || mOp == boNotILike ) // change from LIKE syntax to regexp { - QString esc_regexp = QRegExp::escape( regexp ); + QString esc_regexp = QgsStringUtils::qRegExpEscape( regexp ); // manage escape % and _ if ( esc_regexp.startsWith( '%' ) ) { esc_regexp.replace( 0, 1, QStringLiteral( ".*" ) ); } - thread_local QRegExp rx1( QStringLiteral( "[^\\\\](%)" ) ); + const thread_local QRegularExpression rx1( QStringLiteral( "[^\\\\](%)" ) ); int pos = 0; - while ( ( pos = rx1.indexIn( esc_regexp, pos ) ) != -1 ) + while ( ( pos = esc_regexp.indexOf( rx1, pos ) ) != -1 ) { esc_regexp.replace( pos + 1, 1, QStringLiteral( ".*" ) ); pos += 1; } - thread_local QRegExp rx2( QStringLiteral( "\\\\%" ) ); + const thread_local QRegularExpression rx2( QStringLiteral( "\\\\%" ) ); esc_regexp.replace( rx2, QStringLiteral( "%" ) ); if ( esc_regexp.startsWith( '_' ) ) { esc_regexp.replace( 0, 1, QStringLiteral( "." ) ); } - thread_local QRegExp rx3( QStringLiteral( "[^\\\\](_)" ) ); + const thread_local QRegularExpression rx3( QStringLiteral( "[^\\\\](_)" ) ); pos = 0; - while ( ( pos = rx3.indexIn( esc_regexp, pos ) ) != -1 ) + while ( ( pos = esc_regexp.indexOf( rx3, pos ) ) != -1 ) { esc_regexp.replace( pos + 1, 1, '.' ); pos += 1; } esc_regexp.replace( QLatin1String( "\\\\_" ), QLatin1String( "_" ) ); - matches = QRegExp( esc_regexp, mOp == boLike || mOp == boNotLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( str ); + matches = QRegularExpression( QRegularExpression::anchoredPattern( esc_regexp ), mOp == boLike || mOp == boNotLike ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption ).match( str ).hasMatch(); } else { @@ -1479,8 +1480,9 @@ bool QgsExpressionNodeColumnRef::prepareNode( QgsExpression *parent, const QgsEx QString QgsExpressionNodeColumnRef::dump() const { - const thread_local QRegExp re( QStringLiteral( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ) ); - return re.exactMatch( mName ) ? mName : QgsExpression::quotedColumnRef( mName ); + const thread_local QRegularExpression re( QStringLiteral( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ) ); + const QRegularExpressionMatch match = re.match( mName ); + return match.hasMatch() ? mName : QgsExpression::quotedColumnRef( mName ); } QSet QgsExpressionNodeColumnRef::referencedColumns() const diff --git a/src/core/providers/gdal/qgsgdaldataitems.cpp b/src/core/providers/gdal/qgsgdaldataitems.cpp index 1f3ced5629a..9c08084c8aa 100644 --- a/src/core/providers/gdal/qgsgdaldataitems.cpp +++ b/src/core/providers/gdal/qgsgdaldataitems.cpp @@ -30,10 +30,12 @@ #include "qgsprovidersublayerdetails.h" #include -#include +#include #include #include +#include + // defined in qgsgdalprovider.cpp void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QStringList &extensions, QStringList &wildcards ); @@ -225,8 +227,8 @@ QgsDataItem *QgsGdalDataItemProvider::createDataItem( const QString &pathIn, Qgs const auto constSWildcards = sWildcards; for ( const QString &wildcard : constSWildcards ) { - QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard ); - if ( rx.exactMatch( info.fileName() ) ) + const QRegularExpression rx( QRegularExpression::wildcardToRegularExpression( wildcard ), QRegularExpression::CaseInsensitiveOption ); + if ( rx.match( info.fileName() ).hasMatch() ) { matches = true; break; diff --git a/src/core/providers/gdal/qgsgdalprovider.cpp b/src/core/providers/gdal/qgsgdalprovider.cpp index c8f7cad37c3..fe69888fea9 100644 --- a/src/core/providers/gdal/qgsgdalprovider.cpp +++ b/src/core/providers/gdal/qgsgdalprovider.cpp @@ -2455,7 +2455,7 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS QString myGdalDriverLongName = GDALGetMetadataItem( myGdalDriver, GDAL_DMD_LONGNAME, "" ); // remove any superfluous (.*) strings at the end as // they'll confuse QFileDialog::getOpenFileNames() - myGdalDriverLongName.remove( QRegExp( "\\(.*\\)$" ) ); + myGdalDriverLongName.remove( QRegularExpression( "\\(.*\\)$" ) ); // if we have both the file name extension and the long name, // then we've all the information we need for the current diff --git a/src/core/providers/ogr/qgsogrdataitems.cpp b/src/core/providers/ogr/qgsogrdataitems.cpp index 79c72227ebf..ac17bf3dce3 100644 --- a/src/core/providers/ogr/qgsogrdataitems.cpp +++ b/src/core/providers/ogr/qgsogrdataitems.cpp @@ -630,8 +630,8 @@ QgsDataItem *QgsOgrDataItemProvider::createDataItem( const QString &pathIn, QgsD const auto constWildcards = QgsOgrProviderUtils::wildcards(); for ( const QString &wildcard : constWildcards ) { - QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard ); - if ( rx.exactMatch( info.fileName() ) ) + const QRegularExpression rx( QRegularExpression::wildcardToRegularExpression( wildcard ), QRegularExpression::CaseInsensitiveOption ); + if ( rx.match( info.fileName() ).hasMatch() ) { matches = true; break; diff --git a/src/core/providers/qgsproviderregistry.cpp b/src/core/providers/qgsproviderregistry.cpp index bfcb672a429..8d0552167bc 100644 --- a/src/core/providers/qgsproviderregistry.cpp +++ b/src/core/providers/qgsproviderregistry.cpp @@ -18,10 +18,6 @@ #include "qgsproviderregistry.h" -#include -#include -#include - #include "qgis.h" #include "qgsdataprovider.h" #include "qgsdataitemprovider.h" @@ -51,6 +47,11 @@ #include "qgspostgresprovider.h" #endif +#include +#include +#include +#include + static QgsProviderRegistry *sInstance = nullptr; QgsProviderRegistry *QgsProviderRegistry::instance( const QString &pluginPath ) @@ -218,7 +219,7 @@ void QgsProviderRegistry::init() // provider file regex pattern, only files matching the pattern are loaded if the variable is defined QString filePattern = getenv( "QGIS_PROVIDER_FILE" ); - QRegExp fileRegexp; + QRegularExpression fileRegexp; if ( !filePattern.isEmpty() ) { fileRegexp.setPattern( filePattern ); @@ -229,9 +230,9 @@ void QgsProviderRegistry::init() const auto constEntryInfoList = mLibraryDirectory.entryInfoList(); for ( const QFileInfo &fi : constEntryInfoList ) { - if ( !fileRegexp.isEmpty() ) + if ( !filePattern.isEmpty() ) { - if ( fileRegexp.indexIn( fi.fileName() ) == -1 ) + if ( fi.fileName().indexOf( fileRegexp ) == -1 ) { QgsDebugMsg( "provider " + fi.fileName() + " skipped because doesn't match pattern " + filePattern ); continue; diff --git a/src/core/qgsstringutils.cpp b/src/core/qgsstringutils.cpp index 53847abf7d8..d83e2106c23 100644 --- a/src/core/qgsstringutils.cpp +++ b/src/core/qgsstringutils.cpp @@ -688,6 +688,39 @@ QString QgsStringUtils::substituteVerticalCharacters( QString string ) return string; } +QString QgsStringUtils::qRegExpEscape( const QString &string ) +{ + // code and logic taken from the Qt source code + const QLatin1Char backslash( '\\' ); + const int count = string.count(); + + QString escaped; + escaped.reserve( count * 2 ); + for ( int i = 0; i < count; i++ ) + { + switch ( string.at( i ).toLatin1() ) + { + case '$': + case '(': + case ')': + case '*': + case '+': + case '.': + case '?': + case '[': + case '\\': + case ']': + case '^': + case '{': + case '|': + case '}': + escaped.append( backslash ); + } + escaped.append( string.at( i ) ); + } + return escaped; +} + QgsStringReplacement::QgsStringReplacement( const QString &match, const QString &replacement, bool caseSensitive, bool wholeWordOnly ) : mMatch( match ) , mReplacement( replacement ) diff --git a/src/core/qgsstringutils.h b/src/core/qgsstringutils.h index f569d7d742b..505dcf62c94 100644 --- a/src/core/qgsstringutils.h +++ b/src/core/qgsstringutils.h @@ -307,6 +307,14 @@ class CORE_EXPORT QgsStringUtils */ static QString htmlToMarkdown( const QString &html ); + /** + * Returns an escaped string matching the behavior of QRegExp::escape. + * \param string String to escape + * \returns Escaped string + * \since QGIS 3.22 + */ + static QString qRegExpEscape( const QString &string ); + }; #endif //QGSSTRINGUTILS_H diff --git a/src/core/symbology/qgscptcityarchive.cpp b/src/core/symbology/qgscptcityarchive.cpp index 9a275cfa8e2..e0116c8c6dd 100644 --- a/src/core/symbology/qgscptcityarchive.cpp +++ b/src/core/symbology/qgscptcityarchive.cpp @@ -15,15 +15,6 @@ * * ***************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - #include "qgssettings.h" #include "qgscptcityarchive.h" #include "qgis.h" @@ -34,6 +25,16 @@ #include "qgsapplication.h" #include "qgssymbollayerutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + typedef QMap< QString, QgsCptCityArchive * > ArchiveRegistry; typedef QMap< QString, QMap< QString, QString > > CopyingInfoMap; @@ -974,12 +975,12 @@ QMap< QString, QStringList > QgsCptCityDirectoryItem::rampsMap() } else { - QRegExp rxVariant( "^(.*[^\\d])(\\d{1,3})$" ); - int pos = rxVariant.indexIn( schemeName ); - if ( pos > -1 ) + const thread_local QRegularExpression rxVariant( "^(.*[^\\d])(\\d{1,3})$" ); + const QRegularExpressionMatch match = rxVariant.match( schemeName ); + if ( match.hasMatch() ) { - curName = rxVariant.cap( 1 ); - curVariant = rxVariant.cap( 2 ); + curName = match.captured( 1 ); + curVariant = match.captured( 2 ); } } diff --git a/src/core/symbology/qgsrendererrange.cpp b/src/core/symbology/qgsrendererrange.cpp index f3b44f4eaff..283991fb59e 100644 --- a/src/core/symbology/qgsrendererrange.cpp +++ b/src/core/symbology/qgsrendererrange.cpp @@ -16,6 +16,7 @@ #include "qgsrendererrange.h" #include "qgsclassificationmethod.h" #include "qgssymbol.h" + #include @@ -220,7 +221,7 @@ QString QgsRendererRangeLabelFormat::formatNumber( double value ) const QString valueStr = QLocale().toString( value, 'f', mPrecision ); if ( mTrimTrailingZeroes ) valueStr = valueStr.remove( mReTrailingZeroes ); - if ( mReNegativeZero.exactMatch( valueStr ) ) + if ( mReNegativeZero.match( valueStr ).hasMatch() ) valueStr = valueStr.mid( 1 ); return valueStr; } diff --git a/src/core/symbology/qgsrendererrange.h b/src/core/symbology/qgsrendererrange.h index f2a888b9f60..a4b7d2925c3 100644 --- a/src/core/symbology/qgsrendererrange.h +++ b/src/core/symbology/qgsrendererrange.h @@ -16,12 +16,12 @@ #ifndef QGSRENDERERRANGE_H #define QGSRENDERERRANGE_H -#include - #include "qgis_core.h" #include "qgis_sip.h" #include "qgssymbollayerutils.h" +#include + class QDomDocument; class QDomElement; @@ -141,8 +141,8 @@ class CORE_EXPORT Q_DECL_DEPRECATED QgsRendererRangeLabelFormat SIP_DEPRECATED // values used to manage number formatting - precision and trailing zeroes double mNumberScale = 1.0; QString mNumberSuffix; - QRegExp mReTrailingZeroes; - QRegExp mReNegativeZero; + QRegularExpression mReTrailingZeroes; + QRegularExpression mReNegativeZero; }; diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 84bda614fad..115fbaf5b6c 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -3258,7 +3257,8 @@ QList QgsSymbolLayerUtils::parseColorList( const QString &colorStr ) QList colors; //try splitting string at commas, spaces or newlines - QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) ); + const thread_local QRegularExpression sepCommaSpaceRegExp( "(,|\\s)" ); + QStringList components = colorStr.simplified().split( sepCommaSpaceRegExp ); QStringList::iterator it = components.begin(); for ( ; it != components.end(); ++it ) { @@ -3274,7 +3274,8 @@ QList QgsSymbolLayerUtils::parseColorList( const QString &colorStr ) } //try splitting string at commas or newlines - components = colorStr.split( QRegExp( "(,|\n)" ) ); + const thread_local QRegularExpression sepCommaRegExp( "(,|\n)" ); + components = colorStr.split( sepCommaRegExp ); it = components.begin(); for ( ; it != components.end(); ++it ) { @@ -3594,10 +3595,11 @@ QgsNamedColorList QgsSymbolLayerUtils::importColorsFromGpl( QFile &file, bool &o } if ( line.startsWith( QLatin1String( "Name:" ) ) ) { - QRegExp nameRx( "Name:\\s*(\\S.*)$" ); - if ( nameRx.indexIn( line ) != -1 ) + const thread_local QRegularExpression nameRx( "Name:\\s*(\\S.*)$" ); + const QRegularExpressionMatch match = nameRx.match( line ); + if ( match.hasMatch() ) { - name = nameRx.cap( 1 ); + name = match.captured( 1 ); } } @@ -3613,17 +3615,18 @@ QgsNamedColorList QgsSymbolLayerUtils::importColorsFromGpl( QFile &file, bool &o } //ready to start reading colors - QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" ); + const thread_local QRegularExpression rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" ); while ( !in.atEnd() ) { line = in.readLine(); - if ( rx.indexIn( line ) == -1 ) + const QRegularExpressionMatch match = rx.match( line ); + if ( !match.hasMatch() ) { continue; } - int red = rx.cap( 1 ).toInt(); - int green = rx.cap( 2 ).toInt(); - int blue = rx.cap( 3 ).toInt(); + int red = match.captured( 1 ).toInt(); + int green = match.captured( 2 ).toInt(); + int blue = match.captured( 3 ).toInt(); QColor color = QColor( red, green, blue ); if ( !color.isValid() ) { @@ -3634,7 +3637,7 @@ QgsNamedColorList QgsSymbolLayerUtils::importColorsFromGpl( QFile &file, bool &o QString label; if ( rx.captureCount() > 3 ) { - label = rx.cap( 4 ).simplified(); + label = match.captured( 4 ).simplified(); } else { @@ -3659,11 +3662,11 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & { QColor parsedColor; - QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" ); - int hexColorIndex = hexColorAlphaRx.indexIn( colorStr ); + const thread_local QRegularExpression hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" ); + QRegularExpressionMatch match = hexColorAlphaRx.match( colorStr ); //color in hex format "#aabbcc", but not #aabbccdd - if ( hexColorIndex == -1 && QColor::isValidColor( colorStr ) ) + if ( !match.hasMatch() && QColor::isValidColor( colorStr ) ) { //string is a valid hex color string parsedColor.setNamedColor( colorStr ); @@ -3675,12 +3678,12 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in hex format, with alpha - if ( hexColorIndex > -1 ) + if ( match.hasMatch() ) { - QString hexColor = hexColorAlphaRx.cap( 1 ); + QString hexColor = match.captured( 1 ); parsedColor.setNamedColor( QStringLiteral( "#" ) + hexColor ); bool alphaOk; - int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 ); + int alphaHex = match.captured( 2 ).toInt( &alphaOk, 16 ); if ( parsedColor.isValid() && alphaOk ) { @@ -3693,8 +3696,8 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & if ( !strictEval ) { //color in hex format, without # - QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" ); - if ( hexColorRx2.indexIn( colorStr ) != -1 ) + const thread_local QRegularExpression hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" ); + if ( colorStr.indexOf( hexColorRx2 ) != -1 ) { //add "#" and parse parsedColor.setNamedColor( QStringLiteral( "#" ) + colorStr ); @@ -3707,12 +3710,13 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional - QRegExp rgbFormatRx( "^\\s*(?:rgb)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*\\)?\\s*;?\\s*$" ); - if ( rgbFormatRx.indexIn( colorStr ) != -1 ) + const thread_local QRegularExpression rgbFormatRx( "^\\s*(?:rgb)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*\\)?\\s*;?\\s*$" ); + match = rgbFormatRx.match( colorStr ); + if ( match.hasMatch() ) { - int r = rgbFormatRx.cap( 1 ).toInt(); - int g = rgbFormatRx.cap( 2 ).toInt(); - int b = rgbFormatRx.cap( 3 ).toInt(); + int r = match.captured( 1 ).toInt(); + int g = match.captured( 2 ).toInt(); + int b = match.captured( 3 ).toInt(); parsedColor.setRgb( r, g, b ); if ( parsedColor.isValid() ) { @@ -3722,8 +3726,8 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in hsl(h,s,l) format, brackets optional - const QRegularExpression hslFormatRx( "^\\s*hsl\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*\\)?\\s*;?\\s*$" ); - QRegularExpressionMatch match = hslFormatRx.match( colorStr ); + const thread_local QRegularExpression hslFormatRx( "^\\s*hsl\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*\\)?\\s*;?\\s*$" ); + match = hslFormatRx.match( colorStr ); if ( match.hasMatch() ) { int h = match.captured( 1 ).toInt(); @@ -3738,12 +3742,13 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in (r%,g%,b%) format, brackets and rgb prefix optional - QRegExp rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" ); - if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 ) + const thread_local QRegularExpression rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" ); + match = rgbPercentFormatRx.match( colorStr ); + if ( match.hasMatch() ) { - int r = std::round( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); - int g = std::round( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); - int b = std::round( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); + int r = std::round( match.captured( 1 ).toDouble() * 2.55 ); + int g = std::round( match.captured( 2 ).toDouble() * 2.55 ); + int b = std::round( match.captured( 3 ).toDouble() * 2.55 ); parsedColor.setRgb( r, g, b ); if ( parsedColor.isValid() ) { @@ -3753,13 +3758,14 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in (r,g,b,a) format, brackets and rgba prefix optional - QRegExp rgbaFormatRx( "^\\s*(?:rgba)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" ); - if ( rgbaFormatRx.indexIn( colorStr ) != -1 ) + const thread_local QRegularExpression rgbaFormatRx( "^\\s*(?:rgba)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" ); + match = rgbaFormatRx.match( colorStr ); + if ( match.hasMatch() ) { - int r = rgbaFormatRx.cap( 1 ).toInt(); - int g = rgbaFormatRx.cap( 2 ).toInt(); - int b = rgbaFormatRx.cap( 3 ).toInt(); - int a = std::round( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 ); + int r = match.captured( 1 ).toInt(); + int g = match.captured( 2 ).toInt(); + int b = match.captured( 3 ).toInt(); + int a = std::round( match.captured( 4 ).toDouble() * 255.0 ); parsedColor.setRgb( r, g, b, a ); if ( parsedColor.isValid() ) { @@ -3769,13 +3775,14 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in (r%,g%,b%,a) format, brackets and rgba prefix optional - QRegExp rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" ); - if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 ) + const thread_local QRegularExpression rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" ); + match = rgbaPercentFormatRx.match( colorStr ); + if ( match.hasMatch() ) { - int r = std::round( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); - int g = std::round( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); - int b = std::round( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); - int a = std::round( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 ); + int r = std::round( match.captured( 1 ).toDouble() * 2.55 ); + int g = std::round( match.captured( 2 ).toDouble() * 2.55 ); + int b = std::round( match.captured( 3 ).toDouble() * 2.55 ); + int a = std::round( match.captured( 4 ).toDouble() * 255.0 ); parsedColor.setRgb( r, g, b, a ); if ( parsedColor.isValid() ) { @@ -3785,7 +3792,7 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & } //color in hsla(h,s%,l%,a) format, brackets optional - const QRegularExpression hslaPercentFormatRx( "^\\s*hsla\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*,\\s*([\\d\\.]+)\\s*\\)?\\s*;?\\s*$" ); + const thread_local QRegularExpression hslaPercentFormatRx( "^\\s*hsla\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*,\\s*([\\d\\.]+)\\s*\\)?\\s*;?\\s*$" ); match = hslaPercentFormatRx.match( colorStr ); if ( match.hasMatch() ) { diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index cf4684d4910..1a00daeed16 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -765,7 +765,10 @@ class TestQgsExpression: public QObject QTest::newRow( "like 2" ) << "'hello' like '_el%'" << false << QVariant( 1 ); QTest::newRow( "like 3" ) << "'hello' like 'lo'" << false << QVariant( 0 ); QTest::newRow( "like 4" ) << "'hello' like '%LO'" << false << QVariant( 0 ); + QTest::newRow( "like 5" ) << "'QGIS' like '%G%'" << false << QVariant( 1 ); + QTest::newRow( "like 6" ) << "'[testing]' like '[testing%'" << false << QVariant( 1 ); QTest::newRow( "ilike" ) << "'hello' ilike '%LO'" << false << QVariant( 1 ); + QTest::newRow( "ilike with dot" ) << "'QGIS .123' ilike 'qgis .123'" << false << QVariant( 1 ); // the \\\\ is like \\ in the interface QTest::newRow( "like escape 1" ) << "'1%' like '1\\\\%'" << false << QVariant( 1 ); QTest::newRow( "like escape 2" ) << "'1_' like '1\\\\%'" << false << QVariant( 0 );