20 #ifndef AKONADI_ENTITYCACHE_P_H
21 #define AKONADI_ENTITYCACHE_P_H
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>
31 #include "akonadiprivate_export.h"
37 #include <QtCore/QQueue>
41 typedef QList<Akonadi::Entity::Id> EntityIdList;
42 Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
56 void setSession(
Session *session);
65 virtual void processResult( KJob* job ) = 0;
69 struct EntityCacheNode
71 EntityCacheNode() : pending( false ), invalid( false ) {}
72 EntityCacheNode(
typename T::Id
id ) : entity( T( id ) ), pending( true ), invalid( false ) {}
82 template<
typename T,
typename FetchJob,
typename FetchScope_>
86 typedef FetchScope_ FetchScope;
89 mCapacity( maxCapacity )
100 EntityCacheNode<T>* node = cacheNodeForId(
id );
101 return node && !node->pending;
107 return cacheNodeForId(
id );
113 EntityCacheNode<T>* node = cacheNodeForId(
id );
114 if ( node && !node->pending && !node->invalid ) {
123 EntityCacheNode<T>* node = cacheNodeForId(
id );
125 node->invalid =
true;
130 void update(
typename T::Id
id,
const FetchScope &scope )
132 EntityCacheNode<T>* node = cacheNodeForId(
id );
134 mCache.removeAll( node );
135 if ( node->pending ) {
136 request(
id, scope );
143 virtual bool ensureCached(
typename T::Id
id,
const FetchScope &scope )
145 EntityCacheNode<T>* node = cacheNodeForId(
id );
147 request(
id, scope );
150 return !node->pending;
158 virtual void request(
typename T::Id
id,
const FetchScope &scope )
160 Q_ASSERT( !isRequested(
id ) );
162 EntityCacheNode<T> *node =
new EntityCacheNode<T>( id );
163 FetchJob* job = createFetchJob(
id );
164 job->setFetchScope( scope );
165 job->setProperty(
"EntityCacheNode", QVariant::fromValue<typename T::Id>(
id ) );
166 connect( job, SIGNAL( result( KJob* )), SLOT(processResult( KJob* ) ) );
167 mCache.enqueue( node );
171 EntityCacheNode<T>* cacheNodeForId(
typename T::Id
id )
const
173 for (
typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
174 it != endIt; ++it ) {
175 if ( ( *it )->entity.id() == id ) {
182 void processResult( KJob* job )
185 typename T::Id
id = job->property(
"EntityCacheNode" ).template value<typename T::Id>();
186 EntityCacheNode<T> *node = cacheNodeForId(
id );
191 node->pending =
false;
192 extractResult( node, job );
195 if ( node->entity.id() != id ) {
197 node->entity.setId(
id );
198 node->invalid =
true;
200 emit dataAvailable();
203 void extractResult( EntityCacheNode<T>* node, KJob* job )
const;
205 inline FetchJob* createFetchJob(
typename T::Id
id )
207 return new FetchJob( T(
id ), session );
213 while ( mCache.size() >= mCapacity && !mCache.first()->pending ) {
214 delete mCache.dequeue();
219 QQueue<EntityCacheNode<T>*> mCache;
223 template<>
inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job )
const
225 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
227 if ( fetch->collections().isEmpty() ) {
228 node->entity = Collection();
230 node->entity = fetch->collections().first();
234 template<>
inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job )
const
236 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
238 if ( fetch->items().isEmpty() ) {
239 node->entity = Item();
241 node->entity = fetch->items().first();
245 template<>
inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id
id )
247 return new CollectionFetchJob( Collection(
id ), CollectionFetchJob::Base, session );
250 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
251 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
253 template <
typename T>
254 struct EntityListCacheNode
256 EntityListCacheNode() : pending( false ), invalid( false ) {}
257 EntityListCacheNode(
typename T::Id
id ) : entity(id), pending( true ), invalid( false ) {}
264 template<
typename T,
typename FetchJob,
typename FetchScope_>
265 class EntityListCache :
public EntityCacheBase
268 typedef FetchScope_ FetchScope;
270 explicit EntityListCache(
int maxCapacity, Session *session = 0, QObject *parent = 0 ) :
271 EntityCacheBase( session, parent ),
272 mCapacity( maxCapacity )
277 qDeleteAll( mCache );
281 typename T::List retrieve(
const QList<Entity::Id> &ids )
const
283 typename T::List list;
285 foreach( Entity::Id
id, ids ) {
286 EntityListCacheNode<T>* node = mCache.value(
id );
287 if ( !node || node->pending || node->invalid )
288 return typename T::List();
290 list << node->entity;
297 bool ensureCached(
const QList<Entity::Id> &ids,
const FetchScope &scope )
299 QList<Entity::Id> toRequest;
302 foreach( Entity::Id
id, ids ) {
303 EntityListCacheNode<T>* node = mCache.value(
id );
313 if ( !toRequest.isEmpty() ) {
314 request( toRequest, scope, ids );
322 void invalidate(
const QList<Entity::Id> &ids )
324 foreach( Entity::Id
id, ids ) {
325 EntityListCacheNode<T>* node = mCache.value(
id );
327 node->invalid =
true;
333 void update(
const QList<Entity::Id> &ids,
const FetchScope &scope )
335 QList<Entity::Id> toRequest;
337 foreach( Entity::Id
id, ids ) {
338 EntityListCacheNode<T>* node = mCache.value(
id );
341 if ( node->pending ) {
348 if ( !toRequest.isEmpty() ) {
349 request( toRequest, scope );
358 void request(
const QList<Entity::Id> &ids,
const FetchScope &scope,
const QList<Entity::Id> &preserveIds = QList<Entity::Id>() )
360 Q_ASSERT( isNotRequested( ids ) );
361 shrinkCache( preserveIds );
362 foreach( Entity::Id
id, ids ) {
363 EntityListCacheNode<T> *node =
new EntityListCacheNode<T>( id );
364 mCache.insert(
id, node );
366 FetchJob* job = createFetchJob( ids );
367 job->setFetchScope( scope );
368 job->setProperty(
"EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >( ids ) );
369 connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) );
372 bool isNotRequested(
const QList<Entity::Id> &ids )
const
374 foreach( Entity::Id
id, ids ) {
375 if ( mCache.contains(
id ) )
383 bool isCached(
const QList<Entity::Id> &ids )
const
385 foreach( Entity::Id
id, ids ) {
386 EntityListCacheNode<T>* node = mCache.value(
id );
387 if ( !node || node->pending ) {
396 void shrinkCache(
const QList<Entity::Id> &preserveIds )
399 QHash< Entity::Id, EntityListCacheNode<T>* >::Iterator iter = mCache.begin();
400 while ( iter != mCache.end() && mCache.size() >= mCapacity ) {
401 if ( iter.value()->pending || preserveIds.contains( iter.key() ) ) {
407 iter = mCache.erase( iter );
411 inline FetchJob* createFetchJob(
const QList<Entity::Id> &ids )
413 return new FetchJob( ids, session );
416 void processResult( KJob* job )
418 const QList<Entity::Id> ids = job->property(
"EntityListCacheIds" ).value< QList<Entity::Id> >();
420 typename T::List entities;
421 extractResults( job, entities );
423 foreach( Entity::Id
id, ids ) {
424 EntityListCacheNode<T> *node = mCache.value(
id );
429 node->pending =
false;
432 typename T::List::Iterator iter = entities.begin();
433 for ( ; iter != entities.end(); ++iter ) {
434 if ( (*iter).id() == id ) {
436 entities.erase( iter );
443 if ( !result.isValid() ) {
444 node->entity = T(
id );
445 node->invalid =
true;
447 node->entity = result;
451 emit dataAvailable();
454 void extractResults( KJob* job,
typename T::List &entities )
const;
457 QHash< Entity::Id, EntityListCacheNode<T>* > mCache;
461 template<>
inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults( KJob *job, Collection::List &collections )
const
463 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job );
465 collections = fetch->collections();
468 template<>
inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults( KJob *job, Item::List &items )
const
470 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job );
472 items = fetch->items();
476 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(
const QList<Entity::Id> &ids )
478 return new CollectionFetchJob( ids, CollectionFetchJob::Base, session );
481 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
482 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
virtual bool ensureCached(typename T::Id id, const FetchScope &scope)
Requests the object to be cached if it is not yet in the cache.
void invalidate(typename T::Id id)
Marks the cache entry as invalid, use in case the object has been deleted on the server.
void update(typename T::Id id, const FetchScope &scope)
Triggers a re-fetching of a cache entry, use if it has changed on the server.
bool isCached(typename T::Id id) const
Object is available in the cache and can be retrieved.
A communication session with the Akonadi storage.
bool isRequested(typename T::Id id) const
Object has been requested but is not yet loaded into the cache or is already available.
virtual void request(typename T::Id id, const FetchScope &scope)
Asks the cache to retrieve id.
virtual T retrieve(typename T::Id id) const
Returns the cached object if available, an empty instance otherwise.