Skip to content

Commit

Permalink
Merge custom data
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu committed Apr 18, 2019
1 parent 5b28610 commit dcb18b1
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 3 deletions.
27 changes: 27 additions & 0 deletions src/core/CustomData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
*/

#include "CustomData.h"
#include "Clock.h"

#include "core/Global.h"

const QString CustomData::LastModified = "_LAST_MODIFIED";

CustomData::CustomData(QObject* parent)
: QObject(parent)
{
Expand Down Expand Up @@ -60,6 +63,7 @@ void CustomData::set(const QString& key, const QString& value)

if (addAttribute || changeValue) {
m_data.insert(key, value);
updateLastModified();
emit customDataModified();
}

Expand All @@ -74,6 +78,7 @@ void CustomData::remove(const QString& key)

m_data.remove(key);

updateLastModified();
emit removed(key);
emit customDataModified();
}
Expand All @@ -94,6 +99,7 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
m_data.remove(oldKey);
m_data.insert(newKey, data);

updateLastModified();
emit customDataModified();
emit renamed(oldKey, newKey);
}
Expand All @@ -108,9 +114,19 @@ void CustomData::copyDataFrom(const CustomData* other)

m_data = other->m_data;

updateLastModified();
emit reset();
emit customDataModified();
}

QDateTime CustomData::getLastModified() const
{
if (m_data.contains(LastModified)) {
return Clock::parse(m_data.value(LastModified));
}
return {};
}

bool CustomData::operator==(const CustomData& other) const
{
return (m_data == other.m_data);
Expand Down Expand Up @@ -152,3 +168,14 @@ int CustomData::dataSize() const
}
return size;
}

void CustomData::updateLastModified()
{
if (m_data.size() == 1 && m_data.contains(LastModified)) {
m_data.remove(LastModified);
return;
}

// Add/Update LastModified
m_data.insert(LastModified, Clock::currentDateTimeUtc().toString());
}
7 changes: 7 additions & 0 deletions src/core/CustomData.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ class CustomData : public QObject
int size() const;
int dataSize() const;
void copyDataFrom(const CustomData* other);
QDateTime getLastModified() const;
bool operator==(const CustomData& other) const;
bool operator!=(const CustomData& other) const;

static const QString LastModified;

signals:
void customDataModified();
void aboutToBeAdded(const QString& key);
Expand All @@ -55,6 +58,10 @@ class CustomData : public QObject
void renamed(const QString& oldKey, const QString& newKey);
void aboutToBeReset();
void reset();
void lastModified();

private slots:
void updateLastModified();

private:
QHash<QString, QString> m_data;
Expand Down
30 changes: 27 additions & 3 deletions src/core/Merger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,9 +609,6 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context)
// TODO HNH: missing handling of recycle bin, names, templates for groups and entries,
// public data (entries of newer dict override keys of older dict - ignoring
// their own age - it is enough if one entry of the whole dict is newer) => possible lost update
// TODO HNH: CustomData is merged with entries of the new customData overwrite entries
// of the older CustomData - the dict with the newest entry is considered
// newer regardless of the age of the other entries => possible lost update
ChangeList changes;
auto* sourceMetadata = context.m_sourceDb->metadata();
auto* targetMetadata = context.m_targetDb->metadata();
Expand All @@ -624,5 +621,32 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context)
changes << tr("Adding missing icon %1").arg(QString::fromLatin1(customIconId.toRfc4122().toHex()));
}
}

// Merge Custom Data if source is newer
const auto targetCustomDataModificationTime = sourceMetadata->customData()->getLastModified();
const auto sourceCustomDataModificationTime = targetMetadata->customData()->getLastModified();
if (!targetMetadata->customData()->contains(CustomData::LastModified) ||
(targetCustomDataModificationTime.isValid() && sourceCustomDataModificationTime.isValid() &&
targetCustomDataModificationTime > sourceCustomDataModificationTime)) {
const auto sourceCustomDataKeys = sourceMetadata->customData()->keys();
const auto targetCustomDataKeys = targetMetadata->customData()->keys();

// Check missing keys from source. Remove those from target
for (const auto& key : targetCustomDataKeys) {
if (!sourceMetadata->customData()->contains(key)) {
auto value = targetMetadata->customData()->value(key);
targetMetadata->customData()->remove(key);
changes << tr("Removed custom data %1 [%2]").arg(key, value);
}
}

// Transfer new/existing keys
for (const auto& key : sourceCustomDataKeys) {
auto value = sourceMetadata->customData()->value(key);
targetMetadata->customData()->set(key, value);
changes << tr("Adding custom data %1 [%2]").arg(key, value);
}
}

return changes;
}
59 changes: 59 additions & 0 deletions tests/TestMerge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,65 @@ void TestMerge::testMetadata()
// will be used - exception is the target has no recycle bin activated
}

void TestMerge::testCustomdata()
{
QScopedPointer<Database> dbDestination(new Database());
QScopedPointer<Database> dbSource(createTestDatabase());
QScopedPointer<Database> dbDestination2(new Database());
QScopedPointer<Database> dbSource2(createTestDatabase());

m_clock->advanceSecond(1);

dbDestination->metadata()->customData()->set("toBeDeleted", "value");
dbDestination->metadata()->customData()->set("key3", "oldValue");

dbSource2->metadata()->customData()->set("key1", "value1");
dbSource2->metadata()->customData()->set("key2", "value2");
dbSource2->metadata()->customData()->set("key3", "newValue");
dbSource2->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]");

m_clock->advanceSecond(1);

dbSource->metadata()->customData()->set("key1", "value1");
dbSource->metadata()->customData()->set("key2", "value2");
dbSource->metadata()->customData()->set("key3", "newValue");
dbSource->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]");

dbDestination2->metadata()->customData()->set("notToBeDeleted", "value");
dbDestination2->metadata()->customData()->set("key3", "oldValue");

// Sanity check.
QVERIFY(!dbSource->metadata()->customData()->isEmpty());
QVERIFY(!dbSource2->metadata()->customData()->isEmpty());

m_clock->advanceSecond(1);

Merger merger(dbSource.data(), dbDestination.data());
merger.merge();

Merger merger2(dbSource2.data(), dbDestination2.data());
merger2.merge();

// Source is newer, data should be merged
QVERIFY(!dbDestination->metadata()->customData()->isEmpty());
QVERIFY(dbDestination->metadata()->customData()->contains("key1"));
QVERIFY(dbDestination->metadata()->customData()->contains("key2"));
QVERIFY(dbDestination->metadata()->customData()->contains("Browser"));
QVERIFY(!dbDestination->metadata()->customData()->contains("toBeDeleted"));
QCOMPARE(dbDestination->metadata()->customData()->value("key1"), QString("value1"));
QCOMPARE(dbDestination->metadata()->customData()->value("key2"), QString("value2"));
QCOMPARE(dbDestination->metadata()->customData()->value("Browser"), QString("n'8=3W@L^6d->d.]St_>]"));
QCOMPARE(dbDestination->metadata()->customData()->value("key3"), QString("newValue")); // Old value should be replaced

// Target is newer, no data is merged
QVERIFY(!dbDestination2->metadata()->customData()->isEmpty());
QVERIFY(!dbDestination2->metadata()->customData()->contains("key1"));
QVERIFY(!dbDestination2->metadata()->customData()->contains("key2"));
QVERIFY(!dbDestination2->metadata()->customData()->contains("Browser"));
QVERIFY(dbDestination2->metadata()->customData()->contains("notToBeDeleted"));
QCOMPARE(dbDestination2->metadata()->customData()->value("key3"), QString("oldValue")); // Old value should not be replaced
}

void TestMerge::testDeletedEntry()
{
QScopedPointer<Database> dbDestination(createTestDatabase());
Expand Down
1 change: 1 addition & 0 deletions tests/TestMerge.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private slots:
void testMergeCustomIcons();
void testMergeDuplicateCustomIcons();
void testMetadata();
void testCustomdata();
void testDeletedEntry();
void testDeletedGroup();
void testDeletedRevertedEntry();
Expand Down

0 comments on commit dcb18b1

Please sign in to comment.