• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
entitycache_p.h
1/*
2 Copyright (c) 2009 Volker Krause <vkrause@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#ifndef AKONADI_ENTITYCACHE_P_H
21#define AKONADI_ENTITYCACHE_P_H
22
23#include <akonadi/item.h>
24#include <akonadi/itemfetchjob.h>
25#include <akonadi/itemfetchscope.h>
26#include <akonadi/collection.h>
27#include <akonadi/collectionfetchjob.h>
28#include <akonadi/collectionfetchscope.h>
29#include <akonadi/tag.h>
30#include <akonadi/tagfetchjob.h>
31#include <akonadi/tagfetchscope.h>
32#include <akonadi/session.h>
33
34#include "akonadiprivate_export.h"
35
36#include <kdebug.h>
37#include <qobject.h>
38#include <QQueue>
39#include <QVariant>
40#include <QHash>
41#include <QtCore/QQueue>
42
43class Dummy;
44class KJob;
45
46typedef QList<Akonadi::Entity::Id> EntityIdList;
47Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
48
49namespace Akonadi {
50
55class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
56{
57 Q_OBJECT
58public:
59 explicit EntityCacheBase(Session *session, QObject *parent = 0);
60
61 void setSession(Session *session);
62
63protected:
64 Session *session;
65
66Q_SIGNALS:
67 void dataAvailable();
68
69private Q_SLOTS:
70 virtual void processResult(KJob *job) = 0;
71};
72
73template <typename T>
74struct EntityCacheNode
75{
76 EntityCacheNode()
77 : pending(false)
78 , invalid(false)
79 {
80 }
81 EntityCacheNode(typename T::Id id)
82 : entity(T(id))
83 , pending(true)
84 , invalid(false)
85 {
86 }
87 T entity;
88 bool pending;
89 bool invalid;
90};
91
96template<typename T, typename FetchJob, typename FetchScope_>
97class EntityCache : public EntityCacheBase
98{
99public:
100 typedef FetchScope_ FetchScope;
101 explicit EntityCache(int maxCapacity, Session *session = 0, QObject *parent = 0)
102 : EntityCacheBase(session, parent)
103 , mCapacity(maxCapacity)
104 {
105 }
106
107 ~EntityCache()
108 {
109 qDeleteAll(mCache);
110 }
111
113 bool isCached(typename T::Id id) const
114 {
115 EntityCacheNode<T> *node = cacheNodeForId(id);
116 return node && !node->pending;
117 }
118
120 bool isRequested(typename T::Id id) const
121 {
122 return cacheNodeForId(id);
123 }
124
126 virtual T retrieve(typename T::Id id) const
127 {
128 EntityCacheNode<T> *node = cacheNodeForId(id);
129 if (node && !node->pending && !node->invalid) {
130 return node->entity;
131 }
132 return T();
133 }
134
136 void invalidate(typename T::Id id)
137 {
138 EntityCacheNode<T> *node = cacheNodeForId(id);
139 if (node) {
140 node->invalid = true;
141 }
142 }
143
145 void update(typename T::Id id, const FetchScope &scope)
146 {
147 EntityCacheNode<T> *node = cacheNodeForId(id);
148 if (node) {
149 mCache.removeAll(node);
150 if (node->pending) {
151 request(id, scope);
152 }
153 delete node;
154 }
155 }
156
158 virtual bool ensureCached(typename T::Id id, const FetchScope &scope)
159 {
160 EntityCacheNode<T> *node = cacheNodeForId(id);
161 if (!node) {
162 request(id, scope);
163 return false;
164 }
165 return !node->pending;
166 }
167
173 virtual void request(typename T::Id id, const FetchScope &scope)
174 {
175 Q_ASSERT(!isRequested(id));
176 shrinkCache();
177 EntityCacheNode<T> *node = new EntityCacheNode<T>(id);
178 FetchJob *job = createFetchJob(id, scope);
179 job->setProperty("EntityCacheNode", QVariant::fromValue<typename T::Id>(id));
180 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
181 mCache.enqueue(node);
182 }
183
184private:
185 EntityCacheNode<T> *cacheNodeForId(typename T::Id id) const
186 {
187 for (typename QQueue<EntityCacheNode<T> *>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
188 it != endIt; ++it) {
189 if ((*it)->entity.id() == id) {
190 return *it;
191 }
192 }
193 return 0;
194 }
195
196 void processResult(KJob *job)
197 {
198 typename T::Id id = job->property("EntityCacheNode").template value<typename T::Id>();
199 // Error handling?
200 if ( job->error() ) {
201 kWarning() << job->errorString();
202 }
203 EntityCacheNode<T> *node = cacheNodeForId(id);
204 if (!node) {
205 return; // got replaced in the meantime
206 }
207
208 node->pending = false;
209 extractResult(node, job);
210 // make sure we find this node again if something went wrong here,
211 // most likely the object got deleted from the server in the meantime
212 if (node->entity.id() != id) {
213 // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
214 node->entity.setId(id);
215 node->invalid = true;
216 }
217 emit dataAvailable();
218 }
219
220 void extractResult(EntityCacheNode<T> *node, KJob *job) const;
221
222 inline FetchJob *createFetchJob(typename T::Id id, const FetchScope &scope)
223 {
224 FetchJob *fetch = new FetchJob(T(id), session);
225 fetch->setFetchScope(scope);
226 return fetch;
227 }
228
230 void shrinkCache()
231 {
232 while (mCache.size() >= mCapacity && !mCache.first()->pending) {
233 delete mCache.dequeue();
234 }
235 }
236
237private:
238 QQueue<EntityCacheNode<T> *> mCache;
239 int mCapacity;
240};
241
242template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult(EntityCacheNode<Collection> *node, KJob *job) const
243{
244 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
245 Q_ASSERT(fetch);
246 if (fetch->collections().isEmpty()) {
247 node->entity = Collection();
248 } else {
249 node->entity = fetch->collections().first();
250 }
251}
252
253template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult(EntityCacheNode<Item> *node, KJob *job) const
254{
255 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
256 Q_ASSERT(fetch);
257 if (fetch->items().isEmpty()) {
258 node->entity = Item();
259 } else {
260 node->entity = fetch->items().first();
261 }
262}
263
264template<> inline void EntityCache<Tag, TagFetchJob, TagFetchScope>::extractResult(EntityCacheNode<Tag> *node, KJob *job) const
265{
266 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
267 Q_ASSERT(fetch);
268 if (fetch->tags().isEmpty()) {
269 node->entity = Tag();
270 } else {
271 node->entity = fetch->tags().first();
272 }
273}
274
275template<> inline CollectionFetchJob *EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(Collection::Id id, const CollectionFetchScope &scope)
276{
277 CollectionFetchJob *fetch = new CollectionFetchJob(Collection(id), CollectionFetchJob::Base, session);
278 fetch->setFetchScope(scope);
279 return fetch;
280}
281
282typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
283typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
284typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
285
286template <typename T>
287struct EntityListCacheNode
288{
289 EntityListCacheNode()
290 : pending(false)
291 , invalid(false)
292 {
293 }
294 EntityListCacheNode(typename T::Id id)
295 : entity(id)
296 , pending(true)
297 , invalid(false)
298 {
299 }
300
301 T entity;
302 bool pending;
303 bool invalid;
304};
305
306template<typename T, typename FetchJob, typename FetchScope_>
307class EntityListCache : public EntityCacheBase
308{
309public:
310 typedef FetchScope_ FetchScope;
311
312 explicit EntityListCache(int maxCapacity, Session *session = 0, QObject *parent = 0)
313 : EntityCacheBase(session, parent)
314 , mCapacity(maxCapacity)
315 {
316 }
317
318 ~EntityListCache()
319 {
320 qDeleteAll(mCache);
321 }
322
324 typename T::List retrieve(const QList<Entity::Id> &ids) const
325 {
326 typename T::List list;
327
328 foreach (Entity::Id id, ids) {
329 EntityListCacheNode<T> *node = mCache.value(id);
330 if (!node || node->pending || node->invalid) {
331 return typename T::List();
332 }
333
334 list << node->entity;
335 }
336
337 return list;
338 }
339
341 bool ensureCached(const QList<Entity::Id> &ids, const FetchScope &scope)
342 {
343 QList<Entity::Id> toRequest;
344 bool result = true;
345
346 foreach (Entity::Id id, ids) {
347 EntityListCacheNode<T> *node = mCache.value(id);
348 if (!node) {
349 toRequest << id;
350 continue;
351 }
352
353 if (node->pending) {
354 result = false;
355 }
356 }
357
358 if (!toRequest.isEmpty()) {
359 request(toRequest, scope, ids);
360 return false;
361 }
362
363 return result;
364 }
365
367 void invalidate(const QList<Entity::Id> &ids)
368 {
369 foreach (Entity::Id id, ids) {
370 EntityListCacheNode<T> *node = mCache.value(id);
371 if (node) {
372 node->invalid = true;
373 }
374 }
375 }
376
378 void update(const QList<Entity::Id> &ids, const FetchScope &scope)
379 {
380 QList<Entity::Id> toRequest;
381
382 foreach (Entity::Id id, ids) {
383 EntityListCacheNode<T> *node = mCache.value(id);
384 if (node) {
385 mCache.remove(id);
386 if (node->pending) {
387 toRequest << id;
388 }
389 delete node;
390 }
391 }
392
393 if (!toRequest.isEmpty()) {
394 request(toRequest, scope);
395 }
396 }
397
403 void request(const QList<Entity::Id> &ids, const FetchScope &scope, const QList<Entity::Id> &preserveIds = QList<Entity::Id>())
404 {
405 Q_ASSERT(isNotRequested(ids));
406 shrinkCache(preserveIds);
407 foreach (Entity::Id id, ids) {
408 EntityListCacheNode<T> *node = new EntityListCacheNode<T>(id);
409 mCache.insert(id, node);
410 }
411 FetchJob *job = createFetchJob(ids, scope);
412 job->setProperty("EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >(ids));
413 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
414 }
415
416 bool isNotRequested(const QList<Entity::Id> &ids) const
417 {
418 foreach (Entity::Id id, ids) {
419 if (mCache.contains(id)) {
420 return false;
421 }
422 }
423
424 return true;
425 }
426
428 bool isCached(const QList<Entity::Id> &ids) const
429 {
430 foreach (Entity::Id id, ids) {
431 EntityListCacheNode<T> *node = mCache.value(id);
432 if (!node || node->pending) {
433 return false;
434 }
435 }
436 return true;
437 }
438
439private:
441 void shrinkCache(const QList<Entity::Id> &preserveIds)
442 {
443 typename
444 QHash< Entity::Id, EntityListCacheNode<T> *>::Iterator iter = mCache.begin();
445 while (iter != mCache.end() && mCache.size() >= mCapacity) {
446 if (iter.value()->pending || preserveIds.contains(iter.key())) {
447 ++iter;
448 continue;
449 }
450
451 delete iter.value();
452 iter = mCache.erase(iter);
453 }
454 }
455
456 inline FetchJob *createFetchJob(const QList<Entity::Id> &ids, const FetchScope &scope)
457 {
458 FetchJob *job = new FetchJob(ids, session);
459 job->setFetchScope(scope);
460 return job;
461 }
462
463 void processResult(KJob *job)
464 {
465 if ( job->error() ) {
466 kWarning() << job->errorString();
467 }
468 const QList<Entity::Id> ids = job->property("EntityListCacheIds").value< QList<Entity::Id> >();
469
470 typename T::List entities;
471 extractResults(job, entities);
472
473 foreach (Entity::Id id, ids) {
474 EntityListCacheNode<T> *node = mCache.value(id);
475 if (!node) {
476 continue; // got replaced in the meantime
477 }
478
479 node->pending = false;
480
481 T result;
482 typename T::List::Iterator iter = entities.begin();
483 for (; iter != entities.end(); ++iter) {
484 if ((*iter).id() == id) {
485 result = *iter;
486 entities.erase(iter);
487 break;
488 }
489 }
490
491 // make sure we find this node again if something went wrong here,
492 // most likely the object got deleted from the server in the meantime
493 if (!result.isValid()) {
494 node->entity = T(id);
495 node->invalid = true;
496 } else {
497 node->entity = result;
498 }
499 }
500
501 emit dataAvailable();
502 }
503
504 void extractResults(KJob *job, typename T::List &entities) const;
505
506private:
507 QHash< Entity::Id, EntityListCacheNode<T> *> mCache;
508 int mCapacity;
509};
510
511template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults(KJob *job, Collection::List &collections) const
512{
513 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
514 Q_ASSERT(fetch);
515 collections = fetch->collections();
516}
517
518template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults(KJob *job, Item::List &items) const
519{
520 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
521 Q_ASSERT(fetch);
522 items = fetch->items();
523}
524
525template<> inline void EntityListCache<Tag, TagFetchJob, TagFetchScope>::extractResults(KJob *job, Tag::List &tags) const
526{
527 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
528 Q_ASSERT(fetch);
529 tags = fetch->tags();
530}
531
532template<>
533inline CollectionFetchJob *EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(const QList<Entity::Id> &ids, const CollectionFetchScope &scope)
534{
535 CollectionFetchJob *fetch = new CollectionFetchJob(ids, CollectionFetchJob::Base, session);
536 fetch->setFetchScope(scope);
537 return fetch;
538}
539
540typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
541typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
542typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
543
544}
545
546#endif
Akonadi::CollectionFetchJob::Base
@ Base
Only fetch the base collection.
Definition: collectionfetchjob.h:62
Akonadi::Collection::List
QList< Collection > List
Describes a list of collections.
Definition: collection.h:81
Akonadi::EntityCacheBase
Definition: entitycache_p.h:56
Akonadi::EntityCache
Definition: entitycache_p.h:98
Akonadi::EntityCache::retrieve
virtual T retrieve(typename T::Id id) const
Returns the cached object if available, an empty instance otherwise.
Definition: entitycache_p.h:126
Akonadi::EntityCache::isRequested
bool isRequested(typename T::Id id) const
Object has been requested but is not yet loaded into the cache or is already available.
Definition: entitycache_p.h:120
Akonadi::EntityCache::ensureCached
virtual bool ensureCached(typename T::Id id, const FetchScope &scope)
Requests the object to be cached if it is not yet in the cache.
Definition: entitycache_p.h:158
Akonadi::EntityCache::update
void update(typename T::Id id, const FetchScope &scope)
Triggers a re-fetching of a cache entry, use if it has changed on the server.
Definition: entitycache_p.h:145
Akonadi::EntityCache::isCached
bool isCached(typename T::Id id) const
Object is available in the cache and can be retrieved.
Definition: entitycache_p.h:113
Akonadi::EntityCache::invalidate
void invalidate(typename T::Id id)
Marks the cache entry as invalid, use in case the object has been deleted on the server.
Definition: entitycache_p.h:136
Akonadi::EntityCache::request
virtual void request(typename T::Id id, const FetchScope &scope)
Asks the cache to retrieve id.
Definition: entitycache_p.h:173
Akonadi::Entity::Id
qint64 Id
Describes the unique id type.
Definition: entity.h:65
Akonadi::Session
A communication session with the Akonadi storage.
Definition: session.h:60
Akonadi
FreeBusyManager::Singleton.
Definition: actionstatemanager_p.h:28
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal