• Skip to content
  • Skip to link menu
KDE 4.8 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

akonadi

entitycache_p.h
00001 /*
00002     Copyright (c) 2009 Volker Krause <vkrause@kde.org>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #ifndef AKONADI_ENTITYCACHE_P_H
00021 #define AKONADI_ENTITYCACHE_P_H
00022 
00023 #include <akonadi/item.h>
00024 #include <akonadi/itemfetchjob.h>
00025 #include <akonadi/itemfetchscope.h>
00026 #include <akonadi/collection.h>
00027 #include <akonadi/collectionfetchjob.h>
00028 #include <akonadi/collectionfetchscope.h>
00029 #include <akonadi/session.h>
00030 
00031 #include "akonadiprivate_export.h"
00032 
00033 #include <qobject.h>
00034 #include <QQueue>
00035 #include <QVariant>
00036 #include <QtCore/QQueue>
00037 
00038 class KJob;
00039 
00040 namespace Akonadi {
00041 
00046 class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject
00047 {
00048   Q_OBJECT
00049   public:
00050     explicit EntityCacheBase ( Session *session, QObject * parent = 0 );
00051 
00052     void setSession(Session *session);
00053 
00054   protected:
00055     Session *session;
00056 
00057   signals:
00058     void dataAvailable();
00059 
00060   private slots:
00061     virtual void processResult( KJob* job ) = 0;
00062 };
00063 
00064 template <typename T>
00065 struct EntityCacheNode
00066 {
00067   EntityCacheNode() : pending( false ), invalid( false ) {}
00068   EntityCacheNode( typename T::Id id ) : entity( T( id ) ), pending( true ), invalid( false ) {}
00069   T entity;
00070   bool pending;
00071   bool invalid;
00072 };
00073 
00078 template<typename T, typename FetchJob, typename FetchScope_>
00079 class EntityCache : public EntityCacheBase
00080 {
00081   public:
00082     typedef FetchScope_ FetchScope;
00083     explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
00084       EntityCacheBase( session, parent ),
00085       mCapacity( maxCapacity )
00086     {}
00087 
00088     ~EntityCache()
00089     {
00090       qDeleteAll( mCache );
00091     }
00092 
00094     bool isCached( typename T::Id id ) const
00095     {
00096       EntityCacheNode<T>* node = cacheNodeForId( id );
00097       return node && !node->pending;
00098     }
00099 
00101     bool isRequested( typename T::Id id ) const
00102     {
00103       return cacheNodeForId( id );
00104     }
00105 
00107     virtual T retrieve( typename T::Id id ) const
00108     {
00109       EntityCacheNode<T>* node = cacheNodeForId( id );
00110       if ( node && !node->pending && !node->invalid )
00111         return node->entity;
00112       return T();
00113     }
00114 
00116     void invalidate( typename T::Id id )
00117     {
00118       EntityCacheNode<T>* node = cacheNodeForId( id );
00119       if ( node )
00120         node->invalid = true;
00121     }
00122 
00124     void update( typename T::Id id, const FetchScope &scope )
00125     {
00126       EntityCacheNode<T>* node = cacheNodeForId( id );
00127       if ( node ) {
00128         mCache.removeAll( node );
00129         if ( node->pending )
00130           request( id, scope );
00131         delete node;
00132       }
00133     }
00134 
00136     virtual bool ensureCached( typename T::Id id, const FetchScope &scope )
00137     {
00138       EntityCacheNode<T>* node = cacheNodeForId( id );
00139       if ( !node ) {
00140         request( id, scope );
00141         return false;
00142       }
00143       return !node->pending;
00144     }
00145 
00151     virtual void request( typename T::Id id, const FetchScope &scope )
00152     {
00153       Q_ASSERT( !isRequested( id ) );
00154       shrinkCache();
00155       EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
00156       FetchJob* job = createFetchJob( id );
00157       job->setFetchScope( scope );
00158       job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
00159       connect( job, SIGNAL( result( KJob* )), SLOT(processResult( KJob* ) ) );
00160       mCache.enqueue( node );
00161     }
00162 
00163   private:
00164     EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
00165     {
00166       for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
00167             it != endIt; ++it )
00168       {
00169         if ( (*it)->entity.id() == id )
00170           return *it;
00171       }
00172       return 0;
00173     }
00174 
00175     void processResult( KJob* job )
00176     {
00177       // Error handling?
00178       typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
00179       EntityCacheNode<T> *node = cacheNodeForId( id );
00180       if ( !node )
00181         return; // got replaced in the meantime
00182 
00183       node->pending = false;
00184       extractResult( node, job );
00185       // make sure we find this node again if something went wrong here,
00186       // most likely the object got deleted from the server in the meantime
00187       if ( node->entity.id() != id ) {
00188         // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true!
00189         node->entity.setId( id );
00190         node->invalid = true;
00191       }
00192       emit dataAvailable();
00193     }
00194 
00195     void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
00196 
00197     inline FetchJob* createFetchJob( typename T::Id id )
00198     {
00199       return new FetchJob( T( id ), session );
00200     }
00201 
00203     void shrinkCache()
00204     {
00205       while ( mCache.size() >= mCapacity && !mCache.first()->pending )
00206         delete mCache.dequeue();
00207     }
00208 
00209   private:
00210     QQueue<EntityCacheNode<T>*> mCache;
00211     int mCapacity;
00212 };
00213 
00214 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
00215 {
00216   CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
00217   Q_ASSERT( fetch );
00218   if ( fetch->collections().isEmpty() )
00219     node->entity = Collection();
00220   else
00221     node->entity = fetch->collections().first();
00222 }
00223 
00224 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
00225 {
00226   ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
00227   Q_ASSERT( fetch );
00228   if ( fetch->items().isEmpty() )
00229     node->entity = Item();
00230   else
00231     node->entity = fetch->items().first();
00232 }
00233 
00234 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
00235 {
00236   return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
00237 }
00238 
00239 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
00240 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
00241 
00242 
00243 template<typename T>
00244 class Comparator
00245 {
00246 public:
00247   static bool compare(const typename T::List &lhs_, const QList<typename T::Id> &rhs_ )
00248   {
00249     bool val = true;
00250 
00251     if (lhs_.size() != rhs_.size())
00252       return false;
00253 
00254     typename T::List lhs = lhs_;
00255     QList<typename T::Id> rhs = rhs_;
00256 
00257     qSort(lhs);
00258     qSort(rhs);
00259     return lhs == rhs;
00260   }
00261 
00262   static bool compare(const QList<typename T::Id> &l1, const typename T::List &l2)
00263   {
00264     return compare(l2, l1);
00265   }
00266 
00267   static bool compare(const typename T::List &l1, const typename T::List &l2)
00268   {
00269     typename T::List l1_ = l1;
00270     typename T::List l2_ = l2;
00271     qSort(l1_);
00272     qSort(l2_);
00273     return l1_ == l2_;
00274   }
00275 };
00276 
00277 
00278 template <typename T>
00279 struct EntityListCacheNode
00280 {
00281   EntityListCacheNode( const typename T::List &list ) : entityList( list ), pending( false ), invalid( false ) {}
00282   EntityListCacheNode( const QList<typename T::Id> &list ) : pending( false ), invalid( false ) {
00283     foreach( typename T::Id id, list)
00284       entityList.append(T(id));
00285   }
00286   typename T::List entityList;
00287   bool pending;
00288   bool invalid;
00289 };
00290 
00291 template<typename T, typename FetchJob, typename FetchScope_>
00292 class EntityListCache : public EntityCacheBase
00293 {
00294 public:
00295   typedef FetchScope_ FetchScope;
00296 
00297   explicit EntityListCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
00298     EntityCacheBase( session, parent ),
00299     mCapacity( maxCapacity )
00300   {}
00301 
00302   ~EntityListCache()
00303   {
00304     qDeleteAll( mCache );
00305   }
00306 
00308   template<typename TArg>
00309   typename T::List retrieve( const QList<TArg> &id ) const
00310   {
00311     EntityListCacheNode<T>* node = cacheNodeForId( id );
00312     if ( node && !node->pending && !node->invalid )
00313       return node->entityList;
00314     return typename T::List();
00315   }
00316 
00318   template<typename TArg>
00319   bool ensureCached( const QList<TArg> &id, const FetchScope &scope )
00320   {
00321     EntityListCacheNode<T>* node = cacheNodeForId( id );
00322     if ( !node ) {
00323       request( id, scope );
00324       return false;
00325     }
00326     return !node->pending;
00327   }
00328 
00330   template<typename TArg>
00331   void invalidate( const QList<TArg> &id )
00332   {
00333     EntityListCacheNode<T>* node = cacheNodeForId( id );
00334     if ( node )
00335       node->invalid = true;
00336   }
00337 
00339   template<typename TArg>
00340   void update( const QList<TArg> &id, const FetchScope &scope )
00341   {
00342     EntityListCacheNode<T>* node = cacheNodeForId( id );
00343     if ( node ) {
00344       mCache.removeAll( node );
00345       if ( node->pending )
00346         request( id, scope );
00347       delete node;
00348     }
00349   }
00350 
00351 
00353   template<typename TArg>
00354   bool isCached( const QList<TArg> &id ) const
00355   {
00356     EntityListCacheNode<T>* node = cacheNodeForId( id );
00357     return node && !node->pending;
00358   }
00359 
00360 private:
00361 
00362   typename T::List getTList( const QList<typename T::Id> &id )
00363   {
00364     typename T::List ids;
00365     foreach(typename T::Id id_, id)
00366       ids.append(T(id_));
00367     return ids;
00368   }
00369 
00370   typename T::List getTList( const typename T::List &id )
00371   {
00372     return id;
00373   }
00374 
00380   template<typename TArg>
00381   void request( const QList<TArg> &id, const FetchScope &scope )
00382   {
00383     Q_ASSERT( !isRequested( id ) );
00384     shrinkCache();
00385     EntityListCacheNode<T> *node = new EntityListCacheNode<T>( id );
00386     FetchJob* job = createFetchJob( id );
00387     job->setFetchScope( scope );
00388     job->setProperty( "EntityListCacheNode", QVariant::fromValue<typename T::List>( getTList( id ) ) );
00389     connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) );
00390     mCache.enqueue( node );
00391   }
00392 
00394   void shrinkCache()
00395   {
00396     while ( mCache.size() >= mCapacity && !mCache.first()->pending )
00397       delete mCache.dequeue();
00398   }
00399 
00401   template<typename TArg>
00402   bool isRequested( const QList<TArg> &id ) const
00403   {
00404     return cacheNodeForId( id );
00405   }
00406 
00407   template<typename TArg>
00408   EntityListCacheNode<T>* cacheNodeForId( const QList<TArg> &id ) const
00409   {
00410     for ( typename QQueue<EntityListCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
00411           it != endIt; ++it )
00412     {
00413       if ( Comparator<T>::compare( ( *it )->entityList, id ) )
00414         return *it;
00415     }
00416     return 0;
00417   }
00418 
00419   template<typename TArg>
00420   inline FetchJob* createFetchJob( const QList<TArg> &id )
00421   {
00422     return new FetchJob( id, session );
00423   }
00424 
00425   void processResult( KJob* job )
00426   {
00427     typename T::List ids = job->property( "EntityListCacheNode" ).template value<typename T::List>();
00428 
00429     EntityListCacheNode<T> *node = cacheNodeForId( ids );
00430     if ( !node )
00431       return; // got replaced in the meantime
00432 
00433     node->pending = false;
00434     extractResult( node, job );
00435     // make sure we find this node again if something went wrong here,
00436     // most likely the object got deleted from the server in the meantime
00437     if ( node->entityList != ids ) {
00438       node->entityList = ids;
00439       node->invalid = true;
00440     }
00441     emit dataAvailable();
00442   }
00443 
00444   void extractResult( EntityListCacheNode<T>* node, KJob* job ) const;
00445 
00446 
00447 private:
00448   QQueue<EntityListCacheNode<T>*> mCache;
00449   int mCapacity;
00450 };
00451 
00452 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityListCacheNode<Collection>* node, KJob *job ) const
00453 {
00454   CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
00455   Q_ASSERT( fetch );
00456   node->entityList = fetch->collections();
00457 }
00458 
00459 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityListCacheNode<Item>* node, KJob *job ) const
00460 {
00461   ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
00462   Q_ASSERT( fetch );
00463   node->entityList = fetch->items();
00464 }
00465 
00466 template<>
00467 template<typename TArg>
00468 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( const QList<TArg> &id )
00469 {
00470   return new CollectionFetchJob( id, CollectionFetchJob::Base, session );
00471 }
00472 
00473 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
00474 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
00475 
00476 }
00477 
00478 #endif

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • 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
Generated for KDE-PIM Libraries by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal