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"
41#include <QtCore/QQueue>
46typedef QList<Akonadi::Entity::Id> EntityIdList;
47Q_DECLARE_METATYPE(QList<Akonadi::Entity::Id>)
61 void setSession(
Session *session);
70 virtual void processResult(KJob *job) = 0;
81 EntityCacheNode(
typename T::Id
id)
96template<
typename T,
typename FetchJob,
typename FetchScope_>
100 typedef FetchScope_ FetchScope;
103 , mCapacity(maxCapacity)
115 EntityCacheNode<T> *node = cacheNodeForId(
id);
116 return node && !node->pending;
122 return cacheNodeForId(
id);
128 EntityCacheNode<T> *node = cacheNodeForId(
id);
129 if (node && !node->pending && !node->invalid) {
138 EntityCacheNode<T> *node = cacheNodeForId(
id);
140 node->invalid =
true;
145 void update(
typename T::Id
id,
const FetchScope &scope)
147 EntityCacheNode<T> *node = cacheNodeForId(
id);
149 mCache.removeAll(node);
160 EntityCacheNode<T> *node = cacheNodeForId(
id);
165 return !node->pending;
173 virtual void request(
typename T::Id
id,
const FetchScope &scope)
177 EntityCacheNode<T> *node =
new EntityCacheNode<T>(
id);
178 FetchJob *job = createFetchJob(
id, scope);
179 job->setProperty(
"EntityCacheNode", QVariant::fromValue<typename T::Id>(
id));
180 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
181 mCache.enqueue(node);
185 EntityCacheNode<T> *cacheNodeForId(
typename T::Id
id)
const
187 for (
typename QQueue<EntityCacheNode<T> *>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd();
189 if ((*it)->entity.id() ==
id) {
196 void processResult(KJob *job)
198 typename T::Id
id = job->property(
"EntityCacheNode").template value<typename T::Id>();
200 if ( job->error() ) {
201 kWarning() << job->errorString();
203 EntityCacheNode<T> *node = cacheNodeForId(
id);
208 node->pending =
false;
209 extractResult(node, job);
212 if (node->entity.id() !=
id) {
214 node->entity.setId(
id);
215 node->invalid =
true;
217 emit dataAvailable();
220 void extractResult(EntityCacheNode<T> *node, KJob *job)
const;
222 inline FetchJob *createFetchJob(
typename T::Id
id,
const FetchScope &scope)
224 FetchJob *fetch =
new FetchJob(T(
id), session);
225 fetch->setFetchScope(scope);
232 while (mCache.size() >= mCapacity && !mCache.first()->pending) {
233 delete mCache.dequeue();
238 QQueue<EntityCacheNode<T> *> mCache;
242template<>
inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult(EntityCacheNode<Collection> *node, KJob *job)
const
244 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
246 if (fetch->collections().isEmpty()) {
247 node->entity = Collection();
249 node->entity = fetch->collections().first();
253template<>
inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult(EntityCacheNode<Item> *node, KJob *job)
const
255 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
257 if (fetch->items().isEmpty()) {
258 node->entity = Item();
260 node->entity = fetch->items().first();
264template<>
inline void EntityCache<Tag, TagFetchJob, TagFetchScope>::extractResult(EntityCacheNode<Tag> *node, KJob *job)
const
266 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
268 if (fetch->tags().isEmpty()) {
269 node->entity = Tag();
271 node->entity = fetch->tags().first();
275template<>
inline CollectionFetchJob *EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(
Collection::Id id,
const CollectionFetchScope &scope)
278 fetch->setFetchScope(scope);
282typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
283typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
284typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
287struct EntityListCacheNode
289 EntityListCacheNode()
294 EntityListCacheNode(
typename T::Id
id)
306template<
typename T,
typename FetchJob,
typename FetchScope_>
307class EntityListCache :
public EntityCacheBase
310 typedef FetchScope_ FetchScope;
312 explicit EntityListCache(
int maxCapacity, Session *session = 0, QObject *parent = 0)
313 : EntityCacheBase(session, parent)
314 , mCapacity(maxCapacity)
324 typename T::List retrieve(
const QList<Entity::Id> &ids)
const
326 typename T::List list;
329 EntityListCacheNode<T> *node = mCache.value(
id);
330 if (!node || node->pending || node->invalid) {
331 return typename T::List();
334 list << node->entity;
341 bool ensureCached(
const QList<Entity::Id> &ids,
const FetchScope &scope)
343 QList<Entity::Id> toRequest;
347 EntityListCacheNode<T> *node = mCache.value(
id);
358 if (!toRequest.isEmpty()) {
359 request(toRequest, scope, ids);
367 void invalidate(
const QList<Entity::Id> &ids)
370 EntityListCacheNode<T> *node = mCache.value(
id);
372 node->invalid =
true;
378 void update(
const QList<Entity::Id> &ids,
const FetchScope &scope)
380 QList<Entity::Id> toRequest;
383 EntityListCacheNode<T> *node = mCache.value(
id);
393 if (!toRequest.isEmpty()) {
394 request(toRequest, scope);
403 void request(
const QList<Entity::Id> &ids,
const FetchScope &scope,
const QList<Entity::Id> &preserveIds = QList<Entity::Id>())
405 Q_ASSERT(isNotRequested(ids));
406 shrinkCache(preserveIds);
408 EntityListCacheNode<T> *node =
new EntityListCacheNode<T>(
id);
409 mCache.insert(
id, node);
411 FetchJob *job = createFetchJob(ids, scope);
412 job->setProperty(
"EntityListCacheIds", QVariant::fromValue< QList<Entity::Id> >(ids));
413 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
416 bool isNotRequested(
const QList<Entity::Id> &ids)
const
419 if (mCache.contains(
id)) {
428 bool isCached(
const QList<Entity::Id> &ids)
const
431 EntityListCacheNode<T> *node = mCache.value(
id);
432 if (!node || node->pending) {
441 void shrinkCache(
const QList<Entity::Id> &preserveIds)
444 QHash< Entity::Id, EntityListCacheNode<T> *>::Iterator iter = mCache.begin();
445 while (iter != mCache.end() && mCache.size() >= mCapacity) {
446 if (iter.value()->pending || preserveIds.contains(iter.key())) {
452 iter = mCache.erase(iter);
456 inline FetchJob *createFetchJob(
const QList<Entity::Id> &ids,
const FetchScope &scope)
458 FetchJob *job =
new FetchJob(ids, session);
459 job->setFetchScope(scope);
463 void processResult(KJob *job)
465 if ( job->error() ) {
466 kWarning() << job->errorString();
468 const QList<Entity::Id> ids = job->property(
"EntityListCacheIds").value< QList<Entity::Id> >();
470 typename T::List entities;
471 extractResults(job, entities);
474 EntityListCacheNode<T> *node = mCache.value(
id);
479 node->pending =
false;
482 typename T::List::Iterator iter = entities.begin();
483 for (; iter != entities.end(); ++iter) {
484 if ((*iter).id() ==
id) {
486 entities.erase(iter);
493 if (!result.isValid()) {
494 node->entity = T(
id);
495 node->invalid =
true;
497 node->entity = result;
501 emit dataAvailable();
504 void extractResults(KJob *job,
typename T::List &entities)
const;
507 QHash< Entity::Id, EntityListCacheNode<T> *> mCache;
511template<>
inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults(KJob *job,
Collection::List &collections)
const
513 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
515 collections = fetch->collections();
518template<>
inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults(KJob *job, Item::List &items)
const
520 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
522 items = fetch->items();
525template<>
inline void EntityListCache<Tag, TagFetchJob, TagFetchScope>::extractResults(KJob *job, Tag::List &tags)
const
527 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
529 tags = fetch->tags();
533inline CollectionFetchJob *EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(
const QList<Entity::Id> &ids,
const CollectionFetchScope &scope)
536 fetch->setFetchScope(scope);
540typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
541typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
542typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
@ Base
Only fetch the base collection.
QList< Collection > List
Describes a list of collections.
virtual T retrieve(typename T::Id id) const
Returns the cached object if available, an empty instance otherwise.
bool isRequested(typename T::Id id) const
Object has been requested but is not yet loaded into the cache or is already available.
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 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.
void invalidate(typename T::Id id)
Marks the cache entry as invalid, use in case the object has been deleted on the server.
virtual void request(typename T::Id id, const FetchScope &scope)
Asks the cache to retrieve id.
qint64 Id
Describes the unique id type.
A communication session with the Akonadi storage.
FreeBusyManager::Singleton.