annotate data/fileio/FileSource.h @ 460:93fb1ebff76b

* Add persistent cache file support to FileSource (e.g. for RDF descriptions) * Query RDF plugin data in a background thread on startup
author Chris Cannam
date Fri, 17 Oct 2008 13:32:55 +0000
parents 183ee2a55fc7
children 63b8ba45d953
rev   line source
Chris@208 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@208 2
Chris@208 3 /*
Chris@208 4 Sonic Visualiser
Chris@208 5 An audio file viewer and annotation editor.
Chris@208 6 Centre for Digital Music, Queen Mary, University of London.
Chris@208 7 This file copyright 2007 QMUL.
Chris@208 8
Chris@208 9 This program is free software; you can redistribute it and/or
Chris@208 10 modify it under the terms of the GNU General Public License as
Chris@208 11 published by the Free Software Foundation; either version 2 of the
Chris@208 12 License, or (at your option) any later version. See the file
Chris@208 13 COPYING included with this distribution for more information.
Chris@208 14 */
Chris@208 15
Chris@208 16 #ifndef _REMOTE_FILE_H_
Chris@208 17 #define _REMOTE_FILE_H_
Chris@208 18
Chris@208 19 #include <QUrl>
Chris@208 20 #include <QMutex>
Chris@208 21 #include <QString>
Chris@210 22 #include <QTimer>
Chris@208 23
Chris@304 24 #include <map>
Chris@304 25
Chris@208 26 class QFtp;
Chris@208 27 class QHttp;
Chris@208 28 class QFile;
Chris@210 29 class QHttpResponseHeader;
Chris@392 30 class ProgressReporter;
Chris@208 31
Chris@325 32 /**
Chris@325 33 * FileSource is a class used to refer to the contents of a file that
Chris@325 34 * may be either local or at a remote location such as a HTTP URL.
Chris@325 35 *
Chris@325 36 * When a FileSource object is constructed, the file or URL passed to
Chris@325 37 * its constructor is tested for validity, and if it refers to a
Chris@325 38 * remote HTTP or FTP location it is retrieved asynchronously (the Qt
Chris@325 39 * event loop must be running) and cached locally. You can then query
Chris@325 40 * a local path for the file and refer to that as a normal filename.
Chris@325 41 *
Chris@325 42 * Use isAvailable() to test whether the file or remote URL exists,
Chris@325 43 * and isOK() to test for internal validity or transmission errors.
Chris@325 44 * Call waitForStatus() to block until the availability and validity
Chris@325 45 * of the URL have been established so that isAvailable may be called,
Chris@325 46 * and waitForData() to block until the entire file has been cached.
Chris@325 47 *
Chris@325 48 * FileSource handles reference counting for cache files. You can
Chris@325 49 * construct many FileSource objects with the same URL and you will
Chris@325 50 * receive the same cached file for each; it is also reasonable to
Chris@325 51 * pass FileSource objects by value. FileSource only makes sense for
Chris@325 52 * stateless URLs that result in the same data on each request.
Chris@325 53 *
Chris@460 54 * Cached files (in their default temporary mode) share a lifetime
Chris@460 55 * with their "owning" FileSource objects. When the last FileSource
Chris@460 56 * referring to a given URL is deleted or goes out of scope, its
Chris@460 57 * cached file (if any) is also removed. You can change this
Chris@460 58 * behaviour by using persistent cache mode; \see LocalCacheMode for
Chris@460 59 * details and caveats.
Chris@325 60 */
Chris@317 61 class FileSource : public QObject
Chris@208 62 {
Chris@208 63 Q_OBJECT
Chris@208 64
Chris@208 65 public:
Chris@325 66 /**
Chris@460 67 * Type of local cache to be used when retrieving remote files.
Chris@460 68 *
Chris@460 69 * Temporary cache files are created when a FileSource object is
Chris@460 70 * first created for a given URL, and removed when the last extant
Chris@460 71 * temporary cache mode FileSource object referring to a given URL
Chris@460 72 * is deleted (i.e. when its reference count is lowered to zero).
Chris@460 73 * They are also stored in a temporary directory that will be
Chris@460 74 * deleted when the program exits.
Chris@460 75 *
Chris@460 76 * Persistent cache files are created only when first retrieving a
Chris@460 77 * URL for which no persistent cache already exists, and are never
Chris@460 78 * deleted (by FileSource anyway). They are stored in a directory
Chris@460 79 * that is not deleted when the program exits. FileSource creates
Chris@460 80 * a unique local file name for each source URL, so as long as the
Chris@460 81 * local cache file remains on disc, the remote URL will not be
Chris@460 82 * retrieved again during any further run of the program. You can
Chris@460 83 * find out what local file name will be used for the persistent
Chris@460 84 * cache of a given URL by calling getPersistentCacheFilePath, if
Chris@460 85 * you want to do something such as delete it by hand.
Chris@460 86 *
Chris@460 87 * Note that FileSource does not cache local files (i.e. does not
Chris@460 88 * make a copy of files that already appear to be stored on the
Chris@460 89 * local filesystem) in either mode.
Chris@460 90 */
Chris@460 91 enum LocalCacheMode {
Chris@460 92 TemporaryCache,
Chris@460 93 PersistentCache
Chris@460 94 };
Chris@460 95
Chris@460 96 /**
Chris@325 97 * Construct a FileSource using the given local file path or URL.
Chris@357 98 * The URL may be raw or encoded.
Chris@357 99 *
Chris@392 100 * If a ProgressReporter is provided, it will be updated with
Chris@392 101 * progress status. Note that the progress() signal will also be
Chris@392 102 * emitted regularly during retrieval, even if no reporter is
Chris@392 103 * supplied here. Caller retains ownership of the reporter object.
Chris@460 104 *
Chris@460 105 * If LocalCacheMode is PersistentCache, a persistent cache file
Chris@460 106 * will be used. See LocalCacheMode documentation for details.
Chris@325 107 */
Chris@460 108 FileSource(QString fileOrUrl,
Chris@460 109 ProgressReporter *reporter = 0,
Chris@460 110 LocalCacheMode mode = TemporaryCache);
Chris@325 111
Chris@325 112 /**
Chris@357 113 * Construct a FileSource using the given remote URL.
Chris@357 114 *
Chris@392 115 * If a ProgressReporter is provided, it will be updated with
Chris@392 116 * progress status. Note that the progress() signal will also be
Chris@392 117 * emitted regularly during retrieval, even if no reporter is
Chris@392 118 * supplied here. Caller retains ownership of the reporter object.
Chris@460 119 *
Chris@460 120 * If LocalCacheMode is PersistentCache, a persistent cache file
Chris@460 121 * will be used. See LocalCacheMode documentation for details.
Chris@325 122 */
Chris@460 123 FileSource(QUrl url,
Chris@460 124 ProgressReporter *reporter = 0,
Chris@460 125 LocalCacheMode mode = TemporaryCache);
Chris@325 126
Chris@317 127 FileSource(const FileSource &);
Chris@316 128
Chris@317 129 virtual ~FileSource();
Chris@208 130
Chris@325 131 /**
Chris@325 132 * Block on a sub-event-loop until the availability of the file or
Chris@325 133 * remote URL is known.
Chris@325 134 */
Chris@325 135 void waitForStatus();
Chris@325 136
Chris@325 137 /**
Chris@325 138 * Block on a sub-event-loop until the whole of the data has been
Chris@325 139 * retrieved (if it is remote).
Chris@325 140 */
Chris@325 141 void waitForData();
Chris@325 142
Chris@325 143 /**
Chris@325 144 * Return true if the FileSource object is valid and no error
Chris@325 145 * occurred in looking up the file or remote URL. Non-existence
Chris@325 146 * of the file or URL is not an error -- call isAvailable() to
Chris@325 147 * test for that.
Chris@325 148 */
Chris@325 149 bool isOK() const;
Chris@325 150
Chris@325 151 /**
Chris@325 152 * Return true if the file or remote URL exists. This may block
Chris@325 153 * on a sub-event-loop (calling waitForStatus) if the status is
Chris@325 154 * not yet available.
Chris@325 155 */
Chris@210 156 bool isAvailable();
Chris@210 157
Chris@325 158 /**
Chris@325 159 * Return true if the entire file has been retrieved and is
Chris@325 160 * available.
Chris@325 161 */
Chris@325 162 bool isDone() const;
Chris@316 163
Chris@325 164 /**
Chris@325 165 * Return true if this FileSource is referring to a remote URL.
Chris@325 166 */
Chris@325 167 bool isRemote() const;
Chris@325 168
Chris@325 169 /**
Chris@325 170 * Return the location filename or URL as passed to the
Chris@325 171 * constructor.
Chris@325 172 */
Chris@325 173 QString getLocation() const;
Chris@325 174
Chris@325 175 /**
Chris@325 176 * Return the name of the local file this FileSource refers to.
Chris@325 177 * This is the filename that should be used when reading normally
Chris@325 178 * from the FileSource. If the filename passed to the constructor
Chris@325 179 * was a local file, this will return the same filename; otherwise
Chris@325 180 * this will be the name of the temporary cache file owned by the
Chris@325 181 * FileSource.
Chris@325 182 */
Chris@325 183 QString getLocalFilename() const;
Chris@325 184
Chris@325 185 /**
Chris@325 186 * Return the MIME content type of this file, if known.
Chris@325 187 */
Chris@325 188 QString getContentType() const;
Chris@325 189
Chris@325 190 /**
Chris@325 191 * Return the file extension for this file, if any.
Chris@325 192 */
Chris@325 193 QString getExtension() const;
Chris@325 194
Chris@325 195 /**
Chris@325 196 * Return an error message, if isOK() is false.
Chris@325 197 */
Chris@325 198 QString getErrorString() const;
Chris@325 199
Chris@325 200 /**
Chris@325 201 * Specify whether any local, cached file should remain on disc
Chris@325 202 * after this FileSource has been destroyed. The default is false
Chris@460 203 * (cached files share their FileSource owners' lifespans). This
Chris@460 204 * is only meaningful in TemporaryCache mode; even if this setting
Chris@460 205 * is true, the temporary cache will still be deleted when the
Chris@460 206 * program exits. Use PersistentCache mode if you want the cache
Chris@460 207 * to outlast the program.
Chris@325 208 */
Chris@316 209 void setLeaveLocalFile(bool leave);
Chris@208 210
Chris@325 211 /**
Chris@325 212 * Return true if the given filename or URL refers to a remote
Chris@325 213 * URL.
Chris@325 214 */
Chris@325 215 static bool isRemote(QString fileOrUrl);
Chris@210 216
Chris@325 217 /**
Chris@325 218 * Return true if FileSource can handle the retrieval scheme for
Chris@325 219 * the given URL (or if the URL is for a local file).
Chris@325 220 */
Chris@208 221 static bool canHandleScheme(QUrl url);
Chris@208 222
Chris@460 223 /**
Chris@460 224 * Return the path that will be used for the cache file copy of
Chris@460 225 * the given remote URL by a FileSource object constructed in
Chris@460 226 * PersistentCache mode.
Chris@460 227 *
Chris@460 228 * This method also creates the containing directory for such
Chris@460 229 * cache files, if it does not already exist, and so may throw
Chris@460 230 * DirectoryCreationFailed.
Chris@460 231 */
Chris@460 232 static QString getPersistentCacheFilePath(QUrl url);
Chris@460 233
Chris@208 234 signals:
Chris@325 235 /**
Chris@325 236 * Emitted during URL retrieval, when the retrieval progress
Chris@325 237 * notches up to a new percentage.
Chris@325 238 */
Chris@208 239 void progress(int percent);
Chris@325 240
Chris@325 241 /**
Chris@325 242 * Emitted when the file's existence has been tested and/or
Chris@325 243 * response header received. Calls to isAvailable() after this
Chris@325 244 * has been emitted will not block.
Chris@325 245 */
Chris@325 246 void statusAvailable();
Chris@325 247
Chris@325 248 /**
Chris@325 249 * Emitted when the entire file data has been retrieved and the
Chris@325 250 * local file is complete (if no error has occurred).
Chris@325 251 */
Chris@208 252 void ready();
Chris@208 253
Chris@208 254 protected slots:
Chris@208 255 void dataReadProgress(int done, int total);
Chris@214 256 void httpResponseHeaderReceived(const QHttpResponseHeader &resp);
Chris@214 257 void ftpCommandFinished(int, bool);
Chris@208 258 void dataTransferProgress(qint64 done, qint64 total);
Chris@208 259 void done(bool error);
Chris@210 260 void cancelled();
Chris@208 261
Chris@208 262 protected:
Chris@317 263 FileSource &operator=(const FileSource &); // not provided
Chris@316 264
Chris@304 265 QUrl m_url;
Chris@460 266 LocalCacheMode m_cacheMode;
Chris@208 267 QFtp *m_ftp;
Chris@208 268 QHttp *m_http;
Chris@208 269 QFile *m_localFile;
Chris@208 270 QString m_localFilename;
Chris@208 271 QString m_errorString;
Chris@315 272 QString m_contentType;
Chris@208 273 bool m_ok;
Chris@210 274 int m_lastStatus;
Chris@316 275 bool m_remote;
Chris@208 276 bool m_done;
Chris@316 277 bool m_leaveLocalFile;
Chris@392 278 ProgressReporter *m_reporter;
Chris@208 279
Chris@304 280 typedef std::map<QUrl, int> RemoteRefCountMap;
Chris@304 281 typedef std::map<QUrl, QString> RemoteLocalMap;
Chris@304 282 static RemoteRefCountMap m_refCountMap;
Chris@304 283 static RemoteLocalMap m_remoteLocalMap;
Chris@304 284 static QMutex m_mapMutex;
Chris@316 285 bool m_refCounted;
Chris@316 286
Chris@357 287 void init();
Chris@316 288 void initHttp();
Chris@316 289 void initFtp();
Chris@304 290
Chris@211 291 void cleanup();
Chris@211 292
Chris@316 293 // Create a local file for m_url. If it already existed, return true.
Chris@316 294 // The local filename is stored in m_localFilename.
Chris@316 295 bool createCacheFile();
Chris@316 296 void deleteCacheFile();
Chris@208 297
Chris@460 298 static QString getPersistentCacheDirectory();
Chris@460 299
Chris@208 300 static QMutex m_fileCreationMutex;
Chris@208 301 static int m_count;
Chris@208 302 };
Chris@208 303
Chris@208 304 #endif