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 "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 fetchResult( 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 explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
00083 EntityCacheBase( session, parent ),
00084 mCapacity( maxCapacity )
00085 {}
00086
00087 ~EntityCache()
00088 {
00089 qDeleteAll( mCache );
00090 }
00091
00093 bool isCached( typename T::Id id ) const
00094 {
00095 EntityCacheNode<T>* node = cacheNodeForId( id );
00096 return node && !node->pending;
00097 }
00098
00100 bool isRequested( typename T::Id id ) const
00101 {
00102 return cacheNodeForId( id );
00103 }
00104
00106 T retrieve( typename T::Id id ) const
00107 {
00108 EntityCacheNode<T>* node = cacheNodeForId( id );
00109 if ( node && !node->pending && !node->invalid )
00110 return node->entity;
00111 return T();
00112 }
00113
00115 void invalidate( typename T::Id id )
00116 {
00117 EntityCacheNode<T>* node = cacheNodeForId( id );
00118 if ( node )
00119 node->invalid = true;
00120 }
00121
00123 void update( typename T::Id id, const FetchScope &scope )
00124 {
00125 EntityCacheNode<T>* node = cacheNodeForId( id );
00126 if ( node ) {
00127 mCache.removeAll( node );
00128 if ( node->pending )
00129 request( id, scope );
00130 delete node;
00131 }
00132 }
00133
00135 bool ensureCached( typename T::Id id, const FetchScope &scope )
00136 {
00137 EntityCacheNode<T>* node = cacheNodeForId( id );
00138 if ( !node ) {
00139 request( id, scope );
00140 return false;
00141 }
00142 return !node->pending;
00143 }
00144
00150 void request( typename T::Id id, const FetchScope &scope )
00151 {
00152 Q_ASSERT( !isRequested( id ) );
00153 shrinkCache();
00154 EntityCacheNode<T> *node = new EntityCacheNode<T>( id );
00155 FetchJob* job = createFetchJob( id );
00156 job->setFetchScope( scope );
00157 job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) );
00158 connect( job, SIGNAL( result( KJob* ) ), SLOT( fetchResult( KJob* ) ) );
00159 mCache.enqueue( node );
00160 }
00161
00162 private:
00163 EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const
00164 {
00165 for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
00166 it != endIt; ++it )
00167 {
00168 if ( (*it)->entity.id() == id )
00169 return *it;
00170 }
00171 return 0;
00172 }
00173
00174 void fetchResult( KJob* job )
00175 {
00176 typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>();
00177 EntityCacheNode<T> *node = cacheNodeForId( id );
00178 if ( !node )
00179 return;
00180
00181 node->pending = false;
00182 extractResult( node, job );
00183
00184
00185 if ( node->entity.id() != id ) {
00186 node->entity.setId( id );
00187 node->invalid = true;
00188 }
00189 emit dataAvailable();
00190 }
00191
00192 void extractResult( EntityCacheNode<T>* node, KJob* job ) const;
00193
00194 inline FetchJob* createFetchJob( typename T::Id id )
00195 {
00196 return new FetchJob( T( id ), session );
00197 }
00198
00200 void shrinkCache()
00201 {
00202 while ( mCache.size() >= mCapacity && !mCache.first()->pending )
00203 delete mCache.dequeue();
00204 }
00205
00206 private:
00207 QQueue<EntityCacheNode<T>*> mCache;
00208 int mCapacity;
00209 };
00210
00211 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const
00212 {
00213 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
00214 Q_ASSERT( fetch );
00215 if ( fetch->collections().isEmpty() )
00216 node->entity = Collection();
00217 else
00218 node->entity = fetch->collections().first();
00219 }
00220
00221 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const
00222 {
00223 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
00224 Q_ASSERT( fetch );
00225 if ( fetch->items().isEmpty() )
00226 node->entity = Item();
00227 else
00228 node->entity = fetch->items().first();
00229 }
00230
00231 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id )
00232 {
00233 return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session );
00234 }
00235
00236 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
00237 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
00238
00239 }
00240
00241 #endif