1 /*
2 * SPDX-FileCopyrightText: 2015 Mario Bensi <mbensi@ipsquad.net>
3 * SPDX-FileCopyrightText: 2017 Kevin Ottens <ervin@kde.org>
4 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7
8 #include "akonadicachingstorage.h"
9 #include "akonadistorage.h"
10
11 #include "akonadicollectionfetchjobinterface.h"
12 #include "akonadiitemfetchjobinterface.h"
13
14 #include <QTimer>
15
16 using namespace Akonadi;
17
18 class CachingCollectionFetchJob : public KCompositeJob, public CollectionFetchJobInterface
19 {
20 Q_OBJECT
21 public:
CachingCollectionFetchJob(const StorageInterface::Ptr & storage,const Cache::Ptr & cache,const Collection & collection,StorageInterface::FetchDepth depth,QObject * parent=nullptr)22 CachingCollectionFetchJob(const StorageInterface::Ptr &storage,
23 const Cache::Ptr &cache,
24 const Collection &collection,
25 StorageInterface::FetchDepth depth,
26 QObject *parent = nullptr)
27 : KCompositeJob(parent),
28 m_started(false),
29 m_storage(storage),
30 m_cache(cache),
31 m_collection(collection),
32 m_depth(depth)
33 {
34 QTimer::singleShot(0, this, &CachingCollectionFetchJob::start);
35 }
36
start()37 void start() override
38 {
39 if (m_started)
40 return;
41
42 if (m_cache->isCollectionListPopulated()) {
43 QTimer::singleShot(0, this, &CachingCollectionFetchJob::retrieveFromCache);
44 } else {
45 auto job = m_storage->fetchCollections(Akonadi::Collection::root(),
46 Akonadi::StorageInterface::Recursive,
47 this);
48 job->setResource(m_resource);
49 addSubjob(job->kjob());
50 }
51
52 m_started = true;
53 }
54
55
collections() const56 Collection::List collections() const override
57 {
58 const auto isInputCollection = [this] (const Collection &collection) {
59 return collection.id() == m_collection.id()
60 || (!m_collection.remoteId().isEmpty() && collection.remoteId() == m_collection.remoteId());
61 };
62
63 if (m_depth == StorageInterface::Base) {
64 auto it = std::find_if(m_collections.cbegin(), m_collections.cend(), isInputCollection);
65 if (it != m_collections.cend())
66 return Collection::List() << *it;
67 else
68 return Collection::List();
69 }
70
71 auto collections = m_collections;
72 auto it = collections.begin();
73
74 if (m_depth == StorageInterface::FirstLevel) {
75 it = std::remove_if(collections.begin(), collections.end(),
76 [isInputCollection] (const Collection &collection) {
77 return !isInputCollection(collection.parentCollection());
78 });
79 } else {
80 it = std::remove_if(collections.begin(), collections.end(),
81 [isInputCollection] (const Collection &collection) {
82 auto parent = collection.parentCollection();
83 while (parent.isValid() && !isInputCollection(parent))
84 parent = parent.parentCollection();
85 return !isInputCollection(parent);
86 });
87 }
88
89 collections.erase(it, collections.end());
90 return collections;
91 }
92
setResource(const QString & resource)93 void setResource(const QString &resource) override
94 {
95 m_resource = resource;
96 }
97
98 private:
slotResult(KJob * kjob)99 void slotResult(KJob *kjob) override
100 {
101 if (kjob->error()) {
102 KCompositeJob::slotResult(kjob);
103 return;
104 }
105
106 auto job = dynamic_cast<CollectionFetchJobInterface*>(kjob);
107 Q_ASSERT(job);
108 auto cachedCollections = job->collections();
109 for (const auto &collection : job->collections()) {
110 auto parent = collection.parentCollection();
111 while (parent.isValid() && parent != Akonadi::Collection::root()) {
112 if (!cachedCollections.contains(parent)) {
113 cachedCollections.append(parent);
114 }
115 parent = parent.parentCollection();
116 }
117 }
118 m_cache->setCollections(cachedCollections);
119 m_collections = job->collections();
120 emitResult();
121 }
122
retrieveFromCache()123 void retrieveFromCache()
124 {
125 m_collections = m_cache->collections();
126 emitResult();
127 }
128
129 bool m_started;
130 StorageInterface::Ptr m_storage;
131 Cache::Ptr m_cache;
132 QString m_resource;
133 const Collection m_collection;
134 const StorageInterface::FetchDepth m_depth;
135 Collection::List m_collections;
136 };
137
138 class CachingCollectionItemsFetchJob : public KCompositeJob, public ItemFetchJobInterface
139 {
140 Q_OBJECT
141 public:
CachingCollectionItemsFetchJob(const StorageInterface::Ptr & storage,const Cache::Ptr & cache,const Collection & collection,QObject * parent=nullptr)142 CachingCollectionItemsFetchJob(const StorageInterface::Ptr &storage,
143 const Cache::Ptr &cache,
144 const Collection &collection,
145 QObject *parent = nullptr)
146 : KCompositeJob(parent),
147 m_started(false),
148 m_storage(storage),
149 m_cache(cache),
150 m_collection(collection)
151 {
152 QTimer::singleShot(0, this, &CachingCollectionItemsFetchJob::start);
153 }
154
start()155 void start() override
156 {
157 if (m_started)
158 return;
159
160 if (m_cache->isCollectionPopulated(m_collection.id())) {
161 QTimer::singleShot(0, this, &CachingCollectionItemsFetchJob::retrieveFromCache);
162 } else {
163 auto job = m_storage->fetchItems(m_collection, this);
164 addSubjob(job->kjob());
165 }
166
167 m_started = true;
168 }
169
170
items() const171 Item::List items() const override
172 {
173 return m_items;
174 }
175
setCollection(const Collection & collection)176 void setCollection(const Collection &collection) override
177 {
178 m_collection = collection;
179 }
180
181 private:
slotResult(KJob * kjob)182 void slotResult(KJob *kjob) override
183 {
184 if (kjob->error()) {
185 KCompositeJob::slotResult(kjob);
186 return;
187 }
188
189 auto job = dynamic_cast<ItemFetchJobInterface*>(kjob);
190 Q_ASSERT(job);
191 m_items = job->items();
192 m_cache->populateCollection(m_collection, m_items);
193 emitResult();
194 }
195
retrieveFromCache()196 void retrieveFromCache()
197 {
198 m_items = m_cache->items(m_collection);
199 emitResult();
200 }
201
202 bool m_started;
203 StorageInterface::Ptr m_storage;
204 Cache::Ptr m_cache;
205 Collection m_collection;
206 Item::List m_items;
207 };
208
209 class CachingSingleItemFetchJob : public KCompositeJob, public ItemFetchJobInterface
210 {
211 Q_OBJECT
212 public:
CachingSingleItemFetchJob(const StorageInterface::Ptr & storage,const Cache::Ptr & cache,const Item & item,QObject * parent=nullptr)213 CachingSingleItemFetchJob(const StorageInterface::Ptr &storage,
214 const Cache::Ptr &cache,
215 const Item &item,
216 QObject *parent = nullptr)
217 : KCompositeJob(parent),
218 m_started(false),
219 m_storage(storage),
220 m_cache(cache),
221 m_item(item)
222 {
223 QTimer::singleShot(0, this, &CachingSingleItemFetchJob::start);
224 }
225
start()226 void start() override
227 {
228 if (m_started)
229 return;
230
231 const auto item = m_cache->item(m_item.id());
232 if (item.isValid()) {
233 QTimer::singleShot(0, this, [this, item] {
234 retrieveFromCache(item);
235 });
236 } else {
237 auto job = m_storage->fetchItem(m_item, this);
238 job->setCollection(m_collection);
239 addSubjob(job->kjob());
240 }
241
242 m_started = true;
243 }
244
245
items() const246 Item::List items() const override
247 {
248 return m_items;
249 }
250
setCollection(const Collection & collection)251 void setCollection(const Collection &collection) override
252 {
253 m_collection = collection;
254 }
255
256 private:
slotResult(KJob * kjob)257 void slotResult(KJob *kjob) override
258 {
259 if (kjob->error()) {
260 KCompositeJob::slotResult(kjob);
261 return;
262 }
263
264 auto job = dynamic_cast<ItemFetchJobInterface*>(kjob);
265 Q_ASSERT(job);
266 m_items = job->items();
267 emitResult();
268 }
269
retrieveFromCache(const Item & item)270 void retrieveFromCache(const Item &item)
271 {
272 m_items = Item::List() << item;
273 emitResult();
274 }
275
276 bool m_started;
277 StorageInterface::Ptr m_storage;
278 Cache::Ptr m_cache;
279 Item m_item;
280 Collection m_collection;
281 Item::List m_items;
282 };
283
CachingStorage(const Cache::Ptr & cache,const StorageInterface::Ptr & storage)284 CachingStorage::CachingStorage(const Cache::Ptr &cache, const StorageInterface::Ptr &storage)
285 : m_cache(cache),
286 m_storage(storage)
287 {
288 }
289
~CachingStorage()290 CachingStorage::~CachingStorage()
291 {
292 }
293
defaultCollection()294 Collection CachingStorage::defaultCollection()
295 {
296 return m_storage->defaultCollection();
297 }
298
createItem(Item item,Collection collection)299 KJob *CachingStorage::createItem(Item item, Collection collection)
300 {
301 return m_storage->createItem(item, collection);
302 }
303
updateItem(Item item,QObject * parent)304 KJob *CachingStorage::updateItem(Item item, QObject *parent)
305 {
306 return m_storage->updateItem(item, parent);
307 }
308
removeItem(Item item,QObject * parent)309 KJob *CachingStorage::removeItem(Item item, QObject *parent)
310 {
311 return m_storage->removeItem(item, parent);
312 }
313
removeItems(Item::List items,QObject * parent)314 KJob *CachingStorage::removeItems(Item::List items, QObject *parent)
315 {
316 return m_storage->removeItems(items, parent);
317 }
318
moveItem(Item item,Collection collection,QObject * parent)319 KJob *CachingStorage::moveItem(Item item, Collection collection, QObject *parent)
320 {
321 return m_storage->moveItem(item, collection, parent);
322 }
323
moveItems(Item::List items,Collection collection,QObject * parent)324 KJob *CachingStorage::moveItems(Item::List items, Collection collection, QObject *parent)
325 {
326 return m_storage->moveItems(items, collection, parent);
327 }
328
createCollection(Collection collection,QObject * parent)329 KJob *CachingStorage::createCollection(Collection collection, QObject *parent)
330 {
331 return m_storage->createCollection(collection, parent);
332 }
333
updateCollection(Collection collection,QObject * parent)334 KJob *CachingStorage::updateCollection(Collection collection, QObject *parent)
335 {
336 return m_storage->updateCollection(collection, parent);
337 }
338
removeCollection(Collection collection,QObject * parent)339 KJob *CachingStorage::removeCollection(Collection collection, QObject *parent)
340 {
341 return m_storage->removeCollection(collection, parent);
342 }
343
createTransaction(QObject * parent)344 KJob *CachingStorage::createTransaction(QObject *parent)
345 {
346 return m_storage->createTransaction(parent);
347 }
348
fetchCollections(Collection collection,StorageInterface::FetchDepth depth,QObject * parent)349 CollectionFetchJobInterface *CachingStorage::fetchCollections(Collection collection, StorageInterface::FetchDepth depth, QObject *parent)
350 {
351 return new CachingCollectionFetchJob(m_storage, m_cache, collection, depth, parent);
352 }
353
fetchItems(Collection collection,QObject * parent)354 ItemFetchJobInterface *CachingStorage::fetchItems(Collection collection, QObject *parent)
355 {
356 return new CachingCollectionItemsFetchJob(m_storage, m_cache, collection, parent);
357 }
358
fetchItem(Akonadi::Item item,QObject * parent)359 ItemFetchJobInterface *CachingStorage::fetchItem(Akonadi::Item item, QObject *parent)
360 {
361 return new CachingSingleItemFetchJob(m_storage, m_cache, item, parent);
362 }
363
364 #include "akonadicachingstorage.moc"
365