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 typedef FetchScope_ FetchScope; 00083 explicit EntityCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) : 00084 EntityCacheBase( session, parent ), 00085 mCapacity( maxCapacity ) 00086 {} 00087 00088 ~EntityCache() 00089 { 00090 qDeleteAll( mCache ); 00091 } 00092 00094 bool isCached( typename T::Id id ) const 00095 { 00096 EntityCacheNode<T>* node = cacheNodeForId( id ); 00097 return node && !node->pending; 00098 } 00099 00101 bool isRequested( typename T::Id id ) const 00102 { 00103 return cacheNodeForId( id ); 00104 } 00105 00107 virtual T retrieve( typename T::Id id ) const 00108 { 00109 EntityCacheNode<T>* node = cacheNodeForId( id ); 00110 if ( node && !node->pending && !node->invalid ) 00111 return node->entity; 00112 return T(); 00113 } 00114 00116 void invalidate( typename T::Id id ) 00117 { 00118 EntityCacheNode<T>* node = cacheNodeForId( id ); 00119 if ( node ) 00120 node->invalid = true; 00121 } 00122 00124 void update( typename T::Id id, const FetchScope &scope ) 00125 { 00126 EntityCacheNode<T>* node = cacheNodeForId( id ); 00127 if ( node ) { 00128 mCache.removeAll( node ); 00129 if ( node->pending ) 00130 request( id, scope ); 00131 delete node; 00132 } 00133 } 00134 00136 virtual bool ensureCached( typename T::Id id, const FetchScope &scope ) 00137 { 00138 EntityCacheNode<T>* node = cacheNodeForId( id ); 00139 if ( !node ) { 00140 request( id, scope ); 00141 return false; 00142 } 00143 return !node->pending; 00144 } 00145 00151 virtual void request( typename T::Id id, const FetchScope &scope ) 00152 { 00153 Q_ASSERT( !isRequested( id ) ); 00154 shrinkCache(); 00155 EntityCacheNode<T> *node = new EntityCacheNode<T>( id ); 00156 FetchJob* job = createFetchJob( id ); 00157 job->setFetchScope( scope ); 00158 job->setProperty( "EntityCacheNode", QVariant::fromValue<typename T::Id>( id ) ); 00159 connect( job, SIGNAL( result( KJob* ) ), SLOT( fetchResult( KJob* ) ) ); 00160 mCache.enqueue( node ); 00161 } 00162 00163 private: 00164 EntityCacheNode<T>* cacheNodeForId( typename T::Id id ) const 00165 { 00166 for ( typename QQueue<EntityCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd(); 00167 it != endIt; ++it ) 00168 { 00169 if ( (*it)->entity.id() == id ) 00170 return *it; 00171 } 00172 return 0; 00173 } 00174 00175 void fetchResult( KJob* job ) 00176 { 00177 typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>(); 00178 EntityCacheNode<T> *node = cacheNodeForId( id ); 00179 if ( !node ) 00180 return; // got replaced in the meantime 00181 00182 node->pending = false; 00183 extractResult( node, job ); 00184 // make sure we find this node again if something went wrong here, 00185 // most likely the object got deleted from the server in the meantime 00186 if ( node->entity.id() != id ) { 00187 node->entity.setId( id ); 00188 node->invalid = true; 00189 } 00190 emit dataAvailable(); 00191 } 00192 00193 void extractResult( EntityCacheNode<T>* node, KJob* job ) const; 00194 00195 inline FetchJob* createFetchJob( typename T::Id id ) 00196 { 00197 return new FetchJob( T( id ), session ); 00198 } 00199 00201 void shrinkCache() 00202 { 00203 while ( mCache.size() >= mCapacity && !mCache.first()->pending ) 00204 delete mCache.dequeue(); 00205 } 00206 00207 private: 00208 QQueue<EntityCacheNode<T>*> mCache; 00209 int mCapacity; 00210 }; 00211 00212 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const 00213 { 00214 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job ); 00215 Q_ASSERT( fetch ); 00216 if ( fetch->collections().isEmpty() ) 00217 node->entity = Collection(); 00218 else 00219 node->entity = fetch->collections().first(); 00220 } 00221 00222 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const 00223 { 00224 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job ); 00225 Q_ASSERT( fetch ); 00226 if ( fetch->items().isEmpty() ) 00227 node->entity = Item(); 00228 else 00229 node->entity = fetch->items().first(); 00230 } 00231 00232 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id ) 00233 { 00234 return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session ); 00235 } 00236 00237 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache; 00238 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache; 00239 00240 } 00241 00242 #endif