• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.10.5 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/session.h>
30 
31 #include "akonadiprivate_export.h"
32 
33 #include <qobject.h>
34 #include <QQueue>
35 #include <QVariant>
36 #include <QtCore/QQueue>
37 
38 class KJob;
39 
40 namespace Akonadi {
41 
46 class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
47 {
48  Q_OBJECT
49  public:
50  explicit EntityCacheBase ( Session *session, QObject * parent = 0 );
51 
52  void setSession(Session *session);
53 
54  protected:
55  Session *session;
56 
57  signals:
58  void dataAvailable();
59 
60  private slots:
61  virtual void processResult( KJob* job ) = 0;
62 };
63 
64 template <typename T>
65 struct EntityCacheNode
66 {
67  EntityCacheNode() : pending( false ), invalid( false ) {}
68  EntityCacheNode( typename T::Id id ) : entity( T( id ) ), pending( true ), invalid( false ) {}
69  T entity;
70  bool pending;
71  bool invalid;
72 };
73 
78 template<typename T, typename FetchJob, typename FetchScope_>
79 class EntityCache : public EntityCacheBase
80 {
81  public:
82  typedef FetchScope_ FetchScope;
83  explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
84  EntityCacheBase( session, parent ),
85  mCapacity( maxCapacity )
86  {}
87 
88  ~EntityCache()
89  {
90  qDeleteAll( mCache );
91  }
92 
94  bool isCached( typename T::Id id ) const
95  {
96  EntityCacheNode<T>* node = cacheNodeForId( id );
97  return node && !node->pending;
98  }
99 
101  bool isRequested( typename T::Id id ) const
102  {
103  return cacheNodeForId( id );
104  }
105 
107  virtual T retrieve( typename T::Id id ) const
108  {
109  EntityCacheNode<T>* node = cacheNodeForId( id );
110  if ( node && !node->pending && !node->invalid ) {
111  return node->entity;
112  }
113  return T();
114  }
115 
117  void invalidate( typename T::Id id )
118  {
119  EntityCacheNode<T>* node = cacheNodeForId( id );
120  if ( node ) {
121  node->invalid = true;
122  }
123  }
124 
126  void update( typename T::Id id, const FetchScope &scope )
127  {
128  EntityCacheNode<T>* node = cacheNodeForId( id );
129  if ( node ) {
130  mCache.removeAll( node );
131  if ( node->pending ) {
132  request( id, scope );
133  }
134  delete node;
135  }
136  }
137 
139  virtual bool ensureCached( typename T::Id id, const FetchScope &scope )
140  {
141  EntityCacheNode<T>* node = cacheNodeForId( id );
142  if ( !node ) {
143  request( id, scope );
144  return false;
145  }
146  return !node->pending;
147  }
148 
154  virtual void request( typename T::Id id, const FetchScope &scope )
155  {
156  Q_ASSERT( !isRequested( id ) );
157  shrinkCache();
158  EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
159  FetchJob* job = createFetchJob( id );
160  job->setFetchScope( scope );
161  job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
162  connect( job, SIGNAL( result( KJob* )), SLOT(processResult( KJob* ) ) );
163  mCache.enqueue( node );
164  }
165 
166  private:
167  EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
168  {
169  for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
170  it != endIt; ++it ) {
171  if ( ( *it )->entity.id() == id ) {
172  return *it;
173  }
174  }
175  return 0;
176  }
177 
178  void processResult( KJob* job )
179  {
180  // Error handling?
181  typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
182  EntityCacheNode<T> *node = cacheNodeForId( id );
183  if ( !node ) {
184  return; // got replaced in the meantime
185  }
186 
187  node->pending = false;
188  extractResult( node, job );
189  // make sure we find this node again if something went wrong here,
190  // most likely the object got deleted from the server in the meantime
191  if ( node->entity.id() != id ) {
192  // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
193  node->entity.setId( id );
194  node->invalid = true;
195  }
196  emit dataAvailable();
197  }
198 
199  void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
200 
201  inline FetchJob* createFetchJob( typename T::Id id )
202  {
203  return new FetchJob( T( id ), session );
204  }
205 
207  void shrinkCache()
208  {
209  while ( mCache.size() >= mCapacity && !mCache.first()->pending ) {
210  delete mCache.dequeue();
211  }
212  }
213 
214  private:
215  QQueue<EntityCacheNode<T>*> mCache;
216  int mCapacity;
217 };
218 
219 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
220 {
221  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
222  Q_ASSERT( fetch );
223  if ( fetch->collections().isEmpty() ) {
224  node->entity = Collection();
225  } else {
226  node->entity = fetch->collections().first();
227  }
228 }
229 
230 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
231 {
232  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
233  Q_ASSERT( fetch );
234  if ( fetch->items().isEmpty() ) {
235  node->entity = Item();
236  } else {
237  node->entity = fetch->items().first();
238  }
239 }
240 
241 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
242 {
243  return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
244 }
245 
246 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
247 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
248 
249 
250 template<typename T>
251 class Comparator
252 {
253 public:
254  static bool compare(const typename T::List &lhs_, const QList<typename T::Id> &rhs_ )
255  {
256  if ( lhs_.size() != rhs_.size() ) {
257  return false;
258  }
259 
260  typename T::List lhs = lhs_;
261  QList<typename T::Id> rhs = rhs_;
262 
263  qSort( lhs );
264  qSort( rhs );
265  return lhs == rhs;
266  }
267 
268  static bool compare(const QList<typename T::Id> &l1, const typename T::List &l2)
269  {
270  return compare( l2, l1 );
271  }
272 
273  static bool compare(const typename T::List &l1, const typename T::List &l2)
274  {
275  typename T::List l1_ = l1;
276  typename T::List l2_ = l2;
277  qSort( l1_ );
278  qSort( l2_ );
279  return l1_ == l2_;
280  }
281 };
282 
283 
284 template <typename T>
285 struct EntityListCacheNode
286 {
287  EntityListCacheNode( const typename T::List &list ) : entityList( list ), pending( false ), invalid( false ) {}
288  EntityListCacheNode( const QList<typename T::Id> &list ) : pending( false ), invalid( false ) {
289  foreach ( typename T::Id id, list ) {
290  entityList.append( T( id ) );
291  }
292  }
293  typename T::List entityList;
294  bool pending;
295  bool invalid;
296 };
297 
298 template<typename T, typename FetchJob, typename FetchScope_>
299 class EntityListCache : public EntityCacheBase
300 {
301 public:
302  typedef FetchScope_ FetchScope;
303 
304  explicit EntityListCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
305  EntityCacheBase( session, parent ),
306  mCapacity( maxCapacity )
307  {}
308 
309  ~EntityListCache()
310  {
311  qDeleteAll( mCache );
312  }
313 
315  template<typename TArg>
316  typename T::List retrieve( const QList<TArg> &id ) const
317  {
318  EntityListCacheNode<T>* node = cacheNodeForId( id );
319  if ( node && !node->pending && !node->invalid ) {
320  return node->entityList;
321  }
322  return typename T::List();
323  }
324 
326  template<typename TArg>
327  bool ensureCached( const QList<TArg> &id, const FetchScope &scope )
328  {
329  EntityListCacheNode<T>* node = cacheNodeForId( id );
330  if ( !node ) {
331  request( id, scope );
332  return false;
333  }
334  return !node->pending;
335  }
336 
338  template<typename TArg>
339  void invalidate( const QList<TArg> &id )
340  {
341  EntityListCacheNode<T>* node = cacheNodeForId( id );
342  if ( node ) {
343  node->invalid = true;
344  }
345  }
346 
348  template<typename TArg>
349  void update( const QList<TArg> &id, const FetchScope &scope )
350  {
351  EntityListCacheNode<T>* node = cacheNodeForId( id );
352  if ( node ) {
353  mCache.removeAll( node );
354  if ( node->pending ) {
355  request( id, scope );
356  }
357  delete node;
358  }
359  }
360 
361 
363  template<typename TArg>
364  bool isCached( const QList<TArg> &id ) const
365  {
366  EntityListCacheNode<T>* node = cacheNodeForId( id );
367  return node && !node->pending;
368  }
369 
370 private:
371 
372  typename T::List getTList( const QList<typename T::Id> &id )
373  {
374  typename T::List ids;
375  foreach ( typename T::Id id_, id ) {
376  ids.append( T( id_ ) );
377  }
378  return ids;
379  }
380 
381  typename T::List getTList( const typename T::List &id )
382  {
383  return id;
384  }
385 
391  template<typename TArg>
392  void request( const QList<TArg> &id, const FetchScope &scope )
393  {
394  Q_ASSERT( !isRequested( id ) );
395  shrinkCache();
396  EntityListCacheNode<T> *node = new EntityListCacheNode<T>( id );
397  FetchJob* job = createFetchJob( id );
398  job->setFetchScope( scope );
399  job->setProperty( "EntityListCacheNode", QVariant::fromValue<typename T::List>( getTList( id ) ) );
400  connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) );
401  mCache.enqueue( node );
402  }
403 
405  void shrinkCache()
406  {
407  while ( mCache.size() >= mCapacity && !mCache.first()->pending ) {
408  delete mCache.dequeue();
409  }
410  }
411 
413  template<typename TArg>
414  bool isRequested( const QList<TArg> &id ) const
415  {
416  return cacheNodeForId( id );
417  }
418 
419  template<typename TArg>
420  EntityListCacheNode<T>* cacheNodeForId( const QList<TArg> &id ) const
421  {
422  for ( typename QQueue<EntityListCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
423  it != endIt; ++it ) {
424  if ( Comparator<T>::compare( ( *it )->entityList, id ) ) {
425  return *it;
426  }
427  }
428  return 0;
429  }
430 
431  template<typename TArg>
432  inline FetchJob* createFetchJob( const QList<TArg> &id )
433  {
434  return new FetchJob( id, session );
435  }
436 
437  void processResult( KJob* job )
438  {
439  typename T::List ids = job->property( "EntityListCacheNode" ).template value<typename T::List>();
440 
441  EntityListCacheNode<T> *node = cacheNodeForId( ids );
442  if ( !node ) {
443  return; // got replaced in the meantime
444  }
445 
446  node->pending = false;
447  extractResult( node, job );
448  // make sure we find this node again if something went wrong here,
449  // most likely the object got deleted from the server in the meantime
450  if ( node->entityList != ids ) {
451  node->entityList = ids;
452  node->invalid = true;
453  }
454  emit dataAvailable();
455  }
456 
457  void extractResult( EntityListCacheNode<T>* node, KJob* job ) const;
458 
459 
460 private:
461  QQueue<EntityListCacheNode<T>*> mCache;
462  int mCapacity;
463 };
464 
465 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityListCacheNode<Collection>* node, KJob *job ) const
466 {
467  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
468  Q_ASSERT( fetch );
469  node->entityList = fetch->collections();
470 }
471 
472 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityListCacheNode<Item>* node, KJob *job ) const
473 {
474  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
475  Q_ASSERT( fetch );
476  node->entityList = fetch->items();
477 }
478 
479 template<>
480 template<typename TArg>
481 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( const QList<TArg> &id )
482 {
483  return new CollectionFetchJob( id, CollectionFetchJob::Base, session );
484 }
485 
486 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
487 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
488 
489 }
490 
491 #endif
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:35 by doxygen 1.8.3.1 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.10.5 API Reference

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