Skip to content

Commit

Permalink
Merge pull request #7231 from elpaso/bugfix-18282-non-ascii-tags
Browse files Browse the repository at this point in the history
[bugfix] Style Manager - non ascii tags in symbols
  • Loading branch information
elpaso committed Jun 14, 2018
2 parents 22ce708 + b3c31d3 commit 365fbed
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 19 deletions.
37 changes: 21 additions & 16 deletions src/core/symbology/qgsstyle.cpp
Expand Up @@ -870,29 +870,21 @@ bool QgsStyle::tagSymbol( StyleEntity type, const QString &symbol, const QString
if ( !tag.isEmpty() )
{
// sql: gets the id of the tag if present or insert the tag and get the id of the tag
auto query = QgsSqlite3Mprintf( "SELECT id FROM tag WHERE LOWER(name)='%q'", tag.toUtf8().toLower().constData() );

sqlite3_statement_unique_ptr statement;
int nErr; statement = mCurrentDB.prepare( query, nErr );

int tagid;
if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
{
tagid = sqlite3_column_int( statement.get(), 0 );
}
else
int tagid( tagId( tag ) );
if ( ! tagid )
{
tagid = addTag( tag );
}

// Now map the tag to the symbol if it's not already tagged
if ( !symbolHasTag( type, symbol, tag ) )
{
query = type == SymbolEntity
? QgsSqlite3Mprintf( "INSERT INTO tagmap VALUES (%d,%d)", tagid, symbolid )
: QgsSqlite3Mprintf( "INSERT INTO ctagmap VALUES (%d,%d)", tagid, symbolid );
auto query = type == SymbolEntity
? QgsSqlite3Mprintf( "INSERT INTO tagmap VALUES (%d,%d)", tagid, symbolid )
: QgsSqlite3Mprintf( "INSERT INTO ctagmap VALUES (%d,%d)", tagid, symbolid );

char *zErr = nullptr;
int nErr;
nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(), nullptr, nullptr, &zErr );
if ( nErr )
{
Expand Down Expand Up @@ -963,7 +955,7 @@ bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database for detgging." );
QgsDebugMsg( "Sorry! Cannot open database for detagging." );
return false;
}

Expand Down Expand Up @@ -1085,7 +1077,8 @@ QString QgsStyle::tag( int id ) const

int QgsStyle::getId( const QString &table, const QString &name )
{
auto query = QgsSqlite3Mprintf( "SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), name.toUtf8().toLower().constData() );
QString lowerName( name.toLower() );
auto query = QgsSqlite3Mprintf( "SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), lowerName.toUtf8().constData() );

sqlite3_statement_unique_ptr statement;
int nErr; statement = mCurrentDB.prepare( query, nErr );
Expand All @@ -1095,6 +1088,18 @@ int QgsStyle::getId( const QString &table, const QString &name )
{
id = sqlite3_column_int( statement.get(), 0 );
}
else
{
// Try the name without lowercase conversion
auto query = QgsSqlite3Mprintf( "SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );

sqlite3_statement_unique_ptr statement;
int nErr; statement = mCurrentDB.prepare( query, nErr );
if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
{
id = sqlite3_column_int( statement.get(), 0 );
}
}

return id;
}
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -192,6 +192,7 @@ SET(TESTS
testqgsmeshlayer.cpp
testqgsmeshlayerrenderer.cpp
testqgslayerdefinition.cpp
testqgssqliteutils.cpp
)

IF(WITH_QTWEBKIT)
Expand Down
95 changes: 95 additions & 0 deletions tests/src/core/testqgssqliteutils.cpp
@@ -0,0 +1,95 @@
/***************************************************************************
testqgssqliteutils.cpp
--------------------------------------
Date : 2018-06-13
Copyright : (C) 2018 Alessandro Pasotti
Email : elpaso at itopen dot it
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgstest.h"
#include <QObject>
#include <QStringList>
#include <QApplication>
#include <QFileInfo>

//qgis includes...
#include "qgssqliteutils.h"


/**
* \ingroup UnitTests
* This is a unit test to verify that QgsSqliteUtils are working correctly
*/
class TestQgsSqliteUtils : public QObject
{
Q_OBJECT

public:

TestQgsSqliteUtils() = default;


private slots:

// init / cleanup
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init() {}// will be called before each testfunction is executed.
void cleanup() {}// will be called after every testfunction.
// void initStyles();

void testPrintfAscii();
void testPrintfUtf8();
};


// slots
void TestQgsSqliteUtils::initTestCase()
{
// initialize with test settings directory so we don't mess with user's stuff
QgsApplication::init( QDir::tempPath() + "/dot-qgis" );
QgsApplication::initQgis();
QgsApplication::createDatabase();

// output test environment
QgsApplication::showSettings();

// Set up the QgsSettings environment
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );


}

void TestQgsSqliteUtils::cleanupTestCase()
{
QgsApplication::exitQgis();
}

void TestQgsSqliteUtils::testPrintfAscii()
{
QString tag( "Meteor" );
QString query( QgsSqlite3Mprintf( "SELECT id FROM tag WHERE LOWER(name)='%q'", tag.toUtf8().toLower().constData() ) );
QCOMPARE( query, QString( "SELECT id FROM tag WHERE LOWER(name)='%1'" ).arg( tag.toLower() ) );
}


void TestQgsSqliteUtils::testPrintfUtf8()
{
QString tag( "МЕТЕОР" );
QCOMPARE( tag.toLower(), QString( "метеор" ) );
QString lowerTag( tag.toLower() );
QString query( QgsSqlite3Mprintf( "SELECT id FROM tag WHERE LOWER(name)='%q'", lowerTag.toUtf8().constData() ) );
QCOMPARE( query, QString( "SELECT id FROM tag WHERE LOWER(name)='%1'" ).arg( lowerTag ) );
}


QGSTEST_MAIN( TestQgsSqliteUtils )
#include "testqgssqliteutils.moc"
31 changes: 28 additions & 3 deletions tests/src/core/testqgsstyle.cpp
Expand Up @@ -291,28 +291,35 @@ void TestStyle::testTags()
QCOMPARE( id, mStyle->tagId( "purple" ) );
QCOMPARE( QStringLiteral( "purple" ), mStyle->tag( id ) );

// Cyrillic
id = mStyle->addTag( QStringLiteral( "МЕТЕОР" ) );
QCOMPARE( id, mStyle->tagId( "МЕТЕОР" ) );

QStringList tags = mStyle->tags();
QCOMPARE( tags.count(), 5 );
QCOMPARE( tags.count(), 6 );
QVERIFY( tags.contains( "red" ) );
QVERIFY( tags.contains( "starry" ) );
QVERIFY( tags.contains( "circle" ) );
QVERIFY( tags.contains( "blue" ) );
QVERIFY( tags.contains( "purple" ) );
QVERIFY( tags.contains( "МЕТЕОР" ) );

//remove tag
mStyle->remove( QgsStyle::TagEntity, mStyle->tagId( QStringLiteral( "purple" ) ) );
mStyle->remove( QgsStyle::TagEntity, -999 ); //bad id
tags = mStyle->tags();
QCOMPARE( tags.count(), 4 );
QCOMPARE( tags.count(), 5 );
QVERIFY( !tags.contains( "purple" ) );

//add some symbols
std::unique_ptr< QgsMarkerSymbol> sym1( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
std::unique_ptr< QgsMarkerSymbol> sym2( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
std::unique_ptr< QgsMarkerSymbol> sym3( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
std::unique_ptr< QgsMarkerSymbol> sym4( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
QVERIFY( mStyle->saveSymbol( "symbol1", sym1.get(), false, QStringList() << "red" << "starry" ) );
mStyle->addSymbol( QStringLiteral( "blue starry" ), sym2.release(), true );
mStyle->addSymbol( QStringLiteral( "red circle" ), sym3.release(), true );
mStyle->addSymbol( QStringLiteral( "МЕТЕОР" ), sym4.release(), true );

//tag them
QVERIFY( mStyle->tagSymbol( QgsStyle::SymbolEntity, "blue starry", QStringList() << "blue" << "starry" ) );
Expand All @@ -324,6 +331,13 @@ void TestStyle::testTags()
tags = mStyle->tags();
QVERIFY( tags.contains( "round" ) );

// Cyrillic
// Add twice (see issue #18281)
QVERIFY( mStyle->tagSymbol( QgsStyle::SymbolEntity, "МЕТЕОР", QStringList() << "МЕТЕОР" ) );
tags = mStyle->tags();
QVERIFY( tags.contains( "МЕТЕОР" ) );
QCOMPARE( tags.filter( "МЕТЕОР" ).count(), 1 );

//check that tags have been applied
tags = mStyle->tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "blue starry" ) );
QCOMPARE( tags.count(), 2 );
Expand All @@ -339,8 +353,13 @@ void TestStyle::testTags()
QVERIFY( tags.contains( "red" ) );
QVERIFY( tags.contains( "starry" ) );

tags = mStyle->tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "МЕТЕОР" ) );
QCOMPARE( tags.count(), 1 );
QVERIFY( tags.contains( "МЕТЕОР" ) );

//check that a given tag is attached to a symbol
QVERIFY( mStyle->symbolHasTag( QgsStyle::SymbolEntity, QStringLiteral( "blue starry" ), QStringLiteral( "blue" ) ) );
QVERIFY( mStyle->symbolHasTag( QgsStyle::SymbolEntity, QStringLiteral( "МЕТЕОР" ), QStringLiteral( "МЕТЕОР" ) ) );

//check that a given tag is not attached to a symbol
QCOMPARE( false, mStyle->symbolHasTag( QgsStyle::SymbolEntity, QStringLiteral( "blue starry" ), QStringLiteral( "notblue" ) ) );
Expand Down Expand Up @@ -368,7 +387,9 @@ void TestStyle::testTags()
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->symbolsWithTag( QgsStyle::SymbolEntity, mStyle->tagId( QStringLiteral( "round" ) ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->symbolsWithTag( QgsStyle::SymbolEntity, mStyle->tagId( QStringLiteral( "МЕТЕОР" ) ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "МЕТЕОР" ) );
symbols = mStyle->symbolsWithTag( QgsStyle::SymbolEntity, mStyle->tagId( QStringLiteral( "blue" ) ) );
QVERIFY( symbols.isEmpty() );
symbols = mStyle->symbolsWithTag( QgsStyle::SymbolEntity, mStyle->tagId( QStringLiteral( "no tag" ) ) );
Expand All @@ -392,6 +413,10 @@ void TestStyle::testTags()
symbols = mStyle->findSymbols( QgsStyle::SymbolEntity, QStringLiteral( "round" ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->findSymbols( QgsStyle::SymbolEntity, QStringLiteral( "МЕТЕОР" ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "МЕТЕОР" ) );

}

QGSTEST_MAIN( TestStyle )
Expand Down

0 comments on commit 365fbed

Please sign in to comment.