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 processResult( 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(processResult( 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 processResult( KJob* job ) 00176 { 00177 // Error handling? 00178 typename T::Id id = job->property( "EntityCacheNode" ).template value<typename T::Id>(); 00179 EntityCacheNode<T> *node = cacheNodeForId( id ); 00180 if ( !node ) 00181 return; // got replaced in the meantime 00182 00183 node->pending = false; 00184 extractResult( node, job ); 00185 // make sure we find this node again if something went wrong here, 00186 // most likely the object got deleted from the server in the meantime 00187 if ( node->entity.id() != id ) { 00188 // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true! 00189 node->entity.setId( id ); 00190 node->invalid = true; 00191 } 00192 emit dataAvailable(); 00193 } 00194 00195 void extractResult( EntityCacheNode<T>* node, KJob* job ) const; 00196 00197 inline FetchJob* createFetchJob( typename T::Id id ) 00198 { 00199 return new FetchJob( T( id ), session ); 00200 } 00201 00203 void shrinkCache() 00204 { 00205 while ( mCache.size() >= mCapacity && !mCache.first()->pending ) 00206 delete mCache.dequeue(); 00207 } 00208 00209 private: 00210 QQueue<EntityCacheNode<T>*> mCache; 00211 int mCapacity; 00212 }; 00213 00214 template<> inline void EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityCacheNode<Collection>* node, KJob *job ) const 00215 { 00216 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job ); 00217 Q_ASSERT( fetch ); 00218 if ( fetch->collections().isEmpty() ) 00219 node->entity = Collection(); 00220 else 00221 node->entity = fetch->collections().first(); 00222 } 00223 00224 template<> inline void EntityCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityCacheNode<Item>* node, KJob *job ) const 00225 { 00226 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job ); 00227 Q_ASSERT( fetch ); 00228 if ( fetch->items().isEmpty() ) 00229 node->entity = Item(); 00230 else 00231 node->entity = fetch->items().first(); 00232 } 00233 00234 template<> inline CollectionFetchJob* EntityCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( Collection::Id id ) 00235 { 00236 return new CollectionFetchJob( Collection( id ), CollectionFetchJob::Base, session ); 00237 } 00238 00239 typedef EntityCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionCache; 00240 typedef EntityCache<Item, ItemFetchJob, ItemFetchScope> ItemCache; 00241 00242 00243 template<typename T> 00244 class Comparator 00245 { 00246 public: 00247 static bool compare(const typename T::List &lhs_, const QList<typename T::Id> &rhs_ ) 00248 { 00249 bool val = true; 00250 00251 if (lhs_.size() != rhs_.size()) 00252 return false; 00253 00254 typename T::List lhs = lhs_; 00255 QList<typename T::Id> rhs = rhs_; 00256 00257 qSort(lhs); 00258 qSort(rhs); 00259 return lhs == rhs; 00260 } 00261 00262 static bool compare(const QList<typename T::Id> &l1, const typename T::List &l2) 00263 { 00264 return compare(l2, l1); 00265 } 00266 00267 static bool compare(const typename T::List &l1, const typename T::List &l2) 00268 { 00269 typename T::List l1_ = l1; 00270 typename T::List l2_ = l2; 00271 qSort(l1_); 00272 qSort(l2_); 00273 return l1_ == l2_; 00274 } 00275 }; 00276 00277 00278 template <typename T> 00279 struct EntityListCacheNode 00280 { 00281 EntityListCacheNode( const typename T::List &list ) : entityList( list ), pending( false ), invalid( false ) {} 00282 EntityListCacheNode( const QList<typename T::Id> &list ) : pending( false ), invalid( false ) { 00283 foreach( typename T::Id id, list) 00284 entityList.append(T(id)); 00285 } 00286 typename T::List entityList; 00287 bool pending; 00288 bool invalid; 00289 }; 00290 00291 template<typename T, typename FetchJob, typename FetchScope_> 00292 class EntityListCache : public EntityCacheBase 00293 { 00294 public: 00295 typedef FetchScope_ FetchScope; 00296 00297 explicit EntityListCache( int maxCapacity, Session *session = 0, QObject *parent = 0 ) : 00298 EntityCacheBase( session, parent ), 00299 mCapacity( maxCapacity ) 00300 {} 00301 00302 ~EntityListCache() 00303 { 00304 qDeleteAll( mCache ); 00305 } 00306 00308 template<typename TArg> 00309 typename T::List retrieve( const QList<TArg> &id ) const 00310 { 00311 EntityListCacheNode<T>* node = cacheNodeForId( id ); 00312 if ( node && !node->pending && !node->invalid ) 00313 return node->entityList; 00314 return typename T::List(); 00315 } 00316 00318 template<typename TArg> 00319 bool ensureCached( const QList<TArg> &id, const FetchScope &scope ) 00320 { 00321 EntityListCacheNode<T>* node = cacheNodeForId( id ); 00322 if ( !node ) { 00323 request( id, scope ); 00324 return false; 00325 } 00326 return !node->pending; 00327 } 00328 00330 template<typename TArg> 00331 void invalidate( const QList<TArg> &id ) 00332 { 00333 EntityListCacheNode<T>* node = cacheNodeForId( id ); 00334 if ( node ) 00335 node->invalid = true; 00336 } 00337 00339 template<typename TArg> 00340 void update( const QList<TArg> &id, const FetchScope &scope ) 00341 { 00342 EntityListCacheNode<T>* node = cacheNodeForId( id ); 00343 if ( node ) { 00344 mCache.removeAll( node ); 00345 if ( node->pending ) 00346 request( id, scope ); 00347 delete node; 00348 } 00349 } 00350 00351 00353 template<typename TArg> 00354 bool isCached( const QList<TArg> &id ) const 00355 { 00356 EntityListCacheNode<T>* node = cacheNodeForId( id ); 00357 return node && !node->pending; 00358 } 00359 00360 private: 00361 00362 typename T::List getTList( const QList<typename T::Id> &id ) 00363 { 00364 typename T::List ids; 00365 foreach(typename T::Id id_, id) 00366 ids.append(T(id_)); 00367 return ids; 00368 } 00369 00370 typename T::List getTList( const typename T::List &id ) 00371 { 00372 return id; 00373 } 00374 00380 template<typename TArg> 00381 void request( const QList<TArg> &id, const FetchScope &scope ) 00382 { 00383 Q_ASSERT( !isRequested( id ) ); 00384 shrinkCache(); 00385 EntityListCacheNode<T> *node = new EntityListCacheNode<T>( id ); 00386 FetchJob* job = createFetchJob( id ); 00387 job->setFetchScope( scope ); 00388 job->setProperty( "EntityListCacheNode", QVariant::fromValue<typename T::List>( getTList( id ) ) ); 00389 connect( job, SIGNAL(result(KJob*)), SLOT(processResult(KJob*)) ); 00390 mCache.enqueue( node ); 00391 } 00392 00394 void shrinkCache() 00395 { 00396 while ( mCache.size() >= mCapacity && !mCache.first()->pending ) 00397 delete mCache.dequeue(); 00398 } 00399 00401 template<typename TArg> 00402 bool isRequested( const QList<TArg> &id ) const 00403 { 00404 return cacheNodeForId( id ); 00405 } 00406 00407 template<typename TArg> 00408 EntityListCacheNode<T>* cacheNodeForId( const QList<TArg> &id ) const 00409 { 00410 for ( typename QQueue<EntityListCacheNode<T>*>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd(); 00411 it != endIt; ++it ) 00412 { 00413 if ( Comparator<T>::compare( ( *it )->entityList, id ) ) 00414 return *it; 00415 } 00416 return 0; 00417 } 00418 00419 template<typename TArg> 00420 inline FetchJob* createFetchJob( const QList<TArg> &id ) 00421 { 00422 return new FetchJob( id, session ); 00423 } 00424 00425 void processResult( KJob* job ) 00426 { 00427 typename T::List ids = job->property( "EntityListCacheNode" ).template value<typename T::List>(); 00428 00429 EntityListCacheNode<T> *node = cacheNodeForId( ids ); 00430 if ( !node ) 00431 return; // got replaced in the meantime 00432 00433 node->pending = false; 00434 extractResult( node, job ); 00435 // make sure we find this node again if something went wrong here, 00436 // most likely the object got deleted from the server in the meantime 00437 if ( node->entityList != ids ) { 00438 node->entityList = ids; 00439 node->invalid = true; 00440 } 00441 emit dataAvailable(); 00442 } 00443 00444 void extractResult( EntityListCacheNode<T>* node, KJob* job ) const; 00445 00446 00447 private: 00448 QQueue<EntityListCacheNode<T>*> mCache; 00449 int mCapacity; 00450 }; 00451 00452 template<> inline void EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::extractResult( EntityListCacheNode<Collection>* node, KJob *job ) const 00453 { 00454 CollectionFetchJob* fetch = qobject_cast<CollectionFetchJob*>( job ); 00455 Q_ASSERT( fetch ); 00456 node->entityList = fetch->collections(); 00457 } 00458 00459 template<> inline void EntityListCache<Item, ItemFetchJob, ItemFetchScope>::extractResult( EntityListCacheNode<Item>* node, KJob *job ) const 00460 { 00461 ItemFetchJob* fetch = qobject_cast<ItemFetchJob*>( job ); 00462 Q_ASSERT( fetch ); 00463 node->entityList = fetch->items(); 00464 } 00465 00466 template<> 00467 template<typename TArg> 00468 inline CollectionFetchJob* EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope>::createFetchJob( const QList<TArg> &id ) 00469 { 00470 return new CollectionFetchJob( id, CollectionFetchJob::Base, session ); 00471 } 00472 00473 typedef EntityListCache<Collection, CollectionFetchJob, CollectionFetchScope> CollectionListCache; 00474 typedef EntityListCache<Item, ItemFetchJob, ItemFetchScope> ItemListCache; 00475 00476 } 00477 00478 #endif
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:52:54 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:52:54 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.