diff options
-rw-r--r-- | common/entitybuffer.cpp | 5 | ||||
-rw-r--r-- | common/entitybuffer.h | 1 | ||||
-rw-r--r-- | common/metadata.fbs | 1 | ||||
-rw-r--r-- | common/pipeline.cpp | 44 | ||||
-rw-r--r-- | common/pipeline.h | 37 | ||||
-rw-r--r-- | common/resource.cpp | 5 | ||||
-rw-r--r-- | common/resource.h | 2 | ||||
-rw-r--r-- | dummyresource/facade.cpp | 138 | ||||
-rw-r--r-- | dummyresource/facade.h | 53 | ||||
-rw-r--r-- | dummyresource/resourcefactory.cpp | 56 | ||||
-rw-r--r-- | dummyresource/resourcefactory.h | 1 | ||||
-rw-r--r-- | synchronizer/listener.cpp | 2 |
12 files changed, 253 insertions, 92 deletions
diff --git a/common/entitybuffer.cpp b/common/entitybuffer.cpp index b9c9d76..c5d6bce 100644 --- a/common/entitybuffer.cpp +++ b/common/entitybuffer.cpp | |||
@@ -18,6 +18,11 @@ EntityBuffer::EntityBuffer(void *dataValue, int dataSize) | |||
18 | } | 18 | } |
19 | } | 19 | } |
20 | 20 | ||
21 | const Akonadi2::Entity &EntityBuffer::entity() | ||
22 | { | ||
23 | return *mEntity; | ||
24 | } | ||
25 | |||
21 | const flatbuffers::Vector<uint8_t>* EntityBuffer::resourceBuffer() | 26 | const flatbuffers::Vector<uint8_t>* EntityBuffer::resourceBuffer() |
22 | { | 27 | { |
23 | if (!mEntity) { | 28 | if (!mEntity) { |
diff --git a/common/entitybuffer.h b/common/entitybuffer.h index c072777..bd9360d 100644 --- a/common/entitybuffer.h +++ b/common/entitybuffer.h | |||
@@ -12,6 +12,7 @@ public: | |||
12 | const flatbuffers::Vector<uint8_t> *resourceBuffer(); | 12 | const flatbuffers::Vector<uint8_t> *resourceBuffer(); |
13 | const flatbuffers::Vector<uint8_t> *metadataBuffer(); | 13 | const flatbuffers::Vector<uint8_t> *metadataBuffer(); |
14 | const flatbuffers::Vector<uint8_t> *localBuffer(); | 14 | const flatbuffers::Vector<uint8_t> *localBuffer(); |
15 | const Entity &entity(); | ||
15 | 16 | ||
16 | static void extractResourceBuffer(void *dataValue, int dataSize, const std::function<void(const flatbuffers::Vector<uint8_t> *)> &handler); | 17 | static void extractResourceBuffer(void *dataValue, int dataSize, const std::function<void(const flatbuffers::Vector<uint8_t> *)> &handler); |
17 | static void assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void *metadataData, size_t metadataSize, void *resourceData, size_t resourceSize, void *localData, size_t localSize); | 18 | static void assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void *metadataData, size_t metadataSize, void *resourceData, size_t resourceSize, void *localData, size_t localSize); |
diff --git a/common/metadata.fbs b/common/metadata.fbs index 71684b6..34a8df2 100644 --- a/common/metadata.fbs +++ b/common/metadata.fbs | |||
@@ -2,6 +2,7 @@ namespace Akonadi2; | |||
2 | 2 | ||
3 | table Metadata { | 3 | table Metadata { |
4 | revision: ulong; | 4 | revision: ulong; |
5 | processed: bool = true; | ||
5 | } | 6 | } |
6 | 7 | ||
7 | root_type Metadata; | 8 | root_type Metadata; |
diff --git a/common/pipeline.cpp b/common/pipeline.cpp index 04954ac..8d00480 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp | |||
@@ -41,10 +41,10 @@ public: | |||
41 | } | 41 | } |
42 | 42 | ||
43 | Storage storage; | 43 | Storage storage; |
44 | QVector<Preprocessor *> nullPipeline; | 44 | QHash<QString, QVector<Preprocessor *> > nullPipeline; |
45 | QVector<Preprocessor *> newPipeline; | 45 | QHash<QString, QVector<Preprocessor *> > newPipeline; |
46 | QVector<Preprocessor *> modifiedPipeline; | 46 | QHash<QString, QVector<Preprocessor *> > modifiedPipeline; |
47 | QVector<Preprocessor *> deletedPipeline; | 47 | QHash<QString, QVector<Preprocessor *> > deletedPipeline; |
48 | QVector<PipelineState> activePipelines; | 48 | QVector<PipelineState> activePipelines; |
49 | bool stepScheduled; | 49 | bool stepScheduled; |
50 | }; | 50 | }; |
@@ -60,6 +60,23 @@ Pipeline::~Pipeline() | |||
60 | delete d; | 60 | delete d; |
61 | } | 61 | } |
62 | 62 | ||
63 | void Pipeline::setPreprocessors(const QString &entityType, Type pipelineType, const QVector<Preprocessor *> &preprocessors) | ||
64 | { | ||
65 | switch (pipelineType) { | ||
66 | case NewPipeline: | ||
67 | d->newPipeline[entityType] = preprocessors; | ||
68 | break; | ||
69 | case ModifiedPipeline: | ||
70 | d->modifiedPipeline[entityType] = preprocessors; | ||
71 | break; | ||
72 | case DeletedPipeline: | ||
73 | d->deletedPipeline[entityType] = preprocessors; | ||
74 | break; | ||
75 | default: | ||
76 | break; | ||
77 | }; | ||
78 | } | ||
79 | |||
63 | Storage &Pipeline::storage() const | 80 | Storage &Pipeline::storage() const |
64 | { | 81 | { |
65 | return d->storage; | 82 | return d->storage; |
@@ -68,12 +85,12 @@ Storage &Pipeline::storage() const | |||
68 | void Pipeline::null() | 85 | void Pipeline::null() |
69 | { | 86 | { |
70 | //TODO: is there really any need for the null pipeline? if so, it should be doing something ;) | 87 | //TODO: is there really any need for the null pipeline? if so, it should be doing something ;) |
71 | PipelineState state(this, NullPipeline, QByteArray(), d->nullPipeline); | 88 | // PipelineState state(this, NullPipeline, QByteArray(), d->nullPipeline); |
72 | d->activePipelines << state; | 89 | // d->activePipelines << state; |
73 | state.step(); | 90 | // state.step(); |
74 | } | 91 | } |
75 | 92 | ||
76 | void Pipeline::newEntity(const QByteArray &key, void *resourceBufferData, size_t size) | 93 | void Pipeline::newEntity(const QString &entityType, const QByteArray &key, void *resourceBufferData, size_t size) |
77 | { | 94 | { |
78 | const qint64 newRevision = storage().maxRevision() + 1; | 95 | const qint64 newRevision = storage().maxRevision() + 1; |
79 | 96 | ||
@@ -81,6 +98,7 @@ void Pipeline::newEntity(const QByteArray &key, void *resourceBufferData, size_t | |||
81 | flatbuffers::FlatBufferBuilder metadataFbb; | 98 | flatbuffers::FlatBufferBuilder metadataFbb; |
82 | auto metadataBuilder = Akonadi2::MetadataBuilder(metadataFbb); | 99 | auto metadataBuilder = Akonadi2::MetadataBuilder(metadataFbb); |
83 | metadataBuilder.add_revision(newRevision); | 100 | metadataBuilder.add_revision(newRevision); |
101 | metadataBuilder.add_processed(false); | ||
84 | auto metadataBuffer = metadataBuilder.Finish(); | 102 | auto metadataBuffer = metadataBuilder.Finish(); |
85 | Akonadi2::FinishMetadataBuffer(metadataFbb, metadataBuffer); | 103 | Akonadi2::FinishMetadataBuffer(metadataFbb, metadataBuffer); |
86 | 104 | ||
@@ -90,21 +108,21 @@ void Pipeline::newEntity(const QByteArray &key, void *resourceBufferData, size_t | |||
90 | storage().write(key.data(), key.size(), fbb.GetBufferPointer(), fbb.GetSize()); | 108 | storage().write(key.data(), key.size(), fbb.GetBufferPointer(), fbb.GetSize()); |
91 | storage().setMaxRevision(newRevision); | 109 | storage().setMaxRevision(newRevision); |
92 | 110 | ||
93 | PipelineState state(this, NewPipeline, key, d->newPipeline); | 111 | PipelineState state(this, NewPipeline, key, d->newPipeline[entityType]); |
94 | d->activePipelines << state; | 112 | d->activePipelines << state; |
95 | state.step(); | 113 | state.step(); |
96 | } | 114 | } |
97 | 115 | ||
98 | void Pipeline::modifiedEntity(const QByteArray &key, void *data, size_t size) | 116 | void Pipeline::modifiedEntity(const QString &entityType, const QByteArray &key, void *data, size_t size) |
99 | { | 117 | { |
100 | PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline); | 118 | PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline[entityType]); |
101 | d->activePipelines << state; | 119 | d->activePipelines << state; |
102 | state.step(); | 120 | state.step(); |
103 | } | 121 | } |
104 | 122 | ||
105 | void Pipeline::deletedEntity(const QByteArray &key) | 123 | void Pipeline::deletedEntity(const QString &entityType, const QByteArray &key) |
106 | { | 124 | { |
107 | PipelineState state(this, DeletedPipeline, key, d->deletedPipeline); | 125 | PipelineState state(this, DeletedPipeline, key, d->deletedPipeline[entityType]); |
108 | d->activePipelines << state; | 126 | d->activePipelines << state; |
109 | state.step(); | 127 | state.step(); |
110 | } | 128 | } |
diff --git a/common/pipeline.h b/common/pipeline.h index 6ef8703..8373899 100644 --- a/common/pipeline.h +++ b/common/pipeline.h | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | #include <akonadi2common_export.h> | 28 | #include <akonadi2common_export.h> |
29 | #include <storage.h> | 29 | #include <storage.h> |
30 | #include <clientapi.h> //For domain types | ||
30 | 31 | ||
31 | namespace Akonadi2 | 32 | namespace Akonadi2 |
32 | { | 33 | { |
@@ -46,12 +47,34 @@ public: | |||
46 | 47 | ||
47 | Storage &storage() const; | 48 | Storage &storage() const; |
48 | 49 | ||
50 | // template <typename T> | ||
51 | // Storage &storage() const; | ||
52 | |||
53 | template <typename T> | ||
54 | void setPreprocessors(Type type, const QVector<Preprocessor *> &preprocessors) | ||
55 | { | ||
56 | setPreprocessors(Akonadi2::Domain::getTypeName<T>(), type, preprocessors); | ||
57 | } | ||
58 | |||
49 | void null(); | 59 | void null(); |
50 | //FIXME We should probably directly provide a DomainTypeAdapter here. The data has already been written and we only need to read it for processing. And we need to read all buffers. | 60 | |
51 | void newEntity(const QByteArray &key, void *resourceBufferData, size_t size); | 61 | template <typename T> |
52 | //TODO Send local buffer data as well? | 62 | void newEntity(const QByteArray &key, void *resourceBufferData, size_t size) |
53 | void modifiedEntity(const QByteArray &key, void *data, size_t size); | 63 | { |
54 | void deletedEntity(const QByteArray &key); | 64 | newEntity(Akonadi2::Domain::getTypeName<T>(), key, resourceBufferData, size); |
65 | } | ||
66 | |||
67 | template <typename T> | ||
68 | void modifiedEntity(const QByteArray &key, void *data, size_t size) | ||
69 | { | ||
70 | modifiedEntity(Akonadi2::Domain::getTypeName<T>(), key, data, size); | ||
71 | } | ||
72 | |||
73 | template <typename T> | ||
74 | void deletedEntity(const QByteArray &key) | ||
75 | { | ||
76 | deletedEntity(Akonadi2::Domain::getTypeName<T>(), key); | ||
77 | } | ||
55 | 78 | ||
56 | Q_SIGNALS: | 79 | Q_SIGNALS: |
57 | void revisionUpdated(); | 80 | void revisionUpdated(); |
@@ -61,6 +84,10 @@ private Q_SLOTS: | |||
61 | void stepPipelines(); | 84 | void stepPipelines(); |
62 | 85 | ||
63 | private: | 86 | private: |
87 | void setPreprocessors(const QString &entityType, Type pipelineType, const QVector<Preprocessor *> &preprocessors); | ||
88 | void newEntity(const QString &entityType, const QByteArray &key, void *resourceBufferData, size_t size); | ||
89 | void modifiedEntity(const QString &entityType, const QByteArray &key, void *data, size_t size); | ||
90 | void deletedEntity(const QString &entityType, const QByteArray &key); | ||
64 | void pipelineStepped(const PipelineState &state); | 91 | void pipelineStepped(const PipelineState &state); |
65 | void pipelineCompleted(const PipelineState &state); | 92 | void pipelineCompleted(const PipelineState &state); |
66 | void scheduleStep(); | 93 | void scheduleStep(); |
diff --git a/common/resource.cpp b/common/resource.cpp index bba6609..db08c4f 100644 --- a/common/resource.cpp +++ b/common/resource.cpp | |||
@@ -39,6 +39,11 @@ Resource::~Resource() | |||
39 | //delete d; | 39 | //delete d; |
40 | } | 40 | } |
41 | 41 | ||
42 | void Resource::configurePipeline(Pipeline *pipeline) | ||
43 | { | ||
44 | |||
45 | } | ||
46 | |||
42 | void Resource::processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline) | 47 | void Resource::processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline) |
43 | { | 48 | { |
44 | Q_UNUSED(commandId) | 49 | Q_UNUSED(commandId) |
diff --git a/common/resource.h b/common/resource.h index fb42c1b..52a28a6 100644 --- a/common/resource.h +++ b/common/resource.h | |||
@@ -36,6 +36,8 @@ public: | |||
36 | virtual void processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline); | 36 | virtual void processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline); |
37 | virtual Async::Job<void> synchronizeWithSource(Pipeline *pipeline); | 37 | virtual Async::Job<void> synchronizeWithSource(Pipeline *pipeline); |
38 | 38 | ||
39 | virtual void configurePipeline(Pipeline *pipeline); | ||
40 | |||
39 | private: | 41 | private: |
40 | class Private; | 42 | class Private; |
41 | Private * const d; | 43 | Private * const d; |
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp index d3974e9..c167297 100644 --- a/dummyresource/facade.cpp +++ b/dummyresource/facade.cpp | |||
@@ -33,44 +33,87 @@ | |||
33 | using namespace DummyCalendar; | 33 | using namespace DummyCalendar; |
34 | using namespace flatbuffers; | 34 | using namespace flatbuffers; |
35 | 35 | ||
36 | /** | 36 | //This will become a generic implementation that simply takes the resource buffer and local buffer pointer |
37 | * The property mapper holds accessor functions for all properties. | 37 | class DummyEventAdaptor : public Akonadi2::Domain::BufferAdaptor |
38 | * | ||
39 | * It is by default initialized with accessors that access the local-only buffer, | ||
40 | * and resource simply have to overwrite those accessors. | ||
41 | */ | ||
42 | template<typename BufferType> | ||
43 | class PropertyMapper | ||
44 | { | 38 | { |
45 | public: | 39 | public: |
46 | void setProperty(const QString &key, const QVariant &value, BufferType *buffer) | 40 | DummyEventAdaptor() |
41 | : BufferAdaptor() | ||
47 | { | 42 | { |
48 | if (mWriteAccessors.contains(key)) { | 43 | |
49 | auto accessor = mWriteAccessors.value(key); | 44 | } |
50 | return accessor(value, buffer); | 45 | |
46 | void setProperty(const QString &key, const QVariant &value) | ||
47 | { | ||
48 | if (mResourceMapper->mWriteAccessors.contains(key)) { | ||
49 | // mResourceMapper.setProperty(key, value, mResourceBuffer); | ||
50 | } else { | ||
51 | // mLocalMapper.; | ||
51 | } | 52 | } |
52 | } | 53 | } |
53 | 54 | ||
54 | virtual QVariant getProperty(const QString &key, BufferType const *buffer) const | 55 | virtual QVariant getProperty(const QString &key) const |
55 | { | 56 | { |
56 | if (mReadAccessors.contains(key)) { | 57 | if (mResourceBuffer && mResourceMapper->mReadAccessors.contains(key)) { |
57 | auto accessor = mReadAccessors.value(key); | 58 | return mResourceMapper->getProperty(key, mResourceBuffer); |
58 | return accessor(buffer); | 59 | } else if (mLocalBuffer) { |
60 | return mLocalMapper->getProperty(key, mLocalBuffer); | ||
59 | } | 61 | } |
60 | return QVariant(); | 62 | return QVariant(); |
61 | } | 63 | } |
62 | QHash<QString, std::function<QVariant(BufferType const *)> > mReadAccessors; | 64 | |
63 | QHash<QString, std::function<void(const QVariant &, BufferType*)> > mWriteAccessors; | 65 | Akonadi2::Domain::Buffer::Event const *mLocalBuffer; |
66 | DummyEvent const *mResourceBuffer; | ||
67 | |||
68 | QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> > mLocalMapper; | ||
69 | QSharedPointer<PropertyMapper<DummyEvent> > mResourceMapper; | ||
64 | }; | 70 | }; |
65 | 71 | ||
72 | template<> | ||
73 | QSharedPointer<Akonadi2::Domain::BufferAdaptor> DomainTypeAdaptorFactory<typename Akonadi2::Domain::Event, typename Akonadi2::Domain::Buffer::Event, DummyEvent>::createAdaptor(const Akonadi2::Entity &entity) | ||
74 | { | ||
75 | DummyEvent const *resourceBuffer = 0; | ||
76 | if (auto resourceData = entity.resource()) { | ||
77 | flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size()); | ||
78 | if (VerifyDummyEventBuffer(verifyer)) { | ||
79 | resourceBuffer = GetDummyEvent(resourceData); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | Akonadi2::Metadata const *metadataBuffer = 0; | ||
84 | if (auto metadataData = entity.metadata()) { | ||
85 | flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); | ||
86 | if (Akonadi2::VerifyMetadataBuffer(verifyer)) { | ||
87 | metadataBuffer = Akonadi2::GetMetadata(metadataData); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | Akonadi2::Domain::Buffer::Event const *localBuffer = 0; | ||
92 | if (auto localData = entity.local()) { | ||
93 | flatbuffers::Verifier verifyer(localData->Data(), localData->size()); | ||
94 | if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) { | ||
95 | localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | auto adaptor = QSharedPointer<DummyEventAdaptor>::create(); | ||
100 | adaptor->mLocalBuffer = localBuffer; | ||
101 | adaptor->mResourceBuffer = resourceBuffer; | ||
102 | adaptor->mResourceMapper = mResourceMapper; | ||
103 | adaptor->mLocalMapper = mLocalMapper; | ||
104 | return adaptor; | ||
105 | } | ||
106 | |||
66 | DummyResourceFacade::DummyResourceFacade() | 107 | DummyResourceFacade::DummyResourceFacade() |
67 | : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), | 108 | : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), |
68 | mResourceAccess(new Akonadi2::ResourceAccess("org.kde.dummy")) | 109 | mResourceAccess(new Akonadi2::ResourceAccess("org.kde.dummy")), |
110 | mFactory(new DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent>()) | ||
69 | { | 111 | { |
70 | PropertyMapper<DummyEvent> mapper; | 112 | auto mapper = QSharedPointer<PropertyMapper<DummyEvent> >::create(); |
71 | mapper.mReadAccessors.insert("summary", [](DummyEvent const *buffer) -> QVariant { | 113 | mapper->mReadAccessors.insert("summary", [](DummyEvent const *buffer) -> QVariant { |
72 | return QString::fromStdString(buffer->summary()->c_str()); | 114 | return QString::fromStdString(buffer->summary()->c_str()); |
73 | }); | 115 | }); |
116 | mFactory->mResourceMapper = mapper; | ||
74 | } | 117 | } |
75 | 118 | ||
76 | DummyResourceFacade::~DummyResourceFacade() | 119 | DummyResourceFacade::~DummyResourceFacade() |
@@ -105,45 +148,6 @@ void DummyResourceFacade::remove(const Akonadi2::Domain::Event &domainObject) | |||
105 | // | 148 | // |
106 | 149 | ||
107 | 150 | ||
108 | //This will become a generic implementation that simply takes the resource buffer and local buffer pointer | ||
109 | class DummyEventAdaptor : public Akonadi2::Domain::BufferAdaptor | ||
110 | { | ||
111 | public: | ||
112 | DummyEventAdaptor() | ||
113 | : BufferAdaptor() | ||
114 | { | ||
115 | |||
116 | } | ||
117 | |||
118 | void setProperty(const QString &key, const QVariant &value) | ||
119 | { | ||
120 | if (mResourceMapper.mWriteAccessors.contains(key)) { | ||
121 | // mResourceMapper.setProperty(key, value, mResourceBuffer); | ||
122 | } else { | ||
123 | // mLocalMapper.; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | virtual QVariant getProperty(const QString &key) const | ||
128 | { | ||
129 | if (mResourceBuffer && mResourceMapper.mReadAccessors.contains(key)) { | ||
130 | return mResourceMapper.getProperty(key, mResourceBuffer); | ||
131 | } else if (mLocalBuffer) { | ||
132 | return mLocalMapper.getProperty(key, mLocalBuffer); | ||
133 | } | ||
134 | return QVariant(); | ||
135 | } | ||
136 | |||
137 | Akonadi2::Domain::Buffer::Event const *mLocalBuffer; | ||
138 | DummyEvent const *mResourceBuffer; | ||
139 | |||
140 | PropertyMapper<Akonadi2::Domain::Buffer::Event> mLocalMapper; | ||
141 | PropertyMapper<DummyEvent> mResourceMapper; | ||
142 | |||
143 | //Keep query alive so values remain valid | ||
144 | QSharedPointer<Akonadi2::Storage> storage; | ||
145 | }; | ||
146 | |||
147 | static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) | 151 | static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) |
148 | { | 152 | { |
149 | //Compose some functions to make query matching fast. | 153 | //Compose some functions to make query matching fast. |
@@ -225,26 +229,16 @@ void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function | |||
225 | } | 229 | } |
226 | } | 230 | } |
227 | 231 | ||
228 | Akonadi2::Domain::Buffer::Event const *localBuffer = 0; | ||
229 | if (auto localData = buffer.localBuffer()) { | ||
230 | flatbuffers::Verifier verifyer(localData->Data(), localData->size()); | ||
231 | if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) { | ||
232 | localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | if (!resourceBuffer || !metadataBuffer) { | 232 | if (!resourceBuffer || !metadataBuffer) { |
237 | qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); | 233 | qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); |
238 | return true; | 234 | return true; |
239 | } | 235 | } |
240 | 236 | ||
241 | //We probably only want to create all buffers after the scan | 237 | //We probably only want to create all buffers after the scan |
238 | //TODO use adapter for query and scan? | ||
242 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer)) { | 239 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer)) { |
243 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | 240 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; |
244 | auto adaptor = QSharedPointer<DummyEventAdaptor>::create(); | 241 | auto adaptor = mFactory->createAdaptor(buffer.entity()); |
245 | adaptor->mLocalBuffer = localBuffer; | ||
246 | adaptor->mResourceBuffer = resourceBuffer; | ||
247 | adaptor->storage = storage; | ||
248 | auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, adaptor); | 242 | auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, adaptor); |
249 | resultCallback(event); | 243 | resultCallback(event); |
250 | } | 244 | } |
diff --git a/dummyresource/facade.h b/dummyresource/facade.h index c76e62c..46b27ef 100644 --- a/dummyresource/facade.h +++ b/dummyresource/facade.h | |||
@@ -21,11 +21,63 @@ | |||
21 | 21 | ||
22 | #include "common/clientapi.h" | 22 | #include "common/clientapi.h" |
23 | #include "common/storage.h" | 23 | #include "common/storage.h" |
24 | #include "entity_generated.h" | ||
25 | #include "event_generated.h" | ||
26 | #include "dummycalendar_generated.h" | ||
24 | 27 | ||
25 | namespace Akonadi2 { | 28 | namespace Akonadi2 { |
26 | class ResourceAccess; | 29 | class ResourceAccess; |
27 | } | 30 | } |
28 | 31 | ||
32 | /** | ||
33 | * The property mapper holds accessor functions for all properties. | ||
34 | * | ||
35 | * It is by default initialized with accessors that access the local-only buffer, | ||
36 | * and resource simply have to overwrite those accessors. | ||
37 | */ | ||
38 | template<typename BufferType> | ||
39 | class PropertyMapper | ||
40 | { | ||
41 | public: | ||
42 | void setProperty(const QString &key, const QVariant &value, BufferType *buffer) | ||
43 | { | ||
44 | if (mWriteAccessors.contains(key)) { | ||
45 | auto accessor = mWriteAccessors.value(key); | ||
46 | return accessor(value, buffer); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | virtual QVariant getProperty(const QString &key, BufferType const *buffer) const | ||
51 | { | ||
52 | if (mReadAccessors.contains(key)) { | ||
53 | auto accessor = mReadAccessors.value(key); | ||
54 | return accessor(buffer); | ||
55 | } | ||
56 | return QVariant(); | ||
57 | } | ||
58 | QHash<QString, std::function<QVariant(BufferType const *)> > mReadAccessors; | ||
59 | QHash<QString, std::function<void(const QVariant &, BufferType*)> > mWriteAccessors; | ||
60 | }; | ||
61 | |||
62 | //The factory should define how to go from an entitybuffer (local + resource buffer), to a domain type adapter. | ||
63 | //It defines how values are split accross local and resource buffer. | ||
64 | //This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way. | ||
65 | template<typename DomainType, typename LocalBuffer, typename ResourceBuffer> | ||
66 | class DomainTypeAdaptorFactory | ||
67 | { | ||
68 | }; | ||
69 | |||
70 | template<typename LocalBuffer, typename ResourceBuffer> | ||
71 | class DomainTypeAdaptorFactory<typename Akonadi2::Domain::Event, LocalBuffer, ResourceBuffer> | ||
72 | { | ||
73 | public: | ||
74 | QSharedPointer<Akonadi2::Domain::BufferAdaptor> createAdaptor(const Akonadi2::Entity &entity); | ||
75 | |||
76 | // private: | ||
77 | QSharedPointer<PropertyMapper<LocalBuffer> > mLocalMapper; | ||
78 | QSharedPointer<PropertyMapper<ResourceBuffer> > mResourceMapper; | ||
79 | }; | ||
80 | |||
29 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> | 81 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> |
30 | { | 82 | { |
31 | public: | 83 | public: |
@@ -39,4 +91,5 @@ public: | |||
39 | private: | 91 | private: |
40 | void synchronizeResource(const std::function<void()> &continuation); | 92 | void synchronizeResource(const std::function<void()> &continuation); |
41 | QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; | 93 | QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; |
94 | QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory; | ||
42 | }; | 95 | }; |
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp index c9e4d7a..08222c0 100644 --- a/dummyresource/resourcefactory.cpp +++ b/dummyresource/resourcefactory.cpp | |||
@@ -20,10 +20,48 @@ | |||
20 | #include "resourcefactory.h" | 20 | #include "resourcefactory.h" |
21 | #include "facade.h" | 21 | #include "facade.h" |
22 | #include "entitybuffer.h" | 22 | #include "entitybuffer.h" |
23 | #include "pipeline.h" | ||
23 | #include "dummycalendar_generated.h" | 24 | #include "dummycalendar_generated.h" |
24 | #include "metadata_generated.h" | 25 | #include "metadata_generated.h" |
25 | #include <QUuid> | 26 | #include <QUuid> |
26 | 27 | ||
28 | /* | ||
29 | * Figure out how to implement various classes of processors: | ||
30 | * * read-only (index and such) => domain adapter | ||
31 | * * filter => provide means to move entity elsewhere, and also reflect change in source (I guess?) | ||
32 | * * flag extractors? => like read-only? Or write to local portion of buffer? | ||
33 | * ** $ISSPAM should become part of domain object and is written to the local part of the mail. | ||
34 | * ** => value could be calculated by the server directly | ||
35 | */ | ||
36 | // template <typename DomainType> | ||
37 | class SimpleProcessor : public Akonadi2::Preprocessor | ||
38 | { | ||
39 | public: | ||
40 | SimpleProcessor(const std::function<void(const Akonadi2::PipelineState &state)> &f) | ||
41 | : Akonadi2::Preprocessor(), | ||
42 | mFunction(f) | ||
43 | { | ||
44 | } | ||
45 | |||
46 | void process(const Akonadi2::PipelineState &state) { | ||
47 | mFunction(state); | ||
48 | } | ||
49 | |||
50 | protected: | ||
51 | std::function<void(const Akonadi2::PipelineState &state)> mFunction; | ||
52 | }; | ||
53 | |||
54 | // template <typename DomainType> | ||
55 | // class SimpleReadOnlyProcessor : public SimpleProcessor<DomainType> | ||
56 | // { | ||
57 | // public: | ||
58 | // using SimpleProcessor::SimpleProcessor; | ||
59 | // void process(Akonadi2::PipelineState state) { | ||
60 | // mFunction(); | ||
61 | // } | ||
62 | // }; | ||
63 | |||
64 | |||
27 | static std::string createEvent() | 65 | static std::string createEvent() |
28 | { | 66 | { |
29 | static const size_t attachmentSize = 1024*2; // 2KB | 67 | static const size_t attachmentSize = 1024*2; // 2KB |
@@ -61,6 +99,20 @@ DummyResource::DummyResource() | |||
61 | { | 99 | { |
62 | } | 100 | } |
63 | 101 | ||
102 | void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline) | ||
103 | { | ||
104 | //TODO setup preprocessors for each domain type and pipeline type allowing full customization | ||
105 | //Eventually the order should be self configuring, for now it's hardcoded. | ||
106 | auto eventIndexer = new SimpleProcessor([](const Akonadi2::PipelineState &state) { | ||
107 | //FIXME | ||
108 | // auto adaptor = QSharedPointer<DummyEventAdaptor>::create(); | ||
109 | // adaptor->mLocalBuffer = localBuffer; | ||
110 | // adaptor->mResourceBuffer = resourceBuffer; | ||
111 | // adaptor->storage = storage; | ||
112 | }); | ||
113 | pipeline->setPreprocessors<Akonadi2::Domain::Event>(Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer); | ||
114 | } | ||
115 | |||
64 | void findByRemoteId(QSharedPointer<Akonadi2::Storage> storage, const QString &rid, std::function<void(void *keyValue, int keySize, void *dataValue, int dataSize)> callback) | 116 | void findByRemoteId(QSharedPointer<Akonadi2::Storage> storage, const QString &rid, std::function<void(void *keyValue, int keySize, void *dataValue, int dataSize)> callback) |
65 | { | 117 | { |
66 | //TODO lookup in rid index instead of doing a full scan | 118 | //TODO lookup in rid index instead of doing a full scan |
@@ -119,7 +171,7 @@ Async::Job<void> DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeli | |||
119 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); | 171 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); |
120 | //TODO toRFC4122 would probably be more efficient, but results in non-printable keys. | 172 | //TODO toRFC4122 would probably be more efficient, but results in non-printable keys. |
121 | const auto key = QUuid::createUuid().toString().toUtf8(); | 173 | const auto key = QUuid::createUuid().toString().toUtf8(); |
122 | pipeline->newEntity(key, m_fbb.GetBufferPointer(), m_fbb.GetSize()); | 174 | pipeline->newEntity<Akonadi2::Domain::Event>(key, m_fbb.GetBufferPointer(), m_fbb.GetSize()); |
123 | } else { //modification | 175 | } else { //modification |
124 | //TODO diff and create modification if necessary | 176 | //TODO diff and create modification if necessary |
125 | } | 177 | } |
@@ -139,7 +191,7 @@ void DummyResource::processCommand(int commandId, const QByteArray &data, uint s | |||
139 | builder .add_summary(m_fbb.CreateString("summary summary!")); | 191 | builder .add_summary(m_fbb.CreateString("summary summary!")); |
140 | auto buffer = builder.Finish(); | 192 | auto buffer = builder.Finish(); |
141 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); | 193 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); |
142 | pipeline->newEntity("fakekey", m_fbb.GetBufferPointer(), m_fbb.GetSize()); | 194 | pipeline->newEntity<Akonadi2::Domain::Event>("fakekey", m_fbb.GetBufferPointer(), m_fbb.GetSize()); |
143 | m_fbb.Clear(); | 195 | m_fbb.Clear(); |
144 | } | 196 | } |
145 | 197 | ||
diff --git a/dummyresource/resourcefactory.h b/dummyresource/resourcefactory.h index dba674f..427fcc6 100644 --- a/dummyresource/resourcefactory.h +++ b/dummyresource/resourcefactory.h | |||
@@ -33,6 +33,7 @@ public: | |||
33 | DummyResource(); | 33 | DummyResource(); |
34 | Async::Job<void> synchronizeWithSource(Akonadi2::Pipeline *pipeline); | 34 | Async::Job<void> synchronizeWithSource(Akonadi2::Pipeline *pipeline); |
35 | void processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline); | 35 | void processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline); |
36 | void configurePipeline(Akonadi2::Pipeline *pipeline); | ||
36 | 37 | ||
37 | private: | 38 | private: |
38 | flatbuffers::FlatBufferBuilder m_fbb; | 39 | flatbuffers::FlatBufferBuilder m_fbb; |
diff --git a/synchronizer/listener.cpp b/synchronizer/listener.cpp index 51794ed..8b5a19a 100644 --- a/synchronizer/listener.cpp +++ b/synchronizer/listener.cpp | |||
@@ -37,10 +37,12 @@ Listener::Listener(const QString &resourceName, QObject *parent) | |||
37 | m_server(new QLocalServer(this)), | 37 | m_server(new QLocalServer(this)), |
38 | m_resourceName(resourceName), | 38 | m_resourceName(resourceName), |
39 | m_resource(0), | 39 | m_resource(0), |
40 | //TODO move pipeline(s) to resource | ||
40 | m_pipeline(new Akonadi2::Pipeline(resourceName, parent)), | 41 | m_pipeline(new Akonadi2::Pipeline(resourceName, parent)), |
41 | m_clientBufferProcessesTimer(new QTimer(this)), | 42 | m_clientBufferProcessesTimer(new QTimer(this)), |
42 | m_messageId(0) | 43 | m_messageId(0) |
43 | { | 44 | { |
45 | m_resource->configurePipeline(m_pipeline); | ||
44 | connect(m_pipeline, &Akonadi2::Pipeline::revisionUpdated, | 46 | connect(m_pipeline, &Akonadi2::Pipeline::revisionUpdated, |
45 | this, &Listener::refreshRevision); | 47 | this, &Listener::refreshRevision); |
46 | connect(m_server, &QLocalServer::newConnection, | 48 | connect(m_server, &QLocalServer::newConnection, |