• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.1 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  return T();
113  }
114 
116  void invalidate( typename T::Id id )
117  {
118  EntityCacheNode<T>* node = cacheNodeForId( id );
119  if ( node )
120  node->invalid = true;
121  }
122 
124  void update( typename T::Id id, const FetchScope &scope )
125  {
126  EntityCacheNode<T>* node = cacheNodeForId( id );
127  if ( node ) {
128  mCache.removeAll( node );
129  if ( node->pending )
130  request( id, scope );
131  delete node;
132  }
133  }
134 
136  virtual bool ensureCached( typename T::Id id, const FetchScope &scope )
137  {
138  EntityCacheNode<T>* node = cacheNodeForId( id );
139  if ( !node ) {
140  request( id, scope );
141  return false;
142  }
143  return !node->pending;
144  }
145 
151  virtual void request( typename T::Id id, const FetchScope &scope )
152  {
153  Q_ASSERT( !isRequested( id ) );
154  shrinkCache();
155  EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
156  FetchJob* job = createFetchJob( id );
157  job->setFetchScope( scope );
158  job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
159  connect( job, SIGNAL( result( KJob* )), SLOT(processResult( KJob* ) ) );
160  mCache.enqueue( node );
161  }
162 
163  private:
164  EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
165  {
166  for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
167  it != endIt; ++it )
168  {
169  if ( (*it)->entity.id() == id )
170  return *it;
171  }
172  return 0;
173  }
174 
175  void processResult( KJob* job )
176  {
177  // Error handling?
178  typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
179  EntityCacheNode<T> *node = cacheNodeForId( id );
180  if ( !node )
181  return; // got replaced in the meantime
182 
183  node->pending = false;
184  extractResult( node, job );
185  // make sure we find this node again if something went wrong here,
186  // most likely the object got deleted from the server in the meantime
187  if ( node->entity.id() != id ) {
188  // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
189  node->entity.setId( id );
190  node->invalid = true;
191  }
192  emit dataAvailable();
193  }
194 
195  void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
196 
197  inline FetchJob* createFetchJob( typename T::Id id )
198  {
199  return new FetchJob( T( id ), session );
200  }
201 
203  void shrinkCache()
204  {
205  while ( mCache.size() >= mCapacity && !mCache.first()->pending )
206  delete mCache.dequeue();
207  }
208 
209  private:
210  QQueue<EntityCacheNode<T>*> mCache;
211  int mCapacity;
212 };
213 
214 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
215 {
216  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
217  Q_ASSERT( fetch );
218  if ( fetch->collections().isEmpty() )
219  node->entity = Collection();
220  else
221  node->entity = fetch->collections().first();
222 }
223 
224 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
225 {
226  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
227  Q_ASSERT( fetch );
228  if ( fetch->items().isEmpty() )
229  node->entity = Item();
230  else
231  node->entity = fetch->items().first();
232 }
233 
234 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
235 {
236  return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
237 }
238 
239 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
240 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
241 
242 
243 template<typename T>
244 class Comparator
245 {
246 public:
247  static bool compare(const typename T::List &lhs_, const QList<typename T::Id> &rhs_ )
248  {
249  bool val = true;
250 
251  if (lhs_.size() != rhs_.size())
252  return false;
253 
254  typename T::List lhs = lhs_;
255  QList<typename T::Id> rhs = rhs_;
256 
257  qSort(lhs);
258  qSort(rhs);
259  return lhs == rhs;
260  }
261 
262  static bool compare(const QList<typename T::Id> &l1, const typename T::List &l2)
263  {
264  return compare(l2, l1);
265  }
266 
267  static bool compare(const typename T::List &l1, const typename T::List &l2)
268  {
269  typename T::List l1_ = l1;
270  typename T::List l2_ = l2;
271  qSort(l1_);
272  qSort(l2_);
273  return l1_ == l2_;
274  }
275 };
276 
277 
278 template <typename T>
279 struct EntityListCacheNode
280 {
281  EntityListCacheNode( const typename T::List &list ) : entityList( list ), pending( false ), invalid( false ) {}
282  EntityListCacheNode( const QList<typename T::Id> &list ) : pending( false ), invalid( false ) {
283  foreach( typename T::Id id, list)
284  entityList.append(T(id));
285  }
286  typename T::List entityList;
287  bool pending;
288  bool invalid;
289 };
290 
291 template<typename T, typename FetchJob, typename FetchScope_>
292 class EntityListCache : public EntityCacheBase
293 {
294 public:
295  typedef FetchScope_ FetchScope;
296 
297  explicit EntityListCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
298  EntityCacheBase( session, parent ),
299  mCapacity( maxCapacity )
300  {}
301 
302  ~EntityListCache()
303  {
304  qDeleteAll( mCache );
305  }
306 
308  template<typename TArg>
309  typename T::List retrieve( const QList<TArg> &id ) const
310  {
311  EntityListCacheNode<T>* node = cacheNodeForId( id );
312  if ( node && !node->pending && !node->invalid )
313  return node->entityList;
314  return typename T::List();
315  }
316 
318  template<typename TArg>
319  bool ensureCached( const QList<TArg> &id, const FetchScope &scope )
320  {
321  EntityListCacheNode<T>* node = cacheNodeForId( id );
322  if ( !node ) {
323  request( id, scope );
324  return false;
325  }
326  return !node->pending;
327  }
328 
330  template<typename TArg>
331  void invalidate( const QList<TArg> &id )
332  {
333  EntityListCacheNode<T>* node = cacheNodeForId( id );
334  if ( node )
335  node->invalid = true;
336  }
337 
339  template<typename TArg>
340  void update( const QList<TArg> &id, const FetchScope &scope )
341  {
342  EntityListCacheNode<T>* node = cacheNodeForId( id );
343  if ( node ) {
344  mCache.removeAll( node );
345  if ( node->pending )
346  request( id, scope );
347  delete node;
348  }
349  }
350 
351 
353  template<typename TArg>
354  bool isCached( const QList<TArg> &id ) const
355  {
356  EntityListCacheNode<T>* node = cacheNodeForId( id );
357  return node && !node->pending;
358  }
359 
360 private:
361 
362  typename T::List getTList( const QList<typename T::Id> &id )
363  {
364  typename T::List ids;
365  foreach(typename T::Id id_, id)
366  ids.append(T(id_));
367  return ids;
368  }
369 
370  typename T::List getTList( const typename T::List &id )
371  {
372  return id;
373  }
374 
380  template<typename TArg>
381  void request( const QList<TArg> &id, const FetchScope &scope )
382  {
383  Q_ASSERT( !isRequested( id ) );
384  shrinkCache();
385  EntityListCacheNode<T> *node = new EntityListCacheNode<T>( id );
386  FetchJob* job = createFetchJob( id );
387  job->setFetchScope( scope );
388  job->setProperty( "EntityListCacheNode", QVariant::fromValue<typename T::List>( getTList( id ) ) );
389  connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) );
390  mCache.enqueue( node );
391  }
392 
394  void shrinkCache()
395  {
396  while ( mCache.size() >= mCapacity && !mCache.first()->pending )
397  delete mCache.dequeue();
398  }
399 
401  template<typename TArg>
402  bool isRequested( const QList<TArg> &id ) const
403  {
404  return cacheNodeForId( id );
405  }
406 
407  template<typename TArg>
408  EntityListCacheNode<T>* cacheNodeForId( const QList<TArg> &id ) const
409  {
410  for ( typename QQueue<EntityListCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
411  it != endIt; ++it )
412  {
413  if ( Comparator<T>::compare( ( *it )->entityList, id ) )
414  return *it;
415  }
416  return 0;
417  }
418 
419  template<typename TArg>
420  inline FetchJob* createFetchJob( const QList<TArg> &id )
421  {
422  return new FetchJob( id, session );
423  }
424 
425  void processResult( KJob* job )
426  {
427  typename T::List ids = job->property( "EntityListCacheNode" ).template value<typename T::List>();
428 
429  EntityListCacheNode<T> *node = cacheNodeForId( ids );
430  if ( !node )
431  return; // got replaced in the meantime
432 
433  node->pending = false;
434  extractResult( node, job );
435  // make sure we find this node again if something went wrong here,
436  // most likely the object got deleted from the server in the meantime
437  if ( node->entityList != ids ) {
438  node->entityList = ids;
439  node->invalid = true;
440  }
441  emit dataAvailable();
442  }
443 
444  void extractResult( EntityListCacheNode<T>* node, KJob* job ) const;
445 
446 
447 private:
448  QQueue<EntityListCacheNode<T>*> mCache;
449  int mCapacity;
450 };
451 
452 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityListCacheNode<Collection>* node, KJob *job ) const
453 {
454  CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
455  Q_ASSERT( fetch );
456  node->entityList = fetch->collections();
457 }
458 
459 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityListCacheNode<Item>* node, KJob *job ) const
460 {
461  ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
462  Q_ASSERT( fetch );
463  node->entityList = fetch->items();
464 }
465 
466 template<>
467 template<typename TArg>
468 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( const QList<TArg> &id )
469 {
470  return new CollectionFetchJob( id, CollectionFetchJob::Base, session );
471 }
472 
473 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
474 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
475 
476 }
477 
478 #endif
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Sep 24 2012 09:06:26 by doxygen 1.8.1.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.9.1 API Reference

Skip menu "kdepimlibs-4.9.1 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • 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