From ae4b64b198a143240aa5dd1e202e5016abfdae71 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 8 Dec 2016 13:18:19 +0100 Subject: Wrap references in a Reerence type. This allows us to make sure that references are not taken out of context (the resource). Because we need to use the type-specific accessors more we also ran into a problem that we cannot "downcast" a reference with the change recording still working, for that we have the cast() operator now. --- common/domain/applicationdomaintype.cpp | 35 ++++++++++++++----- common/domain/applicationdomaintype.h | 61 +++++++++++++++++++++++++++++---- common/propertymapper.cpp | 19 ++++++++++ common/query.h | 11 +++--- common/queryrunner.cpp | 6 ++-- common/specialpurposepreprocessor.cpp | 20 +++++++---- common/storage/entitystore.cpp | 3 +- common/typeindex.cpp | 6 ++++ 8 files changed, 130 insertions(+), 31 deletions(-) (limited to 'common') diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp index a655871..f00f3ed 100644 --- a/common/domain/applicationdomaintype.cpp +++ b/common/domain/applicationdomaintype.cpp @@ -31,7 +31,16 @@ namespace ApplicationDomain { constexpr const char *Mail::ThreadId::name; -void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList &properties, bool copyBlobs) +int foo = [] { + QMetaType::registerEqualsComparator(); + QMetaType::registerDebugStreamOperator(); + QMetaType::registerConverter(); + QMetaType::registerDebugStreamOperator(); + QMetaType::registerDebugStreamOperator(); + return 0; +}(); + +void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList &properties, bool copyBlobs, bool pruneReferences) { auto propertiesToCopy = properties; if (properties.isEmpty()) { @@ -44,6 +53,8 @@ void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::Applicatio auto newPath = oldPath + "copy"; QFile::copy(oldPath, newPath); memoryAdaptor.setProperty(property, QVariant::fromValue(BLOB{newPath})); + } else if (pruneReferences && value.canConvert()) { + continue; } else { memoryAdaptor.setProperty(property, value); } @@ -51,14 +62,16 @@ void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::Applicatio } ApplicationDomainType::ApplicationDomainType() - :mAdaptor(new MemoryBufferAdaptor()) + :mAdaptor(new MemoryBufferAdaptor()), + mChangeSet(new QSet()) { } ApplicationDomainType::ApplicationDomainType(const QByteArray &resourceInstanceIdentifier) :mAdaptor(new MemoryBufferAdaptor()), - mResourceInstanceIdentifier(resourceInstanceIdentifier) + mResourceInstanceIdentifier(resourceInstanceIdentifier), + mChangeSet(new QSet()) { } @@ -67,11 +80,13 @@ ApplicationDomainType::ApplicationDomainType(const QByteArray &resourceInstanceI : mAdaptor(adaptor), mResourceInstanceIdentifier(resourceInstanceIdentifier), mIdentifier(identifier), - mRevision(revision) + mRevision(revision), + mChangeSet(new QSet()) { } ApplicationDomainType::ApplicationDomainType(const ApplicationDomainType &other) + : mChangeSet(new QSet()) { *this = other; } @@ -79,7 +94,9 @@ ApplicationDomainType::ApplicationDomainType(const ApplicationDomainType &other) ApplicationDomainType& ApplicationDomainType::operator=(const ApplicationDomainType &other) { mAdaptor = other.mAdaptor; - mChangeSet = other.mChangeSet; + if (other.mChangeSet) { + *mChangeSet = *other.mChangeSet; + } mResourceInstanceIdentifier = other.mResourceInstanceIdentifier; mIdentifier = other.mIdentifier; mRevision = other.mRevision; @@ -110,7 +127,7 @@ QVariant ApplicationDomainType::getProperty(const QByteArray &key) const void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value) { Q_ASSERT(mAdaptor); - mChangeSet.insert(key); + mChangeSet->insert(key); mAdaptor->setProperty(key, value); } @@ -122,7 +139,7 @@ void ApplicationDomainType::setResource(const QByteArray &identifier) void ApplicationDomainType::setProperty(const QByteArray &key, const ApplicationDomainType &value) { Q_ASSERT(!value.identifier().isEmpty()); - setProperty(key, value.identifier()); + setProperty(key, QVariant::fromValue(Reference{value.identifier()})); } QByteArray ApplicationDomainType::getBlobProperty(const QByteArray &key) const @@ -152,12 +169,12 @@ void ApplicationDomainType::setBlobProperty(const QByteArray &key, const QByteAr void ApplicationDomainType::setChangedProperties(const QSet &changeset) { - mChangeSet = changeset; + *mChangeSet = changeset; } QByteArrayList ApplicationDomainType::changedProperties() const { - return mChangeSet.toList(); + return mChangeSet->toList(); } QByteArrayList ApplicationDomainType::availableProperties() const diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 21e42cf..1c0f208 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h @@ -73,12 +73,12 @@ #define SINK_REFERENCE_PROPERTY(TYPE, NAME, LOWERCASENAME) \ struct NAME { \ static constexpr const char *name = #LOWERCASENAME; \ - typedef QByteArray Type; \ + typedef Reference Type; \ typedef ApplicationDomain::TYPE ReferenceType; \ }; \ void set##NAME(const ApplicationDomain::TYPE &value) { setProperty(NAME::name, value); } \ - void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ - QByteArray get##NAME() const { return getProperty(NAME::name).value(); } \ + void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(Reference{value})); } \ + QByteArray get##NAME() const { return getProperty(NAME::name).value().value; } \ #define SINK_INDEX_PROPERTY(TYPE, NAME, LOWERCASENAME) \ struct NAME { \ @@ -102,7 +102,27 @@ struct BLOB { QString value; }; -void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList &properties, bool copyBlobs); +/** + * Internal type. + * + * Represents a reference to another entity in the same resource. + */ +struct Reference { + Reference() = default; + Reference(const Reference &) = default; + Reference(const QByteArray &id) : value(id) {}; + Reference(const char *id) : value(id) {}; + ~Reference() = default; + bool operator==(const Reference &other) const { + return value == other.value; + } + operator QByteArray() const { + return value; + } + QByteArray value; +}; + +void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList &properties, bool copyBlobs, bool pruneReferences); /** * The domain type interface has two purposes: @@ -121,6 +141,14 @@ public: ApplicationDomainType(const ApplicationDomainType &other); ApplicationDomainType& operator=(const ApplicationDomainType &other); + template + DomainType cast() { + static_assert(std::is_base_of::value, "You can only cast to base classes of ApplicationDomainType."); + DomainType t = *this; + t.mChangeSet = mChangeSet; + return t; + } + /** * Returns an in memory representation of the same entity. */ @@ -140,7 +168,7 @@ public: { auto memoryAdaptor = QSharedPointer::create(); Q_ASSERT(domainType.mAdaptor); - copyBuffer(*(domainType.mAdaptor), *memoryAdaptor, properties, true); + copyBuffer(*(domainType.mAdaptor), *memoryAdaptor, properties, true, true); return QSharedPointer::create(QByteArray{}, QByteArray{}, 0, memoryAdaptor); } @@ -195,7 +223,7 @@ public: private: friend QDebug operator<<(QDebug, const ApplicationDomainType &); QSharedPointer mAdaptor; - QSet mChangeSet; + QSharedPointer> mChangeSet; /* * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. */ @@ -223,6 +251,19 @@ inline QDebug operator<< (QDebug d, const ApplicationDomainType &type) return d; } +inline QDebug operator<< (QDebug d, const Reference &ref) +{ + d << ref.value; + return d; +} + +inline QDebug operator<< (QDebug d, const BLOB &blob) +{ + d << blob.value; + return d; +} + + struct SINK_EXPORT SinkAccount : public ApplicationDomainType { typedef QSharedPointer Ptr; explicit SinkAccount(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier, qint64 revision, const QSharedPointer &adaptor); @@ -318,6 +359,13 @@ struct SINK_EXPORT Mail : public Entity { SINK_INDEX_PROPERTY(QByteArray, ThreadId, threadId); }; +inline QDebug operator<< (QDebug d, const Mail::Contact &c) +{ + d << "Contact(" << c.name << ", " << c.emailAddress << ")"; + return d; +} + + /** * The status of an account or resource. * @@ -454,3 +502,4 @@ Q_DECLARE_METATYPE(Sink::ApplicationDomain::Mail::Contact) Q_DECLARE_METATYPE(Sink::ApplicationDomain::Error) Q_DECLARE_METATYPE(Sink::ApplicationDomain::Progress) Q_DECLARE_METATYPE(Sink::ApplicationDomain::BLOB) +Q_DECLARE_METATYPE(Sink::ApplicationDomain::Reference) diff --git a/common/propertymapper.cpp b/common/propertymapper.cpp index 249221a..4cfe154 100644 --- a/common/propertymapper.cpp +++ b/common/propertymapper.cpp @@ -41,6 +41,15 @@ flatbuffers::uoffset_t variantToProperty(const QV return 0; } +template <> +flatbuffers::uoffset_t variantToProperty(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) +{ + if (property.isValid()) { + return fbb.CreateString(property.value().value.toStdString()).o; + } + return 0; +} + template <> flatbuffers::uoffset_t variantToProperty(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) { @@ -129,6 +138,16 @@ QVariant propertyToVariant(const flatbuffers::Str return QVariant(); } +template <> +QVariant propertyToVariant(const flatbuffers::String *property) +{ + if (property) { + // We have to copy the memory, otherwise it would become eventually invalid + return QVariant::fromValue(Sink::ApplicationDomain::Reference{QString::fromStdString(property->c_str()).toUtf8()}); + } + return QVariant(); +} + template <> QVariant propertyToVariant(const flatbuffers::String *property) { diff --git a/common/query.h b/common/query.h index b69639b..c265f92 100644 --- a/common/query.h +++ b/common/query.h @@ -325,16 +325,17 @@ public: } template - Query &filter(const QVariant &value) + Query &filter(const typename T::Type &value) { - filter(T::name, value); + filter(T::name, QVariant::fromValue(value)); return *this; } template - Query &containsFilter(const QVariant &value) + Query &containsFilter(const QByteArray &value) { - QueryBase::filter(T::name, QueryBase::Comparator(value, QueryBase::Comparator::Contains)); + static_assert(std::is_same::value, "The contains filter is only implemented for QByteArray in QByteArrayList"); + QueryBase::filter(T::name, QueryBase::Comparator(QVariant::fromValue(value), QueryBase::Comparator::Contains)); return *this; } @@ -366,7 +367,7 @@ public: template Query &filter(const ApplicationDomain::Entity &value) { - filter(T::name, QVariant::fromValue(value.identifier())); + filter(T::name, QVariant::fromValue(ApplicationDomain::Reference{value.identifier()})); return *this; } diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp index cf56268..d6a90de 100644 --- a/common/queryrunner.cpp +++ b/common/queryrunner.cpp @@ -224,15 +224,15 @@ QPair QueryWorker::executeInitialQuery( if (!query.parentProperty().isEmpty()) { if (parent) { SinkTrace() << "Running initial query for parent:" << parent->identifier(); - modifiedQuery.filter(query.parentProperty(), Query::Comparator(parent->identifier())); + modifiedQuery.filter(query.parentProperty(), Query::Comparator(QVariant::fromValue(Sink::ApplicationDomain::Reference{parent->identifier()}))); } else { SinkTrace() << "Running initial query for toplevel"; - modifiedQuery.filter(query.parentProperty(), Query::Comparator(QVariant())); + modifiedQuery.filter(query.parentProperty(), Query::Comparator(QVariant{})); } } auto entityStore = EntityStore{mResourceContext}; - auto preparedQuery = DataStoreQuery{query, ApplicationDomain::getTypeName(), entityStore}; + auto preparedQuery = DataStoreQuery{modifiedQuery, ApplicationDomain::getTypeName(), entityStore}; auto resultSet = preparedQuery.execute(); SinkTrace() << "Filtered set retrieved. " << Log::TraceTime(time.elapsed()); diff --git a/common/specialpurposepreprocessor.cpp b/common/specialpurposepreprocessor.cpp index ce1a218..e73e4ce 100644 --- a/common/specialpurposepreprocessor.cpp +++ b/common/specialpurposepreprocessor.cpp @@ -59,7 +59,7 @@ QByteArray SpecialPurposeProcessor::ensureFolder(const QByteArray &specialPurpos }); if (!mSpecialPurposeFolders.contains(specialPurpose)) { - SinkTrace() << "Failed to find a drafts folder, creating a new one"; + SinkTrace() << "Failed to find a " << specialPurpose << " folder, creating a new one"; auto folder = ApplicationDomain::Folder::create(mResourceInstanceIdentifier); folder.setSpecialPurpose(QByteArrayList() << specialPurpose); folder.setName(sSpecialPurposeFolders.value(specialPurpose)); @@ -74,15 +74,21 @@ QByteArray SpecialPurposeProcessor::ensureFolder(const QByteArray &specialPurpos void SpecialPurposeProcessor::moveToFolder(Sink::ApplicationDomain::ApplicationDomainType &newEntity) { - if (newEntity.getProperty(ApplicationDomain::Mail::Trash::name).toBool()) { - newEntity.setProperty("folder", ensureFolder(ApplicationDomain::SpecialPurpose::Mail::trash)); + using namespace Sink::ApplicationDomain; + auto mail = newEntity.cast(); + if (mail.getTrash()) { + auto f = ensureFolder(ApplicationDomain::SpecialPurpose::Mail::trash); + SinkTrace() << "Setting trash folder: " << f; + mail.setFolder(f); return; } - if (newEntity.getProperty(ApplicationDomain::Mail::Draft::name).toBool()) { - newEntity.setProperty("folder", ensureFolder(ApplicationDomain::SpecialPurpose::Mail::drafts)); + if (mail.getDraft()) { + mail.setFolder(ensureFolder(ApplicationDomain::SpecialPurpose::Mail::drafts)); + return; } - if (newEntity.getProperty(ApplicationDomain::Mail::Sent::name).toBool()) { - newEntity.setProperty("folder", ensureFolder(ApplicationDomain::SpecialPurpose::Mail::sent)); + if (mail.getSent()) { + mail.setFolder(ensureFolder(ApplicationDomain::SpecialPurpose::Mail::sent)); + return; } } diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 7414f49..999bb2c 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp @@ -205,9 +205,10 @@ bool EntityStore::modify(const QByteArray &type, const ApplicationDomain::Applic auto metadataBuffer = metadataBuilder.Finish(); FinishMetadataBuffer(metadataFbb, metadataBuffer); } + SinkTrace() << "Modified entity: " << newEntity; + SinkTrace() << "Changed properties: " << changeset + newEntity.changedProperties(); newEntity.setChangedProperties(newEntity.availableProperties().toSet()); - SinkTrace() << "Modified entity " << newEntity; flatbuffers::FlatBufferBuilder fbb; d->resourceContext.adaptorFactory(type).createBuffer(newEntity, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); diff --git a/common/typeindex.cpp b/common/typeindex.cpp index b0494f3..9d71463 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp @@ -34,6 +34,12 @@ static QByteArray getByteArray(const QVariant &value) return "nodate"; } } + if (value.canConvert()) { + const auto ba = value.value().value; + if (!ba.isEmpty()) { + return ba; + } + } if (value.isValid() && !value.toByteArray().isEmpty()) { return value.toByteArray(); } -- cgit v1.2.3