summaryrefslogtreecommitdiffstats
path: root/examples/dummyresource/facade.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dummyresource/facade.cpp')
-rw-r--r--examples/dummyresource/facade.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp
new file mode 100644
index 0000000..e50e4f3
--- /dev/null
+++ b/examples/dummyresource/facade.cpp
@@ -0,0 +1,189 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "facade.h"
21
22#include <QDebug>
23#include <functional>
24
25#include "common/resourceaccess.h"
26#include "common/commands.h"
27#include "dummycalendar_generated.h"
28#include "event_generated.h"
29#include "entity_generated.h"
30#include "metadata_generated.h"
31#include "domainadaptor.h"
32#include <common/entitybuffer.h>
33#include <common/index.h>
34#include <common/log.h>
35
36using namespace DummyCalendar;
37using namespace flatbuffers;
38
39
40DummyResourceFacade::DummyResourceFacade()
41 : Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event>("org.kde.dummy"),
42 mFactory(new DummyEventAdaptorFactory)
43{
44}
45
46DummyResourceFacade::~DummyResourceFacade()
47{
48}
49
50Async::Job<void> DummyResourceFacade::create(const Akonadi2::ApplicationDomain::Event &domainObject)
51{
52 flatbuffers::FlatBufferBuilder entityFbb;
53 mFactory->createBuffer(domainObject, entityFbb);
54 return sendCreateCommand("event", QByteArray::fromRawData(reinterpret_cast<const char*>(entityFbb.GetBufferPointer()), entityFbb.GetSize()));
55}
56
57Async::Job<void> DummyResourceFacade::modify(const Akonadi2::ApplicationDomain::Event &domainObject)
58{
59 //Create message buffer and send to resource
60 return Async::null<void>();
61}
62
63Async::Job<void> DummyResourceFacade::remove(const Akonadi2::ApplicationDomain::Event &domainObject)
64{
65 //Create message buffer and send to resource
66 return Async::null<void>();
67}
68
69static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query)
70{
71 //Compose some functions to make query matching fast.
72 //This way we can process the query once, and convert all values into something that can be compared quickly
73 std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> preparedQuery;
74 if (!query.ids.isEmpty()) {
75 //Match by id
76 //TODO: for id's a direct lookup would be way faster
77
78 //We convert the id's to std::string so we don't have to convert each key during the scan. (This runs only once, and the query will be run for every key)
79 //Probably a premature optimization, but perhaps a useful technique to be investigated.
80 QVector<std::string> ids;
81 for (const auto &id : query.ids) {
82 ids << id.toStdString();
83 }
84 preparedQuery = [ids](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
85 if (ids.contains(key)) {
86 return true;
87 }
88 return false;
89 };
90 } else if (!query.propertyFilter.isEmpty()) {
91 if (query.propertyFilter.contains("uid")) {
92 const QByteArray uid = query.propertyFilter.value("uid").toByteArray();
93 preparedQuery = [uid](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
94 if (local && local->uid() && (QByteArray::fromRawData(local->uid()->c_str(), local->uid()->size()) == uid)) {
95 return true;
96 }
97 return false;
98 };
99 }
100 } else {
101 //Match everything
102 preparedQuery = [](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
103 return true;
104 };
105 }
106 return preparedQuery;
107}
108
109void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> preparedQuery)
110{
111 storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
112
113 //Skip internals
114 if (Akonadi2::Storage::isInternalKey(keyValue, keySize)) {
115 return true;
116 }
117
118 //Extract buffers
119 Akonadi2::EntityBuffer buffer(dataValue, dataSize);
120
121 const auto resourceBuffer = Akonadi2::EntityBuffer::readBuffer<DummyEvent>(buffer.entity().resource());
122 const auto localBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::ApplicationDomain::Buffer::Event>(buffer.entity().local());
123 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(buffer.entity().metadata());
124
125 if ((!resourceBuffer && !localBuffer) || !metadataBuffer) {
126 qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize);
127 return true;
128 }
129
130 //We probably only want to create all buffers after the scan
131 //TODO use adapter for query and scan?
132 if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer, localBuffer)) {
133 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
134 //This only works for a 1:1 mapping of resource to domain types.
135 //Not i.e. for tags that are stored as flags in each entity of an imap store.
136 auto adaptor = mFactory->createAdaptor(buffer.entity());
137 //TODO only copy requested properties
138 auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*adaptor);
139 // here we could copy additional properties that don't have a 1:1 mapping, such as separately stored tags.
140 auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy", QByteArray::fromRawData(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor);
141 resultCallback(event);
142 }
143 return true;
144 },
145 [](const Akonadi2::Storage::Error &error) {
146 qWarning() << "Error during query: " << error.message;
147 });
148}
149
150Async::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback)
151{
152 return Async::start<qint64>([=](Async::Future<qint64> &future) {
153 //Now that the sync is complete we can execute the query
154 const auto preparedQuery = prepareQuery(query);
155
156 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
157 storage->setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) {
158 Warning() << "Error during query: " << error.store << error.message;
159 });
160
161 storage->startTransaction(Akonadi2::Storage::ReadOnly);
162 const qint64 revision = storage->maxRevision();
163
164 //Index lookups
165 QVector<QByteArray> keys;
166 if (query.propertyFilter.contains("uid")) {
167 static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly);
168 uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) {
169 keys << value;
170 },
171 [](const Index::Error &error) {
172 Warning() << "Error in index: " << error.message;
173 });
174 }
175
176 if (keys.isEmpty()) {
177 Log() << "Executing a full scan";
178 readValue(storage, QByteArray(), resultCallback, preparedQuery);
179 } else {
180 for (const auto &key : keys) {
181 readValue(storage, key, resultCallback, preparedQuery);
182 }
183 }
184 storage->abortTransaction();
185 future.setValue(revision);
186 future.setFinished();
187 });
188}
189