akonadi
entitycache_p.h
00001 /* 00002 Copyright (c) 2009 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 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; // got replaced in the meantime 00180 00181 node->pending = false; 00182 extractResult( node, job ); 00183 // make sure we find this node again if something went wrong here, 00184 // most likely the object got deleted from the server in the meantime 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