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/tag.h>
30 #include <akonadi/tagfetchjob.h>
31 #include <akonadi/tagfetchscope.h>
32 #include <akonadi/session.h>
34 #include "akonadiprivate_export.h"
40 #include <QtCore/QQueue>
45 typedef QList<Akonadi::Entity::Id> EntityIdList;
46 Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
60 void setSession(
Session *session);
69 virtual void processResult(KJob *job) = 0;
73 struct EntityCacheNode
80 EntityCacheNode(
typename T::Id
id)
95 template<
typename T,
typename FetchJob,
typename FetchScope_>
99 typedef FetchScope_ FetchScope;
102 , mCapacity(maxCapacity)
114 EntityCacheNode<T> *node = cacheNodeForId(
id);
115 return node && !node->pending;
121 return cacheNodeForId(
id);
127 EntityCacheNode<T> *node = cacheNodeForId(
id);
128 if (node && !node->pending && !node->invalid) {
137 EntityCacheNode<T> *node = cacheNodeForId(
id);
139 node->invalid =
true;
144 void update(
typename T::Id
id,
const FetchScope &scope)
146 EntityCacheNode<T> *node = cacheNodeForId(
id);
148 mCache.removeAll(node);
159 EntityCacheNode<T> *node = cacheNodeForId(
id);
164 return !node->pending;
172 virtual void request(
typename T::Id
id,
const FetchScope &scope)
174 Q_ASSERT(!isRequested(
id));
176 EntityCacheNode<T> *node =
new EntityCacheNode<T>(id);
177 FetchJob *job = createFetchJob(
id, scope);
178 job->setProperty(
"EntityCacheNode", QVariant::fromValue<typename T::Id>(
id));
179 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
180 mCache.enqueue(node);
184 EntityCacheNode<T> *cacheNodeForId(
typename T::Id
id)
const
186 for (
typename QQueue<EntityCacheNode<T> *>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
188 if ((*it)->entity.id() == id) {
195 void processResult(KJob *job)
198 typename T::Id
id = job->property(
"EntityCacheNode").template value<typename T::Id>();
199 EntityCacheNode<T> *node = cacheNodeForId(
id);
204 node->pending =
false;
205 extractResult(node, job);
208 if (node->entity.id() != id) {
210 node->entity.setId(
id);
211 node->invalid =
true;
213 emit dataAvailable();
216 void extractResult(EntityCacheNode<T> *node, KJob *job)
const;
218 inline FetchJob *createFetchJob(
typename T::Id
id,
const FetchScope &scope)
220 FetchJob *fetch =
new FetchJob(T(
id), session);
221 fetch->setFetchScope(scope);
228 while (mCache.size() >= mCapacity && !mCache.first()->pending) {
229 delete mCache.dequeue();
234 QQueue<EntityCacheNode<T> *> mCache;
238 template<>
inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult(EntityCacheNode<Collection> *node, KJob *job)
const
240 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
242 if (fetch->collections().isEmpty()) {
243 node->entity = Collection();
245 node->entity = fetch->collections().first();
249 template<>
inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult(EntityCacheNode<Item> *node, KJob *job)
const
251 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
253 if (fetch->items().isEmpty()) {
254 node->entity = Item();
256 node->entity = fetch->items().first();
260 template<>
inline void EntityCache<Tag, TagFetchJob, TagFetchScope>::extractResult(EntityCacheNode<Tag> *node, KJob *job)
const
262 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
264 if (fetch->tags().isEmpty()) {
265 node->entity = Tag();
267 node->entity = fetch->tags().first();
271 template<>
inline CollectionFetchJob *EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(Collection::Id
id,
const CollectionFetchScope &scope)
273 CollectionFetchJob *fetch =
new CollectionFetchJob(Collection(
id), CollectionFetchJob::Base, session);
274 fetch->setFetchScope(scope);
278 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
279 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
280 typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
282 template <
typename T>
283 struct EntityListCacheNode
285 EntityListCacheNode()
290 EntityListCacheNode(
typename T::Id
id)
302 template<
typename T,
typename FetchJob,
typename FetchScope_>
303 class EntityListCache :
public EntityCacheBase
306 typedef FetchScope_ FetchScope;
308 explicit EntityListCache(
int maxCapacity, Session *session = 0, QObject *parent = 0)
309 : EntityCacheBase(session, parent)
310 , mCapacity(maxCapacity)
320 typename T::List retrieve(
const QList<Entity::Id> &ids)
const
322 typename T::List list;
324 foreach (Entity::Id
id, ids) {
325 EntityListCacheNode<T> *node = mCache.value(
id);
326 if (!node || node->pending || node->invalid) {
327 return typename T::List();
330 list << node->entity;
337 bool ensureCached(
const QList<Entity::Id> &ids,
const FetchScope &scope)
339 QList<Entity::Id> toRequest;
342 foreach (Entity::Id
id, ids) {
343 EntityListCacheNode<T> *node = mCache.value(
id);
354 if (!toRequest.isEmpty()) {
355 request(toRequest, scope, ids);
363 void invalidate(
const QList<Entity::Id> &ids)
365 foreach (Entity::Id
id, ids) {
366 EntityListCacheNode<T> *node = mCache.value(
id);
368 node->invalid =
true;
374 void update(
const QList<Entity::Id> &ids,
const FetchScope &scope)
376 QList<Entity::Id> toRequest;
378 foreach (Entity::Id
id, ids) {
379 EntityListCacheNode<T> *node = mCache.value(
id);
389 if (!toRequest.isEmpty()) {
390 request(toRequest, scope);
399 void request(
const QList<Entity::Id> &ids,
const FetchScope &scope,
const QList<Entity::Id> &preserveIds = QList<Entity::Id>())
401 Q_ASSERT(isNotRequested(ids));
402 shrinkCache(preserveIds);
403 foreach (Entity::Id
id, ids) {
404 EntityListCacheNode<T> *node =
new EntityListCacheNode<T>(id);
405 mCache.insert(
id, node);
407 FetchJob *job = createFetchJob(ids, scope);
408 job->setProperty(
"EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >(ids));
409 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
412 bool isNotRequested(
const QList<Entity::Id> &ids)
const
414 foreach (Entity::Id
id, ids) {
415 if (mCache.contains(
id)) {
424 bool isCached(
const QList<Entity::Id> &ids)
const
426 foreach (Entity::Id
id, ids) {
427 EntityListCacheNode<T> *node = mCache.value(
id);
428 if (!node || node->pending) {
437 void shrinkCache(
const QList<Entity::Id> &preserveIds)
440 QHash< Entity::Id, EntityListCacheNode<T> *>::Iterator iter = mCache.begin();
441 while (iter != mCache.end() && mCache.size() >= mCapacity) {
442 if (iter.value()->pending || preserveIds.contains(iter.key())) {
448 iter = mCache.erase(iter);
452 inline FetchJob *createFetchJob(
const QList<Entity::Id> &ids,
const FetchScope &scope)
454 FetchJob *job =
new FetchJob(ids, session);
455 job->setFetchScope(scope);
459 void processResult(KJob *job)
461 const QList<Entity::Id> ids = job->property(
"EntityListCacheIds").value< QList<Entity::Id> >();
463 typename T::List entities;
464 extractResults(job, entities);
466 foreach (Entity::Id
id, ids) {
467 EntityListCacheNode<T> *node = mCache.value(
id);
472 node->pending =
false;
475 typename T::List::Iterator iter = entities.begin();
476 for (; iter != entities.end(); ++iter) {
477 if ((*iter).id() == id) {
479 entities.erase(iter);
486 if (!result.isValid()) {
487 node->entity = T(
id);
488 node->invalid =
true;
490 node->entity = result;
494 emit dataAvailable();
497 void extractResults(KJob *job,
typename T::List &entities)
const;
500 QHash< Entity::Id, EntityListCacheNode<T> *> mCache;
504 template<>
inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults(KJob *job, Collection::List &collections)
const
506 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
508 collections = fetch->collections();
511 template<>
inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults(KJob *job, Item::List &items)
const
513 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
515 items = fetch->items();
518 template<>
inline void EntityListCache<Tag, TagFetchJob, TagFetchScope>::extractResults(KJob *job, Tag::List &tags)
const
520 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
522 tags = fetch->tags();
526 inline CollectionFetchJob *EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(
const QList<Entity::Id> &ids,
const CollectionFetchScope &scope)
528 CollectionFetchJob *fetch =
new CollectionFetchJob(ids, CollectionFetchJob::Base, session);
529 fetch->setFetchScope(scope);
533 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
534 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
535 typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
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.