QGIS API Documentation 3.39.0-Master (3aed037ce22)
Loading...
Searching...
No Matches
qgsabstractcontentcache.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsabstractcontentcache.h
3 ---------------
4 begin : December 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#ifndef QGSABSTRACTCONTENTCACHE_H
19#define QGSABSTRACTCONTENTCACHE_H
20
21#include "qgis_core.h"
22#include "qgis_sip.h"
23#include "qgslogger.h"
24#include "qgsmessagelog.h"
25#include "qgsapplication.h"
28#include "qgsvariantutils.h"
29
30#include <QObject>
31#include <QRecursiveMutex>
32#include <QCache>
33#include <QSet>
34#include <QDateTime>
35#include <QList>
36#include <QFile>
37#include <QNetworkReply>
38#include <QFileInfo>
39#include <QUrl>
40
52{
53 public:
54
58 QgsAbstractContentCacheEntry( const QString &path ) ;
59
60 virtual ~QgsAbstractContentCacheEntry() = default;
61
64
68 QString path;
69
71 QDateTime fileModified;
72
75
77 int mFileModifiedCheckTimeout = 30000;
78
83 QgsAbstractContentCacheEntry *nextEntry = nullptr;
84
89 QgsAbstractContentCacheEntry *previousEntry = nullptr;
90
91 bool operator==( const QgsAbstractContentCacheEntry &other ) const
92 {
93 return other.path == path;
94 }
95
99 virtual int dataSize() const = 0;
100
104 virtual void dump() const = 0;
105
106 protected:
107
113 virtual bool isEqual( const QgsAbstractContentCacheEntry *other ) const = 0;
114
115 private:
116#ifdef SIP_RUN
118#endif
119
120};
121
132class CORE_EXPORT QgsAbstractContentCacheBase: public QObject
133{
134 Q_OBJECT
135
136 public:
137
141 QgsAbstractContentCacheBase( QObject *parent );
142
143 signals:
144
148 void remoteContentFetched( const QString &url );
149
150 protected:
151
156 virtual bool checkReply( QNetworkReply *reply, const QString &path ) const
157 {
158 Q_UNUSED( reply )
159 Q_UNUSED( path )
160 return true;
161 }
162
163 protected slots:
164
171 virtual void onRemoteContentFetched( const QString &url, bool success );
172
173};
174
175#ifndef SIP_RUN
176
190template<class T>
192{
193
194 public:
195
207 QgsAbstractContentCache( QObject *parent SIP_TRANSFERTHIS = nullptr,
208 const QString &typeString = QString(),
209 long maxCacheSize = 20000000,
210 int fileModifiedCheckTimeout = 30000 )
212 , mMaxCacheSize( maxCacheSize )
213 , mFileModifiedCheckTimeout( fileModifiedCheckTimeout )
214 , mTypeString( typeString.isEmpty() ? QObject::tr( "Content" ) : typeString )
215 {
216 }
217
219 {
220 qDeleteAll( mEntryLookup );
221 }
222
223 protected:
224
229 {
230 //only one entry in cache
231 if ( mLeastRecentEntry == mMostRecentEntry )
232 {
233 return;
234 }
235 T *entry = mLeastRecentEntry;
236 while ( entry && ( mTotalSize > mMaxCacheSize ) )
237 {
238 T *bkEntry = entry;
239 entry = static_cast< T * >( entry->nextEntry );
240
241 takeEntryFromList( bkEntry );
242 mEntryLookup.remove( bkEntry->path, bkEntry );
243 mTotalSize -= bkEntry->dataSize();
244 delete bkEntry;
245 }
246 }
247
261 QByteArray getContent( const QString &path, const QByteArray &missingContent, const QByteArray &fetchingContent, bool blocking = false ) const;
262
263 void onRemoteContentFetched( const QString &url, bool success ) override
264 {
265 const QMutexLocker locker( &mMutex );
266 mPendingRemoteUrls.remove( url );
267
268 T *nextEntry = mLeastRecentEntry;
269 while ( T *entry = nextEntry )
270 {
271 nextEntry = static_cast< T * >( entry->nextEntry );
272 if ( entry->path == url )
273 {
274 takeEntryFromList( entry );
275 mEntryLookup.remove( entry->path, entry );
276 mTotalSize -= entry->dataSize();
277 delete entry;
278 }
279 }
280
281 if ( success )
282 emit remoteContentFetched( url );
283 }
284
296 {
297 // Wait up to timeout seconds for task finished
299 {
300 // The wait did not time out
301 // Third step, check status as complete
302 if ( task->status() == QgsTask::Complete )
303 {
304 // Fourth step, force the signal fetched to be sure reply has been checked
305
306 // ARGH this is BAD BAD BAD. The connection will get called twice as a result!!!
307 task->fetched();
308 return true;
309 }
310 }
311 return false;
312 }
313
323 T *findExistingEntry( T *entryTemplate )
324 {
325 //search entries in mEntryLookup
326 const QString path = entryTemplate->path;
327 T *currentEntry = nullptr;
328 const QList<T *> entries = mEntryLookup.values( path );
329 QDateTime modified;
330 for ( T *cacheEntry : entries )
331 {
332 if ( cacheEntry->isEqual( entryTemplate ) )
333 {
334 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
335 {
336 if ( !modified.isValid() )
337 modified = QFileInfo( path ).lastModified();
338
339 if ( cacheEntry->fileModified != modified )
340 continue;
341 else
342 cacheEntry->fileModifiedLastCheckTimer.restart();
343 }
344 currentEntry = cacheEntry;
345 break;
346 }
347 }
348
349 //if not found: insert entryTemplate as a new entry
350 if ( !currentEntry )
351 {
352 currentEntry = insertCacheEntry( entryTemplate );
353 }
354 else
355 {
356 delete entryTemplate;
357 entryTemplate = nullptr;
358 ( void )entryTemplate;
359 takeEntryFromList( currentEntry );
360 if ( !mMostRecentEntry ) //list is empty
361 {
362 mMostRecentEntry = currentEntry;
363 mLeastRecentEntry = currentEntry;
364 }
365 else
366 {
367 mMostRecentEntry->nextEntry = currentEntry;
368 currentEntry->previousEntry = mMostRecentEntry;
369 currentEntry->nextEntry = nullptr;
370 mMostRecentEntry = currentEntry;
371 }
372 }
373
374 //debugging
375 //printEntryList();
376
377 return currentEntry;
378 }
379 mutable QRecursiveMutex mMutex;
380
382 long mTotalSize = 0;
383
385 long mMaxCacheSize = 20000000;
386
387 private:
388
394 T *insertCacheEntry( T *entry )
395 {
396 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
397
398 if ( !entry->path.startsWith( QLatin1String( "base64:" ) ) )
399 {
400 entry->fileModified = QFileInfo( entry->path ).lastModified();
401 entry->fileModifiedLastCheckTimer.start();
402 }
403
404 mEntryLookup.insert( entry->path, entry );
405
406 //insert to most recent place in entry list
407 if ( !mMostRecentEntry ) //inserting first entry
408 {
409 mLeastRecentEntry = entry;
410 mMostRecentEntry = entry;
411 entry->previousEntry = nullptr;
412 entry->nextEntry = nullptr;
413 }
414 else
415 {
416 entry->previousEntry = mMostRecentEntry;
417 entry->nextEntry = nullptr;
418 mMostRecentEntry->nextEntry = entry;
419 mMostRecentEntry = entry;
420 }
421
422 trimToMaximumSize();
423 return entry;
424 }
425
426
430 void takeEntryFromList( T *entry )
431 {
432 if ( !entry )
433 {
434 return;
435 }
436
437 if ( entry->previousEntry )
438 {
439 entry->previousEntry->nextEntry = entry->nextEntry;
440 }
441 else
442 {
443 mLeastRecentEntry = static_cast< T * >( entry->nextEntry );
444 }
445 if ( entry->nextEntry )
446 {
447 entry->nextEntry->previousEntry = entry->previousEntry;
448 }
449 else
450 {
451 mMostRecentEntry = static_cast< T * >( entry->previousEntry );
452 }
453 }
454
458 void printEntryList()
459 {
460 QgsDebugMsgLevel( QStringLiteral( "****************cache entry list*************************" ), 1 );
461 QgsDebugMsgLevel( "Cache size: " + QString::number( mTotalSize ), 1 );
462 T *entry = mLeastRecentEntry;
463 while ( entry )
464 {
465 QgsDebugMsgLevel( QStringLiteral( "***Entry:" ), 1 );
466 entry->dump();
467 entry = static_cast< T * >( entry->nextEntry );
468 }
469 }
470
472 QMultiHash< QString, T * > mEntryLookup;
473
475 int mFileModifiedCheckTimeout = 30000;
476
477 //The content cache keeps the entries on a double connected list, moving the current entry to the front.
478 //That way, removing entries for more space can start with the least used objects.
479 T *mLeastRecentEntry = nullptr;
480 T *mMostRecentEntry = nullptr;
481
482 mutable QCache< QString, QByteArray > mRemoteContentCache;
483 mutable QSet< QString > mPendingRemoteUrls;
484
485 QString mTypeString;
486
487 friend class TestQgsSvgCache;
488 friend class TestQgsImageCache;
489};
490
491#endif
492
493#endif // QGSABSTRACTCONTENTCACHE_H
A QObject derived base class for QgsAbstractContentCache.
void remoteContentFetched(const QString &url)
Emitted when the cache has finished retrieving content from a remote url.
virtual bool checkReply(QNetworkReply *reply, const QString &path) const
Runs additional checks on a network reply to ensure that the reply content is consistent with that re...
Base class for entries in a QgsAbstractContentCache.
virtual int dataSize() const =0
Returns the memory usage in bytes for the entry.
virtual void dump() const =0
Dumps debugging strings containing the item's properties.
virtual ~QgsAbstractContentCacheEntry()=default
QElapsedTimer fileModifiedLastCheckTimer
Time since last check of file modified date.
QgsAbstractContentCacheEntry(const QgsAbstractContentCacheEntry &rh)=delete
QgsAbstractContentCacheEntry & operator=(const QgsAbstractContentCacheEntry &rh)=delete
QString path
Represents the absolute path to a file, a remote URL, or a base64 encoded string.
virtual bool isEqual(const QgsAbstractContentCacheEntry *other) const =0
Tests whether this entry matches another entry.
QDateTime fileModified
Timestamp when file was last modified.
bool operator==(const QgsAbstractContentCacheEntry &other) const
Abstract base class for file content caches, such as SVG or raster image caches.
T * findExistingEntry(T *entryTemplate)
Returns the existing entry from the cache which matches entryTemplate (deleting entryTemplate when do...
void onRemoteContentFetched(const QString &url, bool success) override
Triggered after remote content (i.e.
QgsAbstractContentCache(QObject *parent=nullptr, const QString &typeString=QString(), long maxCacheSize=20000000, int fileModifiedCheckTimeout=30000)
Constructor for QgsAbstractContentCache, with the specified parent object.
void trimToMaximumSize()
Removes the least used cache entries until the maximum cache size is under the predefined size limit.
bool waitForTaskFinished(QgsNetworkContentFetcherTask *task) const
Blocks the current thread until the task finishes (or user's preset network timeout expires)
static int timeout()
Returns the network timeout length, in milliseconds.
Handles HTTP network content fetching in a background task.
void fetched()
Emitted when the network content has been fetched, regardless of whether the fetch was successful or ...
TaskStatus status() const
Returns the current task status.
@ Complete
Task successfully completed.
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
#define SIP_TRANSFERTHIS
Definition qgis_sip.h:53
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39