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>
61 void setSession(
Session *session);
70 virtual void processResult(KJob *job) = 0;
74 struct EntityCacheNode
81 EntityCacheNode(
typename T::Id
id)
96 template<
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)
175 Q_ASSERT(!isRequested(
id));
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();
242 template<>
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();
253 template<>
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();
264 template<>
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();
275 template<>
inline CollectionFetchJob *EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(Collection::Id
id,
const CollectionFetchScope &scope)
277 CollectionFetchJob *fetch =
new CollectionFetchJob(Collection(
id), CollectionFetchJob::Base, session);
278 fetch->setFetchScope(scope);
282 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache;
283 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache;
284 typedef EntityCache<Tag, TagFetchJob, TagFetchScope> TagCache;
286 template <
typename T>
287 struct EntityListCacheNode
289 EntityListCacheNode()
294 EntityListCacheNode(
typename T::Id
id)
306 template<
typename T,
typename FetchJob,
typename FetchScope_>
307 class 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)
326 typename T::List list;
328 foreach (Entity::Id
id, ids) {
329 EntityListCacheNode<T> *node = mCache.value(
id);
330 if (!node || node->pending || node->invalid) {
331 return typename T::List();
334 list << node->entity;
346 foreach (Entity::Id
id, ids) {
347 EntityListCacheNode<T> *node = mCache.value(
id);
359 request(toRequest, scope, ids);
369 foreach (Entity::Id
id, ids) {
370 EntityListCacheNode<T> *node = mCache.value(
id);
372 node->invalid =
true;
382 foreach (Entity::Id
id, ids) {
383 EntityListCacheNode<T> *node = mCache.value(
id);
394 request(toRequest, scope);
405 Q_ASSERT(isNotRequested(ids));
406 shrinkCache(preserveIds);
407 foreach (Entity::Id
id, ids) {
408 EntityListCacheNode<T> *node =
new EntityListCacheNode<T>(id);
409 mCache.insert(
id, node);
411 FetchJob *job = createFetchJob(ids, scope);
413 connect(job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)));
418 foreach (Entity::Id
id, ids) {
419 if (mCache.contains(
id)) {
430 foreach (Entity::Id
id, ids) {
431 EntityListCacheNode<T> *node = mCache.value(
id);
432 if (!node || node->pending) {
445 while (iter != mCache.
end() && mCache.size() >= mCapacity) {
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();
470 typename T::List entities;
471 extractResults(job, entities);
473 foreach (Entity::Id
id, ids) {
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;
511 template<>
inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResults(KJob *job, Collection::List &collections)
const
513 CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob *>(job);
515 collections = fetch->collections();
518 template<>
inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResults(KJob *job, Item::List &items)
const
520 ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
522 items = fetch->items();
525 template<>
inline void EntityListCache<Tag, TagFetchJob, TagFetchScope>::extractResults(KJob *job, Tag::List &tags)
const
527 TagFetchJob *fetch = qobject_cast<TagFetchJob *>(job);
529 tags = fetch->tags();
533 inline CollectionFetchJob *EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob(
const QList<Entity::Id> &ids,
const CollectionFetchScope &scope)
535 CollectionFetchJob *fetch =
new CollectionFetchJob(ids, CollectionFetchJob::Base, session);
536 fetch->setFetchScope(scope);
540 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache;
541 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache;
542 typedef EntityListCache<Tag, TagFetchJob, TagFetchScope> TagListCache;
const Key key(const T &value) const
virtual bool ensureCached(typename T::Id id, const FetchScope &scope)
Requests the object to be cached if it is not yet in the cache.
iterator erase(iterator pos)
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.
iterator erase(iterator pos)
const T value(const Key &key) const
bool contains(const T &value) const
QVariant fromValue(const T &value)
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.
const_iterator constBegin() const