akonadi
entitycache_p.h
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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 <qobject.h>
00032 #include <QQueue>
00033 #include <QVariant>
00034 #include <QtCore/QQueue>
00035
00036 class KJob;
00037
00038 namespace Akonadi {
00039
00044 class EntityCacheBase : public QObject
00045 {
00046 Q_OBJECT
00047 public:
00048 explicit EntityCacheBase ( Session *session, QObject * parent = 0 );
00049
00050 void setSession(Session *session);
00051
00052 protected:
00053 Session *session;
00054
00055 signals:
00056 void dataAvailable();
00057
00058 private slots:
00059 virtual void fetchResult( KJob* job ) = 0;
00060 };
00061
00062 template <typename T>
00063 struct EntityCacheNode
00064 {
00065 EntityCacheNode() : pending( false ), invalid( false ) {}
00066 EntityCacheNode( typename T::Id id ) : entity( T( id ) ), pending( true ), invalid( false ) {}
00067 T entity;
00068 bool pending;
00069 bool invalid;
00070 };
00071
00076 template<typename T, typename FetchJob, typename FetchScope>
00077 class EntityCache : public EntityCacheBase
00078 {
00079 public:
00080 explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
00081 EntityCacheBase( session, parent ),
00082 mCapacity( maxCapacity )
00083 {}
00084
00085 ~EntityCache()
00086 {
00087 qDeleteAll( mCache );
00088 }
00089
00091 bool isCached( typename T::Id id ) const
00092 {
00093 EntityCacheNode<T>* node = cacheNodeForId( id );
00094 return node && !node->pending;
00095 }
00096
00098 bool isRequested( typename T::Id id ) const
00099 {
00100 return cacheNodeForId( id );
00101 }
00102
00104 T retrieve( typename T::Id id ) const
00105 {
00106 EntityCacheNode<T>* node = cacheNodeForId( id );
00107 if ( node && !node->pending && !node->invalid )
00108 return node->entity;
00109 return T();
00110 }
00111
00113 void invalidate( typename T::Id id )
00114 {
00115 EntityCacheNode<T>* node = cacheNodeForId( id );
00116 if ( node )
00117 node->invalid = true;
00118 }
00119
00121 void update( typename T::Id id, const FetchScope &scope )
00122 {
00123 EntityCacheNode<T>* node = cacheNodeForId( id );
00124 if ( node ) {
00125 mCache.removeAll( node );
00126 if ( node->pending )
00127 request( id, scope );
00128 delete node;
00129 }
00130 }
00131
00133 bool ensureCached( typename T::Id id, const FetchScope &scope )
00134 {
00135 EntityCacheNode<T>* node = cacheNodeForId( id );
00136 if ( !node ) {
00137 request( id, scope );
00138 return false;
00139 }
00140 return !node->pending;
00141 }
00142
00148 void request( typename T::Id id, const FetchScope &scope )
00149 {
00150 Q_ASSERT( !isRequested( id ) );
00151 shrinkCache();
00152 EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
00153 FetchJob* job = createFetchJob( id );
00154 job->setFetchScope( scope );
00155 job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
00156 connect( job, SIGNAL(result(KJob*)), SLOT(fetchResult(KJob*)) );
00157 mCache.enqueue( node );
00158 }
00159
00160 private:
00161 EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
00162 {
00163 for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
00164 it != endIt; ++it )
00165 {
00166 if ( (*it)->entity.id() == id )
00167 return *it;
00168 }
00169 return 0;
00170 }
00171
00172 void fetchResult( KJob* job )
00173 {
00174 typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
00175 EntityCacheNode<T> *node = cacheNodeForId( id );
00176 if ( !node )
00177 return;
00178
00179 node->pending = false;
00180 extractResult( node, job );
00181
00182
00183 if ( node->entity.id() != id ) {
00184 node->entity.setId( id );
00185 node->invalid = true;
00186 }
00187 emit dataAvailable();
00188 }
00189
00190 void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
00191
00192 inline FetchJob* createFetchJob( typename T::Id id )
00193 {
00194 return new FetchJob( T( id ), session );
00195 }
00196
00198 void shrinkCache()
00199 {
00200 while ( mCache.size() >= mCapacity && !mCache.first()->pending )
00201 delete mCache.dequeue();
00202 }
00203
00204 private:
00205 QQueue<EntityCacheNode<T>*> mCache;
00206 int mCapacity;
00207 };
00208
00209 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
00210 {
00211 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
00212 Q_ASSERT( fetch );
00213 if ( fetch->collections().isEmpty() )
00214 node->entity = Collection();
00215 else
00216 node->entity = fetch->collections().first();
00217 }
00218
00219 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
00220 {
00221 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
00222 Q_ASSERT( fetch );
00223 if ( fetch->items().isEmpty() )
00224 node->entity = Item();
00225 else
00226 node->entity = fetch->items().first();
00227 }
00228
00229 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
00230 {
00231 return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
00232 }
00233
00234 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
00235 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
00236
00237 }
00238
00239 #endif