SettingsAccessor: Split out reading of shared settings and the merging

Change-Id: Iccf9b88ba7f778ccdc17e088a60680a8d5cf840b
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
This commit is contained in:
Tobias Hunger
2013-04-04 16:19:17 +02:00
parent 8113106f43
commit 0addee8bac
2 changed files with 100 additions and 91 deletions

View File

@@ -492,7 +492,7 @@ namespace {
// It's assumed that the shared map has the same structure as the user map. // It's assumed that the shared map has the same structure as the user map.
template <class Operation_T> template <class Operation_T>
void synchronizeSettings(QVariantMap *userMap, void synchronizeSettings(QVariantMap &userMap,
const QVariantMap &sharedMap, const QVariantMap &sharedMap,
Operation_T *op) Operation_T *op)
{ {
@@ -501,32 +501,33 @@ void synchronizeSettings(QVariantMap *userMap,
for (; it != eit; ++it) { for (; it != eit; ++it) {
const QString &key = it.key(); const QString &key = it.key();
const QVariant &sharedValue = it.value(); const QVariant &sharedValue = it.value();
const QVariant &userValue = userMap->value(key); const QVariant &userValue = userMap.value(key);
if (sharedValue.type() == QVariant::Map) { if (sharedValue.type() == QVariant::Map) {
if (userValue.type() != QVariant::Map) { if (userValue.type() != QVariant::Map) {
// This should happen only if the user manually changed the file in such a way. // This should happen only if the user manually changed the file in such a way.
continue; continue;
} }
QVariantMap nestedUserMap = userValue.toMap(); QVariantMap nestedUserMap = userValue.toMap();
synchronizeSettings(&nestedUserMap, synchronizeSettings(nestedUserMap,
sharedValue.toMap(), sharedValue.toMap(),
op); op);
userMap->insert(key, nestedUserMap); userMap.insert(key, nestedUserMap);
} else if (userMap->contains(key) && userValue != sharedValue) { } else if (userMap.contains(key) && userValue != sharedValue) {
op->apply(userMap, key, sharedValue); op->apply(userMap, key, sharedValue);
} }
} }
} }
struct MergeSharedSetting class MergeSharedSetting
{ {
public:
MergeSharedSetting(const QSet<QString> &sticky) : m_userSticky(sticky) {} MergeSharedSetting(const QSet<QString> &sticky) : m_userSticky(sticky) {}
void apply(QVariantMap *userMap, const QString &key, const QVariant &sharedValue) void apply(QVariantMap &userMap, const QString &key, const QVariant &sharedValue)
{ {
if (!m_userSticky.contains(key)) if (!m_userSticky.contains(key))
userMap->insert(key, sharedValue); userMap.insert(key, sharedValue);
} }
QSet<QString> m_userSticky; QSet<QString> m_userSticky;
}; };
@@ -536,30 +537,32 @@ struct MergeSharedSetting
// corresponding ones in the .user file. Whenever we identify a corresponding setting which // corresponding ones in the .user file. Whenever we identify a corresponding setting which
// has a different value and which is not marked as sticky, we merge the .shared value into // has a different value and which is not marked as sticky, we merge the .shared value into
// the .user value. // the .user value.
void mergeSharedSettings(QVariantMap *userMap, const QVariantMap &sharedMap) QVariantMap mergeSharedSettings(const QVariantMap &userMap, const QVariantMap &sharedMap)
{ {
QVariantMap result = userMap;
if (sharedMap.isEmpty()) if (sharedMap.isEmpty())
return; return result;
QSet<QString> stickyKeys; QSet<QString> stickyKeys;
const QVariant stickyList = userMap->take(QLatin1String(USER_STICKY_KEYS_KEY)).toList(); const QVariant stickyList = result.take(QLatin1String(USER_STICKY_KEYS_KEY)).toList();
if (stickyList.isValid()) { if (stickyList.isValid()) {
if (stickyList.type() != QVariant::List) { if (stickyList.type() != QVariant::List) {
// File is messed up... The user probably changed something. // File is messed up... The user probably changed something.
return; return result;
} }
foreach (const QVariant &v, stickyList.toList()) foreach (const QVariant &v, stickyList.toList())
stickyKeys.insert(v.toString()); stickyKeys.insert(v.toString());
} }
MergeSharedSetting op(stickyKeys); MergeSharedSetting op(stickyKeys);
synchronizeSettings(userMap, sharedMap, &op); synchronizeSettings(result, sharedMap, &op);
return result;
} }
class TrackUserStickySetting
struct TrackUserStickySetting
{ {
void apply(QVariantMap *, const QString &key, const QVariant &) public:
void apply(QVariantMap &, const QString &key, const QVariant &)
{ {
m_userSticky.insert(key); m_userSticky.insert(key);
} }
@@ -574,7 +577,7 @@ struct TrackUserStickySetting
// Although this approach is more flexible than permanent/forever sticky settings, it has // Although this approach is more flexible than permanent/forever sticky settings, it has
// the side-effect that if a particular value unintentionally becomes the same in both // the side-effect that if a particular value unintentionally becomes the same in both
// the .user and .shared files, this setting will "unstick". // the .user and .shared files, this setting will "unstick".
void trackUserStickySettings(QVariantMap *userMap, const QVariantMap &sharedMap) void trackUserStickySettings(QVariantMap &userMap, const QVariantMap &sharedMap)
{ {
if (sharedMap.isEmpty()) if (sharedMap.isEmpty())
return; return;
@@ -582,7 +585,7 @@ void trackUserStickySettings(QVariantMap *userMap, const QVariantMap &sharedMap)
TrackUserStickySetting op; TrackUserStickySetting op;
synchronizeSettings(userMap, sharedMap, &op); synchronizeSettings(userMap, sharedMap, &op);
userMap->insert(QLatin1String(USER_STICKY_KEYS_KEY), QVariant(op.m_userSticky.toList())); userMap.insert(QLatin1String(USER_STICKY_KEYS_KEY), QVariant(op.m_userSticky.toList()));
} }
} // Anonymous } // Anonymous
@@ -594,77 +597,12 @@ QVariantMap SettingsAccessor::restoreSettings() const
return QVariantMap(); return QVariantMap();
SettingsData userSettings = readUserSettings(); SettingsData userSettings = readUserSettings();
SettingsData sharedSettings = readSharedSettings();
// Time to consider shared settings... userSettings = mergeSettings(userSettings, sharedSettings);
SettingsData sharedSettings;
QString fn = project()->property(m_sharedFileAcessor.id()).toString();
if (fn.isEmpty())
fn = project()->document()->fileName() + m_sharedFileAcessor.suffix();
sharedSettings.m_fileName = Utils::FileName::fromString(fn);
if (!sharedSettings.fileName().isEmpty() && m_sharedFileAcessor.readFile(&sharedSettings)) {
bool useSharedSettings = true;
if (sharedSettings.m_version != userSettings.m_version) {
int baseFileVersion;
if (sharedSettings.m_version > m_lastVersion + 1) {
// The shared file version is newer than Creator... If we have valid user
// settings we prompt the user whether we could try an *unsupported* update.
// This makes sense since the merging operation will only replace shared settings
// that perfectly match corresponding user ones. If we don't have valid user
// settings to compare against, there's nothing we can do.
if (!userSettings.isValid())
return QVariantMap();
QMessageBox msgBox(
QMessageBox::Question,
QApplication::translate("ProjectExplorer::SettingsAccessor",
"Unsupported Shared Settings File"),
QApplication::translate("ProjectExplorer::SettingsAccessor",
"The version of your .shared file is not "
"supported by this Qt Creator version. "
"Only settings that are still compatible "
"will be taken into account.\n\n"
"Do you want to try loading it?"),
QMessageBox::Yes | QMessageBox::No,
Core::ICore::mainWindow());
msgBox.setDefaultButton(QMessageBox::No);
msgBox.setEscapeButton(QMessageBox::No);
if (msgBox.exec() == QMessageBox::No)
useSharedSettings = false;
else
baseFileVersion = m_lastVersion + 1;
} else {
baseFileVersion = qMax(userSettings.m_version, sharedSettings.m_version);
}
if (useSharedSettings) {
// We now update the user and shared settings so they are compatible.
for (int i = sharedSettings.m_version; i < baseFileVersion; ++i)
sharedSettings.m_map = m_handlers.value(i)->update(m_project, sharedSettings.m_map);
if (userSettings.isValid()) {
for (int i = userSettings.m_version; i < baseFileVersion; ++i)
userSettings.m_map = m_handlers.value(i)->update(m_project, userSettings.m_map);
userSettings.m_version = baseFileVersion;
}
}
}
if (useSharedSettings) {
m_project->setProperty(SHARED_SETTINGS, sharedSettings.m_map);
if (userSettings.isValid())
mergeSharedSettings(&userSettings.m_map, sharedSettings.m_map);
if (!userSettings.isValid())
userSettings = sharedSettings;
}
}
if (!userSettings.isValid()) if (!userSettings.isValid())
return QVariantMap(); return QVariantMap();
// Update from the base version to Creator's version.
for (int i = userSettings.m_version; i <= m_lastVersion; ++i)
userSettings.m_map = m_handlers.value(i)->update(m_project, userSettings.m_map);
return userSettings.m_map; return userSettings.m_map;
} }
@@ -674,13 +612,10 @@ bool SettingsAccessor::saveSettings(const QVariantMap &map) const
return false; return false;
SettingsData settings(map); SettingsData settings(map);
QString fn = project()->property(m_userFileAcessor.id()).toString(); settings.m_fileName = Utils::FileName::fromString(defaultFileName(m_userFileAcessor.suffix()));
if (fn.isEmpty())
fn = project()->document()->fileName() + m_userFileAcessor.suffix();
settings.m_fileName = Utils::FileName::fromString(fn);
const QVariant &shared = m_project->property(SHARED_SETTINGS); const QVariant &shared = m_project->property(SHARED_SETTINGS);
if (shared.isValid()) if (shared.isValid())
trackUserStickySettings(&settings.m_map, shared.toMap()); trackUserStickySettings(settings.m_map, shared.toMap());
return m_userFileAcessor.writeFile(&settings); return m_userFileAcessor.writeFile(&settings);
} }
@@ -751,6 +686,12 @@ int SettingsAccessor::currentVersion() const
return m_lastVersion + 1; return m_lastVersion + 1;
} }
void SettingsAccessor::incrementVersion(SettingsAccessor::SettingsData &data) const
{
data.m_map = m_handlers.value(data.version())->update(m_project, data.m_map);
++data.m_version;
}
SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const
{ {
SettingsData result; SettingsData result;
@@ -815,6 +756,42 @@ SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const
return result; return result;
} }
SettingsAccessor::SettingsData SettingsAccessor::readSharedSettings() const
{
SettingsData sharedSettings;
QString fn = project()->document()->fileName() + m_sharedFileAcessor.suffix();
sharedSettings.m_fileName = Utils::FileName::fromString(fn);
if (!m_sharedFileAcessor.readFile(&sharedSettings))
return sharedSettings;
if (sharedSettings.m_version > currentVersion()) {
// The shared file version is newer than Creator... If we have valid user
// settings we prompt the user whether we could try an *unsupported* update.
// This makes sense since the merging operation will only replace shared settings
// that perfectly match corresponding user ones. If we don't have valid user
// settings to compare against, there's nothing we can do.
QMessageBox msgBox(
QMessageBox::Question,
QApplication::translate("ProjectExplorer::SettingsAccessor",
"Unsupported Shared Settings File"),
QApplication::translate("ProjectExplorer::SettingsAccessor",
"The version of your .shared file is not "
"supported by Qt Creator. "
"Do you want to try loading it anyway?"),
QMessageBox::Yes | QMessageBox::No,
Core::ICore::mainWindow());
msgBox.setDefaultButton(QMessageBox::No);
msgBox.setEscapeButton(QMessageBox::No);
if (msgBox.exec() == QMessageBox::No)
sharedSettings.clear();
else
sharedSettings.m_version = currentVersion();
}
return sharedSettings;
}
SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringList &candidates) const SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringList &candidates) const
{ {
SettingsData newestNonMatching; SettingsData newestNonMatching;
@@ -856,6 +833,34 @@ SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringL
return result; return result;
} }
SettingsAccessor::SettingsData SettingsAccessor::mergeSettings(const SettingsAccessor::SettingsData &user,
const SettingsAccessor::SettingsData &shared) const
{
SettingsData newUser = user;
SettingsData newShared = shared;
if (shared.isValid() && user.isValid()) {
while (newUser.version() < newShared.version())
incrementVersion(newUser);
while (newShared.version() < newUser.version())
incrementVersion(newShared);
}
m_project->setProperty(SHARED_SETTINGS, newShared.m_map);
SettingsData result = newUser;
result.m_map = mergeSharedSettings(newUser.m_map, newShared.m_map);
if (!result.isValid())
return result;
// Update from the base version to Creator's version.
for (int i = result.version(); i < currentVersion(); ++i)
incrementVersion(result);
return result;
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// SettingsData // SettingsData
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------

View File

@@ -56,9 +56,9 @@ public:
bool saveSettings(const QVariantMap &map) const; bool saveSettings(const QVariantMap &map) const;
private: private:
// Takes ownership of the handler! // Takes ownership of the handler!
void addVersionHandler(Internal::UserFileVersionHandler *handler); void addVersionHandler(Internal::UserFileVersionHandler *handler);
QStringList findSettingsFiles(const QString &suffix) const; QStringList findSettingsFiles(const QString &suffix) const;
QByteArray creatorId() const; QByteArray creatorId() const;
QString defaultFileName(const QString &suffix) const; QString defaultFileName(const QString &suffix) const;
@@ -84,8 +84,12 @@ private:
Utils::FileName m_fileName; Utils::FileName m_fileName;
}; };
void incrementVersion(SettingsData &data) const;
SettingsData readUserSettings() const; SettingsData readUserSettings() const;
SettingsData readSharedSettings() const;
SettingsData findBestSettings(const QStringList &candidates) const; SettingsData findBestSettings(const QStringList &candidates) const;
SettingsData mergeSettings(const SettingsData &user, const SettingsData &shared) const;
// The entity which actually reads/writes to the settings file. // The entity which actually reads/writes to the settings file.
class FileAccessor class FileAccessor