• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.13.3 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 <qobject.h>
37 #include <QQueue>
38 #include <QVariant>
39 #include <QHash>
40 #include <QtCore/QQueue>
41 
42 class Dummy;
43 class KJob;
44 
45 typedef QList<Akonadi::Entity::Id> EntityIdList;
46 Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
47 
48 namespace Akonadi {
49 
54 class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
55 {
56  Q_OBJECT
57 public:
58  explicit EntityCacheBase(Session *session, QObject *parent = 0);
59 
60  void setSession(Session *session);
61 
62 protected:
63  Session *session;
64 
65 Q_SIGNALS:
66  void dataAvailable();
67 
68 private Q_SLOTS:
69  virtual void processResult(KJob *job) = 0;
70 };
71 
72 template <typename T>
73 struct EntityCacheNode
74 {
75  EntityCacheNode()
76  : pending(false)
77  , invalid(false)
78  {
79  }
80  EntityCacheNode(typename T::Id id)
81  : entity(T(id))
82  , pending(true)
83  , invalid(false)
84  {
85  }
86  T entity;
87  bool pending;
88  bool invalid;
89 };
90 
95 template<typename T, typename FetchJob, typename FetchScope_>
96 class EntityCache : public EntityCacheBase
97 {
98 public:
99  typedef FetchScope_ FetchScope;
100  explicit EntityCache(int maxCapacity, Session *session = 0, QObject *parent = 0)
101  : EntityCacheBase(session, parent)
102  , mCapacity(maxCapacity)
103  {
104  }
105 
106  ~EntityCache()
107  {
108  qDeleteAll(mCache);
109  }
110 
112  bool isCached(typename T::Id id) const
113  {
114  EntityCacheNode<T> *node = cacheNodeForId(id);
115  return node && !node->pending;
116  }
117 
119  bool isRequested(typename T::Id id) const
120  {
121  return cacheNodeForId(id);
122  }
123 
125  virtual T retrieve(typename T::Id id) const
126  {
127  EntityCacheNode<T> *node = cacheNodeForId(id);
128  if (node && !node->pending && !node->invalid) {
129  return node->entity;
130  }
131  return T();
132  }
133 
135  void invalidate(typename T::Id id)
136  {
137  EntityCacheNode<T> *node = cacheNodeForId(id);
138  if (node) {
139  node->invalid = true;
140  }
141  }
142 
144  void update(typename T::Id id, const FetchScope &scope)
145  {
146  EntityCacheNode<T> *node = cacheNodeForId(id);
147  if (node) {
148  mCache.removeAll(node);
149  if (node->pending) {
150  request(id, scope);
151  }
152  delete node;
153  }
154  }
155 
157  virtual bool ensureCached(typename T::Id id, const FetchScope &scope)
158  {
159  EntityCacheNode<T> *node = cacheNodeForId(id);
160  if (!node) {
161  request(id, scope);
162  return false;
163  }
164  return !node->pending;
165  }
166 
172  virtual void request(typename T::Id id, const FetchScope &scope)
173  {
174  Q_ASSERT(!isRequested(id));
175  shrinkCache();
176  EntityCacheNode<T> *node = new EntityCacheNode<T>(id);
177  FetchJob *job = createFetchJob(id, scope);
178  job->setProperty("EntityCacheNode", QVariant::fromValue<typename T::Id>(id));
179  connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
180  mCache.enqueue(node);
181  }
182 
183 private:
184  EntityCacheNode<T> *cacheNodeForId(typename T::Id id) const
185  {
186  for (typename QQueue<EntityCacheNode<T> *>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
187  it != endIt; ++it) {
188  if ((*it)->entity.id() == id) {
189  return *it;
190  }
191  }
192  return 0;
193  }
194 
195  void processResult(KJob *job)
196  {
197  // Error handling?
198  typename T::Id id = job->property("EntityCacheNode").template value<typename T::Id>();
199  EntityCacheNode<T> *node = cacheNodeForId(id);
200  if (!node) {
201  return; // got replaced in the meantime
202  }
203 
204  node->pending = false;
205  extractResult(node, job);
206  // make sure we find this node again if something went wrong here,
207  // most likely the object got deleted from the server in the meantime
208  if (node->entity.id() != id) {
209  // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
210  node->entity.setId(id);
211  node->invalid = true;
212  }
213  emit dataAvailable();
214  }
215 
216  void extractResult(EntityCacheNode<T> *node, KJob *job) const;
217 
218  inline FetchJob *createFetchJob(typename T::Id id, const FetchScope &scope)
219  {
220  FetchJob *fetch = new FetchJob(T(id), session);
221  fetch->setFetchScope(scope);
222  return fetch;
223  }
224 
226  void shrinkCache()
227  {
228  while (mCache.size() >= mCapacity && !mCache.first()->pending) {
229  delete mCache.dequeue();
230  }
231  }
232 
233 private:
234  QQueue<EntityCacheNode<T> *> mCache;
235  int mCapacity;
236 };
237 
238 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult(EntityCacheNode<Collection> *node, KJob *job) const
239 {
240  CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
241  Q_ASSERT(fetch);
242  if (fetch->collections().isEmpty()) {
243  node->entity = Collection();
244  } else {
245  node->entity = fetch->collections().first();
246  }
247 }
248 
249 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult(EntityCacheNode<Item> *node, KJob *job) const
250 {
251  ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
252  Q_ASSERT(fetch);
253  if (fetch->items().isEmpty()) {
254  node->entity = Item();
255  } else {
256  node->entity = fetch->items().first();
257  }
258 }
259 
260 template<> inline void EntityCache<Tag, TagFetchJob, TagFetchScope>::extractResult(EntityCacheNode<Tag> *node, KJob *job) const
261 {
262  TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
263  Q_ASSERT(fetch);
264  if (fetch->tags().isEmpty()) {
265  node->entity = Tag();
266  } else {
267  node->entity = fetch->tags().first();
268  }
269 }
270 
271 template<> inline CollectionFetchJob *EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(Collection::Id id, const CollectionFetchScope &scope)
272 {
273  CollectionFetchJob *fetch = new CollectionFetchJob(Collection(id), CollectionFetchJob::Base, session);
274  fetch->setFetchScope(scope);
275  return fetch;
276 }
277 
278 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
279 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
280 typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
281 
282 template <typename T>
283 struct EntityListCacheNode
284 {
285  EntityListCacheNode()
286  : pending(false)
287  , invalid(false)
288  {
289  }
290  EntityListCacheNode(typename T::Id id)
291  : entity(id)
292  , pending(true)
293  , invalid(false)
294  {
295  }
296 
297  T entity;
298  bool pending;
299  bool invalid;
300 };
301 
302 template<typename T, typename FetchJob, typename FetchScope_>
303 class EntityListCache : public EntityCacheBase
304 {
305 public:
306  typedef FetchScope_ FetchScope;
307 
308  explicit EntityListCache(int maxCapacity, Session *session = 0, QObject *parent = 0)
309  : EntityCacheBase(session, parent)
310  , mCapacity(maxCapacity)
311  {
312  }
313 
314  ~EntityListCache()
315  {
316  qDeleteAll(mCache);
317  }
318 
320  typename T::List retrieve(const QList<Entity::Id> &ids) const
321  {
322  typename T::List list;
323 
324  foreach (Entity::Id id, ids) {
325  EntityListCacheNode<T> *node = mCache.value(id);
326  if (!node || node->pending || node->invalid) {
327  return typename T::List();
328  }
329 
330  list << node->entity;
331  }
332 
333  return list;
334  }
335 
337  bool ensureCached(const QList<Entity::Id> &ids, const FetchScope &scope)
338  {
339  QList<Entity::Id> toRequest;
340  bool result = true;
341 
342  foreach (Entity::Id id, ids) {
343  EntityListCacheNode<T> *node = mCache.value(id);
344  if (!node) {
345  toRequest << id;
346  continue;
347  }
348 
349  if (node->pending) {
350  result = false;
351  }
352  }
353 
354  if (!toRequest.isEmpty()) {
355  request(toRequest, scope, ids);
356  return false;
357  }
358 
359  return result;
360  }
361 
363  void invalidate(const QList<Entity::Id> &ids)
364  {
365  foreach (Entity::Id id, ids) {
366  EntityListCacheNode<T> *node = mCache.value(id);
367  if (node) {
368  node->invalid = true;
369  }
370  }
371  }
372 
374  void update(const QList<Entity::Id> &ids, const FetchScope &scope)
375  {
376  QList<Entity::Id> toRequest;
377 
378  foreach (Entity::Id id, ids) {
379  EntityListCacheNode<T> *node = mCache.value(id);
380  if (node) {
381  mCache.remove(id);
382  if (node->pending) {
383  toRequest << id;
384  }
385  delete node;
386  }
387  }
388 
389  if (!toRequest.isEmpty()) {
390  request(toRequest, scope);
391  }
392  }
393 
399  void request(const QList<Entity::Id> &ids, const FetchScope &scope, const QList<Entity::Id> &preserveIds = QList<Entity::Id>())
400  {
401  Q_ASSERT(isNotRequested(ids));
402  shrinkCache(preserveIds);
403  foreach (Entity::Id id, ids) {
404  EntityListCacheNode<T> *node = new EntityListCacheNode<T>(id);
405  mCache.insert(id, node);
406  }
407  FetchJob *job = createFetchJob(ids, scope);
408  job->setProperty("EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >(ids));
409  connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
410  }
411 
412  bool isNotRequested(const QList<Entity::Id> &ids) const
413  {
414  foreach (Entity::Id id, ids) {
415  if (mCache.contains(id)) {
416  return false;
417  }
418  }
419 
420  return true;
421  }
422 
424  bool isCached(const QList<Entity::Id> &ids) const
425  {
426  foreach (Entity::Id id, ids) {
427  EntityListCacheNode<T> *node = mCache.value(id);
428  if (!node || node->pending) {
429  return false;
430  }
431  }
432  return true;
433  }
434 
435 private:
437  void shrinkCache(const QList<Entity::Id> &preserveIds)
438  {
439  typename
440  QHash< Entity::Id, EntityListCacheNode<T> *>::Iterator iter = mCache.begin();
441  while (iter != mCache.end() && mCache.size() >= mCapacity) {
442  if (iter.value()->pending || preserveIds.contains(iter.key())) {
443  ++iter;
444  continue;
445  }
446 
447  delete iter.value();
448  iter = mCache.erase(iter);
449  }
450  }
451 
452  inline FetchJob *createFetchJob(const QList<Entity::Id> &ids, const FetchScope &scope)
453  {
454  FetchJob *job = new FetchJob(ids, session);
455  job->setFetchScope(scope);
456  return job;
457  }
458 
459  void processResult(KJob *job)
460  {
461  const QList<Entity::Id> ids = job->property("EntityListCacheIds").value< QList<Entity::Id> >();
462 
463  typename T::List entities;
464  extractResults(job, entities);
465 
466  foreach (Entity::Id id, ids) {
467  EntityListCacheNode<T> *node = mCache.value(id);
468  if (!node) {
469  continue; // got replaced in the meantime
470  }
471 
472  node->pending = false;
473 
474  T result;
475  typename T::List::Iterator iter = entities.begin();
476  for (; iter != entities.end(); ++iter) {
477  if ((*iter).id() == id) {
478  result = *iter;
479  entities.erase(iter);
480  break;
481  }
482  }
483 
484  // make sure we find this node again if something went wrong here,
485  // most likely the object got deleted from the server in the meantime
486  if (!result.isValid()) {
487  node->entity = T(id);
488  node->invalid = true;
489  } else {
490  node->entity = result;
491  }
492  }
493 
494  emit dataAvailable();
495  }
496 
497  void extractResults(KJob *job, typename T::List &entities) const;
498 
499 private:
500  QHash< Entity::Id, EntityListCacheNode<T> *> mCache;
501  int mCapacity;
502 };
503 
504 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults(KJob *job, Collection::List &collections) const
505 {
506  CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
507  Q_ASSERT(fetch);
508  collections = fetch->collections();
509 }
510 
511 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults(KJob *job, Item::List &items) const
512 {
513  ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
514  Q_ASSERT(fetch);
515  items = fetch->items();
516 }
517 
518 template<> inline void EntityListCache<Tag, TagFetchJob, TagFetchScope>::extractResults(KJob *job, Tag::List &tags) const
519 {
520  TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
521  Q_ASSERT(fetch);
522  tags = fetch->tags();
523 }
524 
525 template<>
526 inline CollectionFetchJob *EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(const QList<Entity::Id> &ids, const CollectionFetchScope &scope)
527 {
528  CollectionFetchJob *fetch = new CollectionFetchJob(ids, CollectionFetchJob::Base, session);
529  fetch->setFetchScope(scope);
530  return fetch;
531 }
532 
533 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
534 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
535 typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
536 
537 }
538 
539 #endif
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:157
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:135
Akonadi::EntityCacheBase
Definition: entitycache_p.h:54
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:144
Akonadi::EntityCache::isCached
bool isCached(typename T::Id id) const
Object is available in the cache and can be retrieved.
Definition: entitycache_p.h:112
Akonadi::Session
A communication session with the Akonadi storage.
Definition: session.h:59
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:119
Akonadi::EntityCache
Definition: entitycache_p.h:96
Akonadi::EntityCache::request
virtual void request(typename T::Id id, const FetchScope &scope)
Asks the cache to retrieve id.
Definition: entitycache_p.h:172
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:125
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Mon Jul 21 2014 08:03:52 by doxygen 1.8.6 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.13.3 API Reference

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