summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/domain/applicationdomaintype.cpp26
-rw-r--r--common/domain/applicationdomaintype.h30
-rw-r--r--common/propertymapper.cpp19
-rw-r--r--examples/maildirresource/maildirresource.cpp46
-rw-r--r--tests/domainadaptortest.cpp47
5 files changed, 138 insertions, 30 deletions
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index 105ae56..1e54622 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -31,6 +31,25 @@ namespace ApplicationDomain {
31 31
32constexpr const char *Mail::ThreadId::name; 32constexpr const char *Mail::ThreadId::name;
33 33
34void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList<QByteArray> &properties, bool copyBlobs)
35{
36 auto propertiesToCopy = properties;
37 if (properties.isEmpty()) {
38 propertiesToCopy = buffer.availableProperties();
39 }
40 for (const auto &property : propertiesToCopy) {
41 const auto value = buffer.getProperty(property);
42 if (copyBlobs && value.canConvert<BLOB>()) {
43 auto oldPath = value.value<BLOB>().value;
44 auto newPath = oldPath + "copy";
45 QFile::copy(oldPath, newPath);
46 memoryAdaptor.setProperty(property, QVariant::fromValue(BLOB{newPath}));
47 } else {
48 memoryAdaptor.setProperty(property, value);
49 }
50 }
51}
52
34ApplicationDomainType::ApplicationDomainType() 53ApplicationDomainType::ApplicationDomainType()
35 :mAdaptor(new MemoryBufferAdaptor()) 54 :mAdaptor(new MemoryBufferAdaptor())
36{ 55{
@@ -85,9 +104,6 @@ bool ApplicationDomainType::hasProperty(const QByteArray &key) const
85QVariant ApplicationDomainType::getProperty(const QByteArray &key) const 104QVariant ApplicationDomainType::getProperty(const QByteArray &key) const
86{ 105{
87 Q_ASSERT(mAdaptor); 106 Q_ASSERT(mAdaptor);
88 if (!mAdaptor->availableProperties().contains(key)) {
89 return QVariant();
90 }
91 return mAdaptor->getProperty(key); 107 return mAdaptor->getProperty(key);
92} 108}
93 109
@@ -111,7 +127,7 @@ void ApplicationDomainType::setProperty(const QByteArray &key, const Application
111 127
112QByteArray ApplicationDomainType::getBlobProperty(const QByteArray &key) const 128QByteArray ApplicationDomainType::getBlobProperty(const QByteArray &key) const
113{ 129{
114 const auto path = getProperty(key).toByteArray(); 130 const auto path = getProperty(key).value<BLOB>().value;
115 QFile file(path); 131 QFile file(path);
116 if (!file.open(QIODevice::ReadOnly)) { 132 if (!file.open(QIODevice::ReadOnly)) {
117 SinkError() << "Failed to open the file: " << file.errorString() << path; 133 SinkError() << "Failed to open the file: " << file.errorString() << path;
@@ -131,7 +147,7 @@ void ApplicationDomainType::setBlobProperty(const QByteArray &key, const QByteAr
131 file.write(value); 147 file.write(value);
132 //Ensure that the file is written to disk immediately 148 //Ensure that the file is written to disk immediately
133 file.close(); 149 file.close();
134 setProperty(key, path); 150 setProperty(key, QVariant::fromValue(BLOB{path}));
135} 151}
136 152
137void ApplicationDomainType::setChangedProperties(const QSet<QByteArray> &changeset) 153void ApplicationDomainType::setChangedProperties(const QSet<QByteArray> &changeset)
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index d6bbdd4..621a512 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -63,12 +63,12 @@
63#define SINK_BLOB_PROPERTY(NAME, LOWERCASENAME) \ 63#define SINK_BLOB_PROPERTY(NAME, LOWERCASENAME) \
64 struct NAME { \ 64 struct NAME { \
65 static constexpr const char *name = #LOWERCASENAME; \ 65 static constexpr const char *name = #LOWERCASENAME; \
66 typedef QString Type; \ 66 typedef BLOB Type; \
67 }; \ 67 }; \
68 void set##NAME(const QByteArray &value) { setBlobProperty(NAME::name, value); } \ 68 void set##NAME(const QByteArray &value) { setBlobProperty(NAME::name, value); } \
69 void set##NAME##Path(const QString &path) { setProperty(NAME::name, QVariant::fromValue(path)); } \ 69 void set##NAME##Path(const QString &path) { setProperty(NAME::name, QVariant::fromValue(BLOB{path})); } \
70 QByteArray get##NAME() const { return getBlobProperty(NAME::name); } \ 70 QByteArray get##NAME() const { return getBlobProperty(NAME::name); } \
71 QString get##NAME##Path() const { return getProperty(NAME::name).value<QString>(); } \ 71 QString get##NAME##Path() const { return getProperty(NAME::name).value<BLOB>().value; } \
72 72
73#define SINK_REFERENCE_PROPERTY(TYPE, NAME, LOWERCASENAME) \ 73#define SINK_REFERENCE_PROPERTY(TYPE, NAME, LOWERCASENAME) \
74 struct NAME { \ 74 struct NAME { \
@@ -98,6 +98,12 @@ struct SINK_EXPORT Progress {
98 98
99}; 99};
100 100
101struct BLOB {
102 QString value;
103};
104
105void copyBuffer(Sink::ApplicationDomain::BufferAdaptor &buffer, Sink::ApplicationDomain::BufferAdaptor &memoryAdaptor, const QList<QByteArray> &properties, bool copyBlobs);
106
101/** 107/**
102 * The domain type interface has two purposes: 108 * The domain type interface has two purposes:
103 * * provide a unified interface to read buffers (for zero-copy reading) 109 * * provide a unified interface to read buffers (for zero-copy reading)
@@ -115,14 +121,29 @@ public:
115 ApplicationDomainType(const ApplicationDomainType &other); 121 ApplicationDomainType(const ApplicationDomainType &other);
116 ApplicationDomainType& operator=(const ApplicationDomainType &other); 122 ApplicationDomainType& operator=(const ApplicationDomainType &other);
117 123
124 /**
125 * Returns an in memory representation of the same entity.
126 */
118 template <typename DomainType> 127 template <typename DomainType>
119 static typename DomainType::Ptr getInMemoryRepresentation(const ApplicationDomainType &domainType, const QList<QByteArray> properties = QList<QByteArray>()) 128 static typename DomainType::Ptr getInMemoryRepresentation(const ApplicationDomainType &domainType, const QList<QByteArray> properties = QList<QByteArray>())
120 { 129 {
121 auto memoryAdaptor = QSharedPointer<Sink::ApplicationDomain::MemoryBufferAdaptor>::create(*(domainType.mAdaptor), properties); 130 auto memoryAdaptor = QSharedPointer<Sink::ApplicationDomain::MemoryBufferAdaptor>::create(*(domainType.mAdaptor), properties);
122 //The identifier still internal refers to the memory-mapped pointer, we need to copy the memory or it will become invalid 131 //mIdentifier internally still refers to the memory-mapped memory, we need to copy the memory or it will become invalid
123 return QSharedPointer<DomainType>::create(domainType.mResourceInstanceIdentifier, QByteArray(domainType.mIdentifier.constData(), domainType.mIdentifier.size()), domainType.mRevision, memoryAdaptor); 132 return QSharedPointer<DomainType>::create(domainType.mResourceInstanceIdentifier, QByteArray(domainType.mIdentifier.constData(), domainType.mIdentifier.size()), domainType.mRevision, memoryAdaptor);
124 } 133 }
125 134
135 /**
136 * Returns an in memory copy without id and resource set.
137 */
138 template <typename DomainType>
139 static typename DomainType::Ptr getInMemoryCopy(const ApplicationDomainType &domainType, const QList<QByteArray> properties = QList<QByteArray>())
140 {
141 auto memoryAdaptor = QSharedPointer<Sink::ApplicationDomain::MemoryBufferAdaptor>::create();
142 Q_ASSERT(domainType.mAdaptor);
143 copyBuffer(*(domainType.mAdaptor), *memoryAdaptor, properties, true);
144 return QSharedPointer<DomainType>::create(QByteArray{}, QByteArray{}, 0, memoryAdaptor);
145 }
146
126 static QByteArray generateUid(); 147 static QByteArray generateUid();
127 148
128 template <class DomainType> 149 template <class DomainType>
@@ -430,3 +451,4 @@ Q_DECLARE_METATYPE(Sink::ApplicationDomain::Identity::Ptr)
430Q_DECLARE_METATYPE(Sink::ApplicationDomain::Mail::Contact) 451Q_DECLARE_METATYPE(Sink::ApplicationDomain::Mail::Contact)
431Q_DECLARE_METATYPE(Sink::ApplicationDomain::Error) 452Q_DECLARE_METATYPE(Sink::ApplicationDomain::Error)
432Q_DECLARE_METATYPE(Sink::ApplicationDomain::Progress) 453Q_DECLARE_METATYPE(Sink::ApplicationDomain::Progress)
454Q_DECLARE_METATYPE(Sink::ApplicationDomain::BLOB)
diff --git a/common/propertymapper.cpp b/common/propertymapper.cpp
index 754c874..249221a 100644
--- a/common/propertymapper.cpp
+++ b/common/propertymapper.cpp
@@ -33,6 +33,15 @@ flatbuffers::uoffset_t variantToProperty<QString>(const QVariant &property, flat
33} 33}
34 34
35template <> 35template <>
36flatbuffers::uoffset_t variantToProperty<Sink::ApplicationDomain::BLOB>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb)
37{
38 if (property.isValid()) {
39 return fbb.CreateString(property.value<Sink::ApplicationDomain::BLOB>().value.toStdString()).o;
40 }
41 return 0;
42}
43
44template <>
36flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) 45flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb)
37{ 46{
38 if (property.isValid()) { 47 if (property.isValid()) {
@@ -111,6 +120,16 @@ QVariant propertyToVariant<QString>(const flatbuffers::String *property)
111} 120}
112 121
113template <> 122template <>
123QVariant propertyToVariant<Sink::ApplicationDomain::BLOB>(const flatbuffers::String *property)
124{
125 if (property) {
126 // We have to copy the memory, otherwise it would become eventually invalid
127 return QVariant::fromValue(Sink::ApplicationDomain::BLOB{QString::fromStdString(property->c_str())});
128 }
129 return QVariant();
130}
131
132template <>
114QVariant propertyToVariant<QByteArray>(const flatbuffers::String *property) 133QVariant propertyToVariant<QByteArray>(const flatbuffers::String *property)
115{ 134{
116 if (property) { 135 if (property) {
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 708dabc..1eee786 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -129,41 +129,44 @@ public:
129 129
130 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE 130 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
131 { 131 {
132 const auto mimeMessage = newEntity.getProperty("mimeMessage"); 132 const ApplicationDomain::Mail mail{newEntity};
133 if (mimeMessage.isValid()) { 133 const auto mimeMessage = mail.getMimeMessagePath();
134 newEntity.setProperty("mimeMessage", moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray())); 134 if (!mimeMessage.isNull()) {
135 ApplicationDomain::Mail{newEntity}.setMimeMessagePath(moveMessage(mimeMessage, mail.getFolder()));
135 } 136 }
136 } 137 }
137 138
138 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE 139 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
139 { 140 {
140 const auto mimeMessage = newEntity.getProperty("mimeMessage"); 141 ApplicationDomain::Mail newMail{newEntity};
141 const auto newFolder = newEntity.getProperty("folder"); 142 const ApplicationDomain::Mail oldMail{oldEntity};
142 const bool mimeMessageChanged = mimeMessage.isValid() && mimeMessage.toString() != oldEntity.getProperty("mimeMessage").toString(); 143 const auto mimeMessage = newMail.getMimeMessagePath();
143 const bool folderChanged = newFolder.isValid() && newFolder.toString() != oldEntity.getProperty("mimeMessage").toString(); 144 const auto newFolder = newMail.getFolder();
145 const bool mimeMessageChanged = !mimeMessage.isNull() && mimeMessage != oldMail.getMimeMessagePath();
146 const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder();
144 if (mimeMessageChanged || folderChanged) { 147 if (mimeMessageChanged || folderChanged) {
145 SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged; 148 SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged;
146 auto newPath = moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray()); 149 auto newPath = moveMessage(mimeMessage, newMail.getFolder());
147 if (newPath != oldEntity.getProperty("mimeMessage").toString()) { 150 if (newPath != oldMail.getMimeMessagePath()) {
148 const auto oldPath = getFilePathFromMimeMessagePath(oldEntity.getProperty("mimeMessage").toString()); 151 const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
149 newEntity.setProperty("mimeMessage", newPath); 152 newMail.setMimeMessagePath(newPath);
150 //Remove the olde mime message if there is a new one 153 //Remove the olde mime message if there is a new one
151 QFile::remove(oldPath); 154 QFile::remove(oldPath);
152 } 155 }
153 } 156 }
154 157
155 auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString(); 158 auto mimeMessagePath = newMail.getMimeMessagePath();
156 const auto maildirPath = getPath(newEntity.getProperty("folder").toByteArray()); 159 const auto maildirPath = getPath(newMail.getFolder());
157 KPIM::Maildir maildir(maildirPath, false); 160 KPIM::Maildir maildir(maildirPath, false);
158 const auto file = getFilePathFromMimeMessagePath(mimeMessagePath); 161 const auto file = getFilePathFromMimeMessagePath(mimeMessagePath);
159 QString identifier = KPIM::Maildir::getKeyFromFile(file); 162 QString identifier = KPIM::Maildir::getKeyFromFile(file);
160 163
161 //get flags from 164 //get flags from
162 KPIM::Maildir::Flags flags; 165 KPIM::Maildir::Flags flags;
163 if (!newEntity.getProperty("unread").toBool()) { 166 if (!newMail.getUnread()) {
164 flags |= KPIM::Maildir::Seen; 167 flags |= KPIM::Maildir::Seen;
165 } 168 }
166 if (newEntity.getProperty("important").toBool()) { 169 if (newMail.getImportant()) {
167 flags |= KPIM::Maildir::Flagged; 170 flags |= KPIM::Maildir::Flagged;
168 } 171 }
169 172
@@ -172,7 +175,8 @@ public:
172 175
173 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE 176 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
174 { 177 {
175 const auto filePath = getFilePathFromMimeMessagePath(oldEntity.getProperty("mimeMessage").toString()); 178 const ApplicationDomain::Mail oldMail{oldEntity};
179 const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
176 QFile::remove(filePath); 180 QFile::remove(filePath);
177 } 181 }
178 QByteArray mResourceInstanceIdentifier; 182 QByteArray mResourceInstanceIdentifier;
@@ -239,7 +243,7 @@ public:
239 } 243 }
240 244
241 if (!md.isRoot()) { 245 if (!md.isRoot()) {
242 folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path().toUtf8())); 246 folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path().toUtf8()));
243 } 247 }
244 createOrModify(bufferType, remoteId, folder); 248 createOrModify(bufferType, remoteId, folder);
245 return remoteId; 249 return remoteId;
@@ -317,11 +321,11 @@ public:
317 SinkTrace() << "Found a mail " << filePath << " : " << fileName; 321 SinkTrace() << "Found a mail " << filePath << " : " << fileName;
318 322
319 Sink::ApplicationDomain::Mail mail; 323 Sink::ApplicationDomain::Mail mail;
320 mail.setProperty("folder", folderLocalId); 324 mail.setFolder(folderLocalId);
321 //We only store the directory path + key, so we facade can add the changing bits (flags) 325 //We only store the directory path + key, so we facade can add the changing bits (flags)
322 mail.setProperty("mimeMessage", KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey); 326 mail.setMimeMessagePath(KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey);
323 mail.setProperty("unread", !flags.testFlag(KPIM::Maildir::Seen)); 327 mail.setUnread(!flags.testFlag(KPIM::Maildir::Seen));
324 mail.setProperty("important", flags.testFlag(KPIM::Maildir::Flagged)); 328 mail.setImportant(flags.testFlag(KPIM::Maildir::Flagged));
325 329
326 createOrModify(bufferType, remoteId, mail); 330 createOrModify(bufferType, remoteId, mail);
327 } 331 }
diff --git a/tests/domainadaptortest.cpp b/tests/domainadaptortest.cpp
index c29e61b..4fd04db 100644
--- a/tests/domainadaptortest.cpp
+++ b/tests/domainadaptortest.cpp
@@ -10,6 +10,7 @@
10#include "common/domainadaptor.h" 10#include "common/domainadaptor.h"
11#include "common/entitybuffer.h" 11#include "common/entitybuffer.h"
12#include "event_generated.h" 12#include "event_generated.h"
13#include "mail_generated.h"
13#include "metadata_generated.h" 14#include "metadata_generated.h"
14#include "entity_generated.h" 15#include "entity_generated.h"
15 16
@@ -23,6 +24,16 @@ public:
23 } 24 }
24}; 25};
25 26
27class TestMailFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Mail, Sink::ApplicationDomain::Buffer::MailBuilder>
28{
29public:
30 TestMailFactory()
31 {
32 mResourceWriteMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::MailBuilder>>::create();
33 Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Mail>::configure(*mResourceWriteMapper);
34 }
35};
36
26/** 37/**
27 * Test of domain adaptor, that it can read and write buffers. 38 * Test of domain adaptor, that it can read and write buffers.
28 */ 39 */
@@ -90,6 +101,42 @@ private slots:
90 QCOMPARE(adaptor->getProperty("summary").toString(), QString("summary1")); 101 QCOMPARE(adaptor->getProperty("summary").toString(), QString("summary1"));
91 } 102 }
92 } 103 }
104
105 void testMail()
106 {
107 auto writeMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::MailBuilder>>::create();
108 Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Mail>::configure(*writeMapper);
109
110 Sink::ApplicationDomain::Mail mail;
111 mail.setExtractedSubject("summary");
112 mail.setMimeMessage("foobar");
113
114 flatbuffers::FlatBufferBuilder metadataFbb;
115 auto metadataBuilder = Sink::MetadataBuilder(metadataFbb);
116 metadataBuilder.add_revision(1);
117 auto metadataBuffer = metadataBuilder.Finish();
118 Sink::FinishMetadataBuffer(metadataFbb, metadataBuffer);
119
120 flatbuffers::FlatBufferBuilder mailFbb;
121 auto pos = createBufferPart<Sink::ApplicationDomain::Buffer::MailBuilder, Sink::ApplicationDomain::Buffer::Mail>(mail, mailFbb, *writeMapper);
122 Sink::ApplicationDomain::Buffer::FinishMailBuffer(mailFbb, pos);
123
124 flatbuffers::FlatBufferBuilder fbb;
125 Sink::EntityBuffer::assembleEntityBuffer(
126 fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), mailFbb.GetBufferPointer(), mailFbb.GetSize(), mailFbb.GetBufferPointer(), mailFbb.GetSize());
127
128 {
129 std::string data(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize());
130 Sink::EntityBuffer buffer((void *)(data.data()), data.size());
131
132 TestMailFactory factory;
133 auto adaptor = factory.createAdaptor(buffer.entity());
134 Sink::ApplicationDomain::Mail readMail{QByteArray{}, QByteArray{}, 0, adaptor};
135 QCOMPARE(readMail.getSubject(), mail.getSubject());
136 QCOMPARE(readMail.getMimeMessage(), mail.getMimeMessage());
137 }
138
139 }
93}; 140};
94 141
95QTEST_MAIN(DomainAdaptorTest) 142QTEST_MAIN(DomainAdaptorTest)