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

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • 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 
43 class Dummy;
44 class KJob;
45 
46 typedef QList<Akonadi::Entity::Id> EntityIdList;
47 Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
48 
49 namespace Akonadi {
50 
55 class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
56 {
57  Q_OBJECT
58 public:
59  explicit EntityCacheBase(Session *session, QObject *parent = 0);
60 
61  void setSession(Session *session);
62 
63 protected:
64  Session *session;
65 
66 Q_SIGNALS:
67  void dataAvailable();
68 
69 private Q_SLOTS:
70  virtual void processResult(KJob *job) = 0;
71 };
72 
73 template <typename T>
74 struct 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 
96 template<typename T, typename FetchJob, typename FetchScope_>
97 class EntityCache : public EntityCacheBase
98 {
99 public:
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 
184 private:
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 
237 private:
238  QQueue<EntityCacheNode<T> *> mCache;
239  int mCapacity;
240 };
241 
242 template<> 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 
253 template<> 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 
264 template<> 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 
275 template<> 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 
282 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
283 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
284 typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
285 
286 template <typename T>
287 struct 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 
306 template<typename T, typename FetchJob, typename FetchScope_>
307 class EntityListCache : public EntityCacheBase
308 {
309 public:
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 
439 private:
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 
506 private:
507  QHash< Entity::Id, EntityListCacheNode<T> *> mCache;
508  int mCapacity;
509 };
510 
511 template<> 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 
518 template<> 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 
525 template<> 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 
532 template<>
533 inline 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 
540 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
541 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
542 typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
543 
544 }
545 
546 #endif
QQueue
QHash::key
const Key key(const T &value) const
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
QList::erase
iterator erase(iterator pos)
QList::value
T value(int i) const
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::EntityCacheBase
Definition: entitycache_p.h:55
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
QHash
QObject
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
QList::isEmpty
bool isEmpty() const
QHash::begin
iterator begin()
Akonadi::Session
A communication session with the Akonadi storage.
Definition: session.h:59
QList
QHash::erase
iterator erase(iterator pos)
QList::end
iterator end()
QHash::value
const T value(const Key &key) const
QList::contains
bool contains(const T &value) const
QVariant::fromValue
QVariant fromValue(const T &value)
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
Definition: entitycache_p.h:97
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::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
QHash::end
iterator end()
QList::constBegin
const_iterator constBegin() const
QList::begin
iterator begin()
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs 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

Search



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