|
| 1 | +/*************************************************************************** |
| 2 | + qgsversionmigration.cpp - QgsVersionMigration |
| 3 | +
|
| 4 | + --------------------- |
| 5 | + begin : 30.7.2017 |
| 6 | + copyright : (C) 2017 by nathan |
| 7 | + email : woodrow.nathan at gmail dot com |
| 8 | + *************************************************************************** |
| 9 | + * * |
| 10 | + * This program is free software; you can redistribute it and/or modify * |
| 11 | + * it under the terms of the GNU General Public License as published by * |
| 12 | + * the Free Software Foundation; either version 2 of the License, or * |
| 13 | + * (at your option) any later version. * |
| 14 | + * * |
| 15 | + ***************************************************************************/ |
| 16 | +#include "qgsversionmigration.h" |
| 17 | +#include "qgssettings.h" |
| 18 | +#include "qgslogger.h" |
| 19 | +#include "qsettings.h" |
| 20 | +#include "qgsmessagelog.h" |
| 21 | +#include "qgsapplication.h" |
| 22 | +#include "qgssymbol.h" |
| 23 | +#include "qgsstyle.h" |
| 24 | +#include "qgssymbollayerutils.h" |
| 25 | +#include "qgsreadwritecontext.h" |
| 26 | + |
| 27 | +#include <QFile> |
| 28 | +#include <QTextStream> |
| 29 | +#include <QDir> |
| 30 | +#include <QSqlDatabase> |
| 31 | +#include <QSqlQuery> |
| 32 | +#include <QSqlError> |
| 33 | +#include <QDomDocument> |
| 34 | + |
| 35 | +QgsVersionMigration::QgsVersionMigration() |
| 36 | +{ |
| 37 | + |
| 38 | +} |
| 39 | + |
| 40 | +QgsVersionMigration::~QgsVersionMigration() |
| 41 | +{ |
| 42 | + |
| 43 | +} |
| 44 | + |
| 45 | +QgsVersionMigration *QgsVersionMigration::canMigrate( int fromVersion, int toVersion ) |
| 46 | +{ |
| 47 | + if ( fromVersion == 20000 && toVersion >= 29900 ) |
| 48 | + { |
| 49 | + return new Qgs2To3Migration(); |
| 50 | + } |
| 51 | + return nullptr; |
| 52 | +} |
| 53 | + |
| 54 | +QgsError Qgs2To3Migration::runMigration() |
| 55 | +{ |
| 56 | + QgsError error; |
| 57 | + QgsError settingsErrors = migrateSettings(); |
| 58 | + if ( !settingsErrors.isEmpty() ) |
| 59 | + { |
| 60 | + // TODO Merge error messages |
| 61 | + } |
| 62 | + QgsError stylesError = migrateStyles(); |
| 63 | + return error; |
| 64 | +} |
| 65 | + |
| 66 | +bool Qgs2To3Migration::requiresMigration() |
| 67 | +{ |
| 68 | + QgsSettings settings; |
| 69 | + bool alreadyMigrated = settings.value( QStringLiteral( "migration/settings" ), false ).toBool(); |
| 70 | + int settingsMigrationVersion = settings.value( QStringLiteral( "migration/fileVersion" ), 0 ).toInt(); |
| 71 | + QFile migrationFile( migrationFilePath() ); |
| 72 | + if ( migrationFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) |
| 73 | + { |
| 74 | + QTextStream in( &migrationFile ); |
| 75 | + QString line = in.readLine(); |
| 76 | + if ( line.startsWith( "#" ) && line.contains( QStringLiteral( "version=" ) ) ) |
| 77 | + { |
| 78 | + QStringList parts = line.split( '=' ); |
| 79 | + mMigrationFileVersion = parts.at( 1 ).toInt(); |
| 80 | + QgsDebugMsg( QString( "File version is=%1" ).arg( mMigrationFileVersion ) ); |
| 81 | + } |
| 82 | + migrationFile.close(); |
| 83 | + } |
| 84 | + else |
| 85 | + { |
| 86 | + QString msg = QString( "Can not open %1" ).arg( migrationFile.fileName() ); |
| 87 | + QgsDebugMsg( msg ); |
| 88 | + mMigrationFileVersion = settingsMigrationVersion; |
| 89 | + } |
| 90 | + |
| 91 | + return ( !alreadyMigrated || settingsMigrationVersion != mMigrationFileVersion ); |
| 92 | +} |
| 93 | + |
| 94 | +QgsError Qgs2To3Migration::migrateStyles() |
| 95 | +{ |
| 96 | + QgsError error; |
| 97 | + QString oldHome = QStringLiteral( "%1/.qgis2" ).arg( QDir::homePath() ); |
| 98 | + QString oldStyleFile = QStringLiteral( "%1/symbology-ng-style.db" ).arg( oldHome ); |
| 99 | + QgsDebugMsg( QString( "OLD STYLE FILE %1" ).arg( oldStyleFile ) ); |
| 100 | + QSqlDatabase db = QSqlDatabase::addDatabase( "QSQLITE", "migration" ); |
| 101 | + db.setDatabaseName( oldStyleFile ); |
| 102 | + if ( !db.open() ) |
| 103 | + { |
| 104 | + error.append( db.lastError().text() ); |
| 105 | + QgsDebugMsg( db.lastError().text() ); |
| 106 | + return error; |
| 107 | + } |
| 108 | + |
| 109 | + QSqlQuery query( db ); |
| 110 | + QSqlQuery tagQuery( "SELECT name FROM tag" |
| 111 | + "JOIN tagmap ON tagmap.tag_id = tag.id" |
| 112 | + "WHERE tagmap.symbol_id = :symbol_id", db ); |
| 113 | + |
| 114 | + QgsStyle *style = QgsStyle::defaultStyle(); |
| 115 | + if ( query.exec( "SELECT id, name, xml FROM symbol" ) ) |
| 116 | + { |
| 117 | + while ( query.next() ) |
| 118 | + { |
| 119 | + QString symbol_id = query.value( 0 ).toString(); |
| 120 | + QString name = query.value( 1 ).toString(); |
| 121 | + QString xml = query.value( 2 ).toString(); |
| 122 | + QDomDocument doc; |
| 123 | + if ( !doc.setContent( xml ) ) |
| 124 | + { |
| 125 | + QgsDebugMsg( "Cannot open symbol " + name ); |
| 126 | + continue; |
| 127 | + } |
| 128 | + |
| 129 | + tagQuery.bindValue( ":symbol_id", symbol_id ); |
| 130 | + |
| 131 | + QStringList tags; |
| 132 | + if ( tagQuery.exec() ) |
| 133 | + { |
| 134 | + while ( query.next() ) |
| 135 | + { |
| 136 | + QString tagname = query.value( 0 ).toString(); |
| 137 | + tags << tagname; |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + QDomElement symElement = doc.documentElement(); |
| 142 | + QgsDebugMsg( QString( "MIGRATION: Importing %1" ).arg( name ) ); |
| 143 | + QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol( symElement, QgsReadWriteContext() ); |
| 144 | + tags << "QGIS 2"; |
| 145 | + if ( style->symbolId( name ) == 0 ) |
| 146 | + { |
| 147 | + style->saveSymbol( name, symbol, false, tags ); |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + QgsDebugMsg( oldStyleFile ); |
| 153 | + return error; |
| 154 | +} |
| 155 | + |
| 156 | +QgsError Qgs2To3Migration::migrateSettings() |
| 157 | +{ |
| 158 | + QgsError error; |
| 159 | + |
| 160 | + QgsSettings newSettings; |
| 161 | + |
| 162 | + // The platform default location for the settings from 2.x |
| 163 | + mOldSettings = new QSettings( "QGIS", "QGIS2" ); |
| 164 | + |
| 165 | + QFile inputFile( migrationFilePath() ); |
| 166 | + std::map<QString, QgsSettings::Section> sections; |
| 167 | + sections["none"] = QgsSettings::NoSection; |
| 168 | + sections["core"] = QgsSettings::Core; |
| 169 | + sections["gui"] = QgsSettings::Gui; |
| 170 | + sections["server"] = QgsSettings::Server; |
| 171 | + sections["plugins"] = QgsSettings::Plugins; |
| 172 | + sections["auth"] = QgsSettings::Auth; |
| 173 | + sections["app"] = QgsSettings::App; |
| 174 | + sections["providers"] = QgsSettings::Providers; |
| 175 | + sections["misc"] = QgsSettings::Misc; |
| 176 | + |
| 177 | + QList<QPair<QString, QString>> keys; |
| 178 | + |
| 179 | + if ( inputFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) |
| 180 | + { |
| 181 | + QTextStream in( &inputFile ); |
| 182 | + while ( !in.atEnd() ) |
| 183 | + { |
| 184 | + QString line = in.readLine(); |
| 185 | + |
| 186 | + if ( line.startsWith( "#" ) ) |
| 187 | + continue; |
| 188 | + |
| 189 | + if ( line.isEmpty() ) |
| 190 | + continue; |
| 191 | + |
| 192 | + QStringList parts = line.split( ";" ); |
| 193 | + |
| 194 | + Q_ASSERT_X( parts.count() == 2, "QgsVersionMigration::migrateSettings()", "Can't split line in 2 parts." ); |
| 195 | + |
| 196 | + QString oldKey = parts.at( 0 ); |
| 197 | + QString newKey = parts.at( 1 ); |
| 198 | + |
| 199 | + if ( oldKey.endsWith( "/*" ) ) |
| 200 | + { |
| 201 | + oldKey = oldKey.replace( "/*", "" ); |
| 202 | + QList<QPair<QString, QString>> keyList = walk( oldKey, newKey ); |
| 203 | + keys.append( keyList ); |
| 204 | + } |
| 205 | + else |
| 206 | + { |
| 207 | + QPair<QString, QString> key = transformKey( oldKey, newKey ); |
| 208 | + keys.append( key ); |
| 209 | + } |
| 210 | + |
| 211 | + } |
| 212 | + inputFile.close(); |
| 213 | + newSettings.setValue( QStringLiteral( "migration/settings" ), true ); |
| 214 | + // Set the dev gen so we can force a migration. |
| 215 | + newSettings.setValue( QStringLiteral( "migration/fileVersion" ), mMigrationFileVersion ); |
| 216 | + } |
| 217 | + else |
| 218 | + { |
| 219 | + QString msg = QString( "Can not open %1" ).arg( inputFile.fileName() ); |
| 220 | + QgsDebugMsg( msg ); |
| 221 | + error.append( msg ); |
| 222 | + } |
| 223 | + |
| 224 | + if ( keys.count() > 0 ) |
| 225 | + { |
| 226 | + QgsDebugMsg( "MIGRATION: Translating settings keys" ); |
| 227 | + QList<QPair<QString, QString>>::iterator i; |
| 228 | + for ( i = keys.begin(); i != keys.end(); ++i ) |
| 229 | + { |
| 230 | + QPair<QString, QString> pair = *i; |
| 231 | + |
| 232 | + QString oldKey = pair.first; |
| 233 | + QString newKey = pair.second; |
| 234 | + |
| 235 | + if ( oldKey.contains( oldKey ) ) |
| 236 | + { |
| 237 | + QgsDebugMsg( QString( " -> %1 -> %2" ).arg( oldKey, newKey ) ); |
| 238 | + newSettings.setValue( newKey, mOldSettings->value( oldKey ) ); |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + return error; |
| 243 | +} |
| 244 | + |
| 245 | +QList<QPair<QString, QString> > Qgs2To3Migration::walk( QString group, QString newkey ) |
| 246 | +{ |
| 247 | + mOldSettings->beginGroup( group ); |
| 248 | + QList<QPair<QString, QString> > foundKeys; |
| 249 | + Q_FOREACH ( const QString &group, mOldSettings->childGroups() ) |
| 250 | + { |
| 251 | + QList<QPair<QString, QString> > data = walk( group, newkey ); |
| 252 | + foundKeys.append( data ); |
| 253 | + } |
| 254 | + |
| 255 | + Q_FOREACH ( const QString &key, mOldSettings->childKeys() ) |
| 256 | + { |
| 257 | + QString fullKey = mOldSettings->group() + "/" + key; |
| 258 | + foundKeys.append( transformKey( fullKey, newkey ) ); |
| 259 | + } |
| 260 | + mOldSettings->endGroup(); |
| 261 | + return foundKeys; |
| 262 | +} |
| 263 | + |
| 264 | +QPair<QString, QString> Qgs2To3Migration::transformKey( QString fullOldKey, QString newKeyPart ) |
| 265 | +{ |
| 266 | + QString newKey = newKeyPart; |
| 267 | + QString oldKey = fullOldKey; |
| 268 | + |
| 269 | + if ( newKeyPart == QStringLiteral( "*" ) ) |
| 270 | + { |
| 271 | + newKey = fullOldKey; |
| 272 | + } |
| 273 | + |
| 274 | + if ( newKeyPart.endsWith( "/*" ) ) |
| 275 | + { |
| 276 | + QStringList newKeyparts = newKeyPart.split( "/" ); |
| 277 | + // Throw away the * |
| 278 | + newKeyparts.removeLast(); |
| 279 | + QStringList oldKeyParts = fullOldKey.split( "/" ); |
| 280 | + for ( int i = 0; i < newKeyparts.count(); ++i ) |
| 281 | + { |
| 282 | + oldKeyParts.replace( i, newKeyparts.at( i ) ); |
| 283 | + } |
| 284 | + newKey = oldKeyParts.join( "/" ); |
| 285 | + } |
| 286 | + |
| 287 | + return qMakePair( oldKey, newKey ); |
| 288 | +} |
| 289 | + |
| 290 | +QString Qgs2To3Migration::migrationFilePath() |
| 291 | +{ |
| 292 | + return QgsApplication::pkgDataPath() + "/resources/2to3migration.txt"; |
| 293 | +} |
0 commit comments