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

akonadi

  • sources
  • kde-4.12
  • 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/session.h>
30 
31 #include "akonadiprivate_export.h"
32 
33 #include <qobject.h>
34 #include <QQueue>
35 #include <QVariant>
36 #include <QHash>
37 #include <QtCore/QQueue>
38 
39 class KJob;
40 
41 typedef QList<Akonadi::Entity::Id> EntityIdList;
42 Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
43 
44 namespace Akonadi {
45 
50 class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
51 {
52  Q_OBJECT
53  public:
54  explicit EntityCacheBase ( Session *session, QObject * parent = 0 );
55 
56  void setSession(Session *session);
57 
58  protected:
59  Session *session;
60 
61  signals:
62  void dataAvailable();
63 
64  private slots:
65  virtual void processResult( KJob* job ) = 0;
66 };
67 
68 template <typename T>
69 struct EntityCacheNode
70 {
71  EntityCacheNode() : pending( false ), invalid( false ) {}
72  EntityCacheNode( typename T::Id id ) : entity( T( id ) ), pending( true ), invalid( false ) {}
73  T entity;
74  bool pending;
75  bool invalid;
76 };
77 
82 template<typename T, typename FetchJob, typename FetchScope_>
83 class EntityCache : public EntityCacheBase
84 {
85  public:
86  typedef FetchScope_ FetchScope;
87  explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
88  EntityCacheBase( session, parent ),
89  mCapacity( maxCapacity )
90  {}
91 
92  ~EntityCache()
93  {
94  qDeleteAll( mCache );
95  }
96 
98  bool isCached( typename T::Id id ) const
99  {
100  EntityCacheNode<T>* node = cacheNodeForId( id );
101  return node && !node->pending;
102  }
103 
105  bool isRequested( typename T::Id id ) const
106  {
107  return cacheNodeForId( id );
108  }
109 
111  virtual T retrieve( typename T::Id id ) const
112  {
113  EntityCacheNode<T>* node = cacheNodeForId( id );
114  if ( node && !node->pending && !node->invalid ) {
115  return node->entity;
116  }
117  return T();
118  }
119 
121  void invalidate( typename T::Id id )
122  {
123  EntityCacheNode<T>* node = cacheNodeForId( id );
124  if ( node ) {
125  node->invalid = true;
126  }
127  }
128 
130  void update( typename T::Id id, const FetchScope &scope )
131  {
132  EntityCacheNode<T>* node = cacheNodeForId( id );
133  if ( node ) {
134  mCache.removeAll( node );
135  if ( node->pending ) {
136  request( id, scope );
137  }
138  delete node;
139  }
140  }
141 
143  virtual bool ensureCached( typename T::Id id, const FetchScope &scope )
144  {
145  EntityCacheNode<T>* node = cacheNodeForId( id );
146  if ( !node ) {
147  request( id, scope );
148  return false;
149  }
150  return !node->pending;
151  }
152 
158  virtual void request( typename T::Id id, const FetchScope &scope )
159  {
160  Q_ASSERT( !isRequested( id ) );
161  shrinkCache();
162  EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
163  FetchJob* job = createFetchJob( id );
164  job->setFetchScope( scope );
165  job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
166  connect( job, SIGNAL( result( KJob* )), SLOT(processResult( KJob* ) ) );
167  mCache.enqueue( node );
168  }
169 
170  private:
171  EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
172  {
173  for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
174  it != endIt; ++it ) {
175  if ( ( *it )->entity.id() == id ) {
176  return *it;
177  }
178  }
179  return 0;
180  }
181 
182  void processResult( KJob* job )
183  {
184  // Error handling?
185  typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
186  EntityCacheNode<T> *node = cacheNodeForId( id );
187  if ( !node ) {
188  return; // got replaced in the meantime
189  }
190 
191  node->pending = false;
192  extractResult( node, job );
193  // make sure we find this node again if something went wrong here,
194  // most likely the object got deleted from the server in the meantime
195  if ( node->entity.id() != id ) {
196  // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
197  node->entity.setId( id );
198  node->invalid = true;
199  }
200  emit dataAvailable();
201  }
202 
203  void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
204 
205  inline FetchJob* createFetchJob( typename T::Id id )
206  {
207  return new FetchJob( T( id ), session );
208  }
209 
211  void shrinkCache()
212  {
213  while ( mCache.size() >= mCapacity && !mCache.first()->pending ) {
214  delete mCache.dequeue();
215  }
216  }
217 
218  private:
219  QQueue<EntityCacheNode<T>*> mCache;
220  int mCapacity;
221 };
222 
223 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
224 {
225  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
226  Q_ASSERT( fetch );
227  if ( fetch->collections().isEmpty() ) {
228  node->entity = Collection();
229  } else {
230  node->entity = fetch->collections().first();
231  }
232 }
233 
234 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
235 {
236  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
237  Q_ASSERT( fetch );
238  if ( fetch->items().isEmpty() ) {
239  node->entity = Item();
240  } else {
241  node->entity = fetch->items().first();
242  }
243 }
244 
245 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
246 {
247  return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
248 }
249 
250 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
251 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
252 
253 template <typename T>
254 struct EntityListCacheNode
255 {
256  EntityListCacheNode() : pending( false ), invalid( false ) {}
257  EntityListCacheNode( typename T::Id id ) : entity(id), pending( true ), invalid( false ) {}
258 
259  T entity;
260  bool pending;
261  bool invalid;
262 };
263 
264 template<typename T, typename FetchJob, typename FetchScope_>
265 class EntityListCache : public EntityCacheBase
266 {
267 public:
268  typedef FetchScope_ FetchScope;
269 
270  explicit EntityListCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
271  EntityCacheBase( session, parent ),
272  mCapacity( maxCapacity )
273  {}
274 
275  ~EntityListCache()
276  {
277  qDeleteAll( mCache );
278  }
279 
281  typename T::List retrieve( const QList<Entity::Id> &ids ) const
282  {
283  typename T::List list;
284 
285  foreach( Entity::Id id, ids ) {
286  EntityListCacheNode<T>* node = mCache.value( id );
287  if ( !node || node->pending || node->invalid )
288  return typename T::List();
289 
290  list << node->entity;
291  }
292 
293  return list;
294  }
295 
297  bool ensureCached( const QList<Entity::Id> &ids, const FetchScope &scope )
298  {
299  QList<Entity::Id> toRequest;
300  bool result = true;
301 
302  foreach( Entity::Id id, ids ) {
303  EntityListCacheNode<T>* node = mCache.value( id );
304  if ( !node ) {
305  toRequest << id;
306  continue;
307  }
308 
309  if ( node->pending )
310  result = false;
311  }
312 
313  if ( !toRequest.isEmpty() ) {
314  request( toRequest, scope, ids );
315  return false;
316  }
317 
318  return result;
319  }
320 
322  void invalidate( const QList<Entity::Id> &ids )
323  {
324  foreach( Entity::Id id, ids ) {
325  EntityListCacheNode<T>* node = mCache.value( id );
326  if ( node ) {
327  node->invalid = true;
328  }
329  }
330  }
331 
333  void update( const QList<Entity::Id> &ids, const FetchScope &scope )
334  {
335  QList<Entity::Id> toRequest;
336 
337  foreach( Entity::Id id, ids ) {
338  EntityListCacheNode<T>* node = mCache.value( id );
339  if ( node ) {
340  mCache.remove( id );
341  if ( node->pending ) {
342  toRequest << id;
343  }
344  delete node;
345  }
346  }
347 
348  if ( !toRequest.isEmpty() ) {
349  request( toRequest, scope );
350  }
351  }
352 
358  void request( const QList<Entity::Id> &ids, const FetchScope &scope, const QList<Entity::Id> &preserveIds = QList<Entity::Id>() )
359  {
360  Q_ASSERT( isNotRequested( ids ) );
361  shrinkCache( preserveIds );
362  foreach( Entity::Id id, ids ) {
363  EntityListCacheNode<T> *node = new EntityListCacheNode<T>( id );
364  mCache.insert( id, node );
365  }
366  FetchJob* job = createFetchJob( ids );
367  job->setFetchScope( scope );
368  job->setProperty( "EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >( ids ) );
369  connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) );
370  }
371 
372  bool isNotRequested( const QList<Entity::Id> &ids ) const
373  {
374  foreach( Entity::Id id, ids ) {
375  if ( mCache.contains( id ) )
376  return false;
377  }
378 
379  return true;
380  }
381 
383  bool isCached( const QList<Entity::Id> &ids ) const
384  {
385  foreach( Entity::Id id, ids ) {
386  EntityListCacheNode<T>* node = mCache.value( id );
387  if ( !node || node->pending ) {
388  return false;
389  }
390  }
391  return true;
392  }
393 
394 private:
396  void shrinkCache( const QList<Entity::Id> &preserveIds )
397  {
398  typename
399  QHash< Entity::Id, EntityListCacheNode<T>* >::Iterator iter = mCache.begin();
400  while ( iter != mCache.end() && mCache.size() >= mCapacity ) {
401  if ( iter.value()->pending || preserveIds.contains( iter.key() ) ) {
402  ++iter;
403  continue;
404  }
405 
406  delete iter.value();
407  iter = mCache.erase( iter );
408  }
409  }
410 
411  inline FetchJob* createFetchJob( const QList<Entity::Id> &ids )
412  {
413  return new FetchJob( ids, session );
414  }
415 
416  void processResult( KJob* job )
417  {
418  const QList<Entity::Id> ids = job->property( "EntityListCacheIds" ).value< QList<Entity::Id> >();
419 
420  typename T::List entities;
421  extractResults( job, entities );
422 
423  foreach( Entity::Id id, ids ) {
424  EntityListCacheNode<T> *node = mCache.value( id );
425  if ( !node ) {
426  continue; // got replaced in the meantime
427  }
428 
429  node->pending = false;
430 
431  T result;
432  typename T::List::Iterator iter = entities.begin();
433  for ( ; iter != entities.end(); ++iter ) {
434  if ( (*iter).id() == id ) {
435  result = *iter;
436  entities.erase( iter );
437  break;
438  }
439  }
440 
441  // make sure we find this node again if something went wrong here,
442  // most likely the object got deleted from the server in the meantime
443  if ( !result.isValid() ) {
444  node->entity = T( id );
445  node->invalid = true;
446  } else {
447  node->entity = result;
448  }
449  }
450 
451  emit dataAvailable();
452  }
453 
454  void extractResults( KJob* job, typename T::List &entities ) const;
455 
456 private:
457  QHash< Entity::Id, EntityListCacheNode<T>* > mCache;
458  int mCapacity;
459 };
460 
461 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults( KJob *job, Collection::List &collections ) const
462 {
463  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
464  Q_ASSERT( fetch );
465  collections = fetch->collections();
466 }
467 
468 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults( KJob *job, Item::List &items ) const
469 {
470  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
471  Q_ASSERT( fetch );
472  items = fetch->items();
473 }
474 
475 template<>
476 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( const QList<Entity::Id> &ids )
477 {
478  return new CollectionFetchJob( ids, CollectionFetchJob::Base, session );
479 }
480 
481 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
482 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
483 
484 }
485 
486 #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:143
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:121
Akonadi::EntityCacheBase
Definition: entitycache_p.h:50
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:130
Akonadi::EntityCache::isCached
bool isCached(typename T::Id id) const
Object is available in the cache and can be retrieved.
Definition: entitycache_p.h:98
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:105
Akonadi::EntityCache
Definition: entitycache_p.h:83
Akonadi::EntityCache::request
virtual void request(typename T::Id id, const FetchScope &scope)
Asks the cache to retrieve id.
Definition: entitycache_p.h:158
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:111
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:27 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
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

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