Chris@208: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@208: Chris@208: /* Chris@208: Sonic Visualiser Chris@208: An audio file viewer and annotation editor. Chris@208: Centre for Digital Music, Queen Mary, University of London. Chris@208: This file copyright 2007 QMUL. Chris@208: Chris@208: This program is free software; you can redistribute it and/or Chris@208: modify it under the terms of the GNU General Public License as Chris@208: published by the Free Software Foundation; either version 2 of the Chris@208: License, or (at your option) any later version. See the file Chris@208: COPYING included with this distribution for more information. Chris@208: */ Chris@208: Chris@1581: #ifndef SV_FILE_SOURCE_H Chris@1581: #define SV_FILE_SOURCE_H Chris@208: Chris@208: #include Chris@208: #include Chris@208: #include Chris@210: #include Chris@762: #include Chris@208: Chris@304: #include Chris@304: Chris@686: #include "base/Debug.h" Chris@686: Chris@208: class QFile; Chris@392: class ProgressReporter; Chris@208: Chris@325: /** Chris@325: * FileSource is a class used to refer to the contents of a file that Chris@325: * may be either local or at a remote location such as a HTTP URL. Chris@325: * Chris@325: * When a FileSource object is constructed, the file or URL passed to Chris@325: * its constructor is tested for validity, and if it refers to a Chris@325: * remote HTTP or FTP location it is retrieved asynchronously (the Qt Chris@325: * event loop must be running) and cached locally. You can then query Chris@325: * a local path for the file and refer to that as a normal filename. Chris@325: * Chris@325: * Use isAvailable() to test whether the file or remote URL exists, Chris@325: * and isOK() to test for internal validity or transmission errors. Chris@325: * Call waitForStatus() to block until the availability and validity Chris@325: * of the URL have been established so that isAvailable may be called, Chris@325: * and waitForData() to block until the entire file has been cached. Chris@325: * Chris@325: * FileSource handles reference counting for cache files. You can Chris@325: * construct many FileSource objects with the same URL and you will Chris@325: * receive the same cached file for each; it is also reasonable to Chris@325: * pass FileSource objects by value. FileSource only makes sense for Chris@325: * stateless URLs that result in the same data on each request. Chris@325: * Chris@469: * Cached files share a lifetime with their "owning" FileSource Chris@469: * objects; when the last FileSource referring to a given URL is Chris@469: * deleted or goes out of scope, its cached file (if any) is also Chris@469: * removed. Chris@325: */ Chris@317: class FileSource : public QObject Chris@208: { Chris@208: Q_OBJECT Chris@208: Chris@208: public: Chris@325: /** Chris@325: * Construct a FileSource using the given local file path or URL. Chris@357: * The URL may be raw or encoded. Chris@357: * Chris@392: * If a ProgressReporter is provided, it will be updated with Chris@392: * progress status. Note that the progress() signal will also be Chris@392: * emitted regularly during retrieval, even if no reporter is Chris@392: * supplied here. Caller retains ownership of the reporter object. Chris@325: */ Chris@520: FileSource(QString fileOrUrl, Chris@520: ProgressReporter *reporter = 0, Chris@520: QString preferredContentType = ""); Chris@325: Chris@325: /** Chris@357: * Construct a FileSource using the given remote URL. Chris@357: * Chris@392: * If a ProgressReporter is provided, it will be updated with Chris@392: * progress status. Note that the progress() signal will also be Chris@392: * emitted regularly during retrieval, even if no reporter is Chris@392: * supplied here. Caller retains ownership of the reporter object. Chris@325: */ Chris@469: FileSource(QUrl url, ProgressReporter *reporter = 0); Chris@325: Chris@317: FileSource(const FileSource &); Chris@316: Chris@317: virtual ~FileSource(); Chris@208: Chris@325: /** Chris@325: * Block on a sub-event-loop until the availability of the file or Chris@325: * remote URL is known. Chris@325: */ Chris@325: void waitForStatus(); Chris@325: Chris@325: /** Chris@325: * Block on a sub-event-loop until the whole of the data has been Chris@325: * retrieved (if it is remote). Chris@325: */ Chris@325: void waitForData(); Chris@325: Chris@325: /** Chris@981: * Return true if the FileSource object is valid and neither error Chris@981: * nor cancellation occurred while retrieving the file or remote Chris@981: * URL. Non-existence of the file or URL is not an error -- call Chris@981: * isAvailable() to test for that. Chris@325: */ Chris@325: bool isOK() const; Chris@325: Chris@325: /** Chris@325: * Return true if the file or remote URL exists. This may block Chris@325: * on a sub-event-loop (calling waitForStatus) if the status is Chris@325: * not yet available. Chris@325: */ Chris@210: bool isAvailable(); Chris@210: Chris@325: /** Chris@325: * Return true if the entire file has been retrieved and is Chris@325: * available. Chris@325: */ Chris@325: bool isDone() const; Chris@316: Chris@325: /** Chris@981: * Return true if the operation was cancelled by the user through Chris@981: * the ProgressReporter interface. Note that the cancelled() Chris@981: * signal will have been emitted, and isOK() will also return Chris@981: * false in this case. Chris@981: */ Chris@981: bool wasCancelled() const; Chris@981: Chris@981: /** Chris@706: * Return true if this FileSource is referring to a QRC resource. Chris@706: */ Chris@706: bool isResource() const; Chris@706: Chris@706: /** Chris@325: * Return true if this FileSource is referring to a remote URL. Chris@325: */ Chris@325: bool isRemote() const; Chris@325: Chris@325: /** Chris@325: * Return the location filename or URL as passed to the Chris@325: * constructor. Chris@325: */ Chris@325: QString getLocation() const; Chris@325: Chris@325: /** Chris@325: * Return the name of the local file this FileSource refers to. Chris@325: * This is the filename that should be used when reading normally Chris@325: * from the FileSource. If the filename passed to the constructor Chris@325: * was a local file, this will return the same filename; otherwise Chris@325: * this will be the name of the temporary cache file owned by the Chris@325: * FileSource. Chris@325: */ Chris@325: QString getLocalFilename() const; Chris@325: Chris@325: /** Chris@678: * Return the base name, i.e. the final path element (including Chris@678: * extension, if any) of the location. Chris@678: */ Chris@678: QString getBasename() const; Chris@678: Chris@678: /** Chris@325: * Return the MIME content type of this file, if known. Chris@325: */ Chris@325: QString getContentType() const; Chris@325: Chris@325: /** Chris@1098: * Return the file extension for this file, if any. The returned Chris@1098: * extension is always lower-case. Chris@325: */ Chris@325: QString getExtension() const; Chris@325: Chris@325: /** Chris@325: * Return an error message, if isOK() is false. Chris@325: */ Chris@325: QString getErrorString() const; Chris@325: Chris@325: /** Chris@325: * Specify whether any local, cached file should remain on disc Chris@325: * after this FileSource has been destroyed. The default is false Chris@469: * (cached files share their FileSource owners' lifespans). Chris@325: */ Chris@316: void setLeaveLocalFile(bool leave); Chris@208: Chris@325: /** Chris@325: * Return true if the given filename or URL refers to a remote Chris@325: * URL. Chris@325: */ Chris@325: static bool isRemote(QString fileOrUrl); Chris@210: Chris@325: /** Chris@325: * Return true if FileSource can handle the retrieval scheme for Chris@325: * the given URL (or if the URL is for a local file). Chris@325: */ Chris@208: static bool canHandleScheme(QUrl url); Chris@208: Chris@1033: /** Chris@1033: * Print some stats, if FileSource was compiled with debugging. Chris@1033: * It's safe to leave a call to this function in release code, as Chris@1033: * long as FileSource itself is compiled with release flags. Chris@1033: */ Chris@1033: static void debugReport(); Chris@1033: Chris@208: signals: Chris@325: /** Chris@325: * Emitted during URL retrieval, when the retrieval progress Chris@325: * notches up to a new percentage. Chris@325: */ Chris@208: void progress(int percent); Chris@325: Chris@325: /** Chris@325: * Emitted when the file's existence has been tested and/or Chris@325: * response header received. Calls to isAvailable() after this Chris@325: * has been emitted will not block. Chris@325: */ Chris@325: void statusAvailable(); Chris@325: Chris@325: /** Chris@325: * Emitted when the entire file data has been retrieved and the Chris@325: * local file is complete (if no error has occurred). Chris@325: */ Chris@208: void ready(); Chris@208: Chris@208: protected slots: Chris@764: void metaDataChanged(); Chris@762: void readyRead(); Chris@762: void replyFailed(QNetworkReply::NetworkError); Chris@762: void replyFinished(); Chris@762: void downloadProgress(qint64 done, qint64 total); Chris@210: void cancelled(); Chris@208: Chris@208: protected: Chris@317: FileSource &operator=(const FileSource &); // not provided Chris@316: chris@831: QString m_rawFileOrUrl; Chris@304: QUrl m_url; Chris@208: QFile *m_localFile; Chris@762: QNetworkReply *m_reply; Chris@208: QString m_localFilename; Chris@208: QString m_errorString; Chris@315: QString m_contentType; Chris@520: QString m_preferredContentType; Chris@208: bool m_ok; Chris@981: bool m_cancelled; Chris@210: int m_lastStatus; Chris@706: bool m_resource; Chris@316: bool m_remote; Chris@208: bool m_done; Chris@316: bool m_leaveLocalFile; Chris@392: ProgressReporter *m_reporter; Chris@208: Chris@304: typedef std::map RemoteRefCountMap; Chris@304: typedef std::map RemoteLocalMap; Chris@304: static RemoteRefCountMap m_refCountMap; Chris@304: static RemoteLocalMap m_remoteLocalMap; Chris@304: static QMutex m_mapMutex; Chris@316: bool m_refCounted; Chris@316: Chris@357: void init(); Chris@762: void initRemote(); Chris@304: Chris@211: void cleanup(); Chris@211: Chris@316: // Create a local file for m_url. If it already existed, return true. Chris@316: // The local filename is stored in m_localFilename. Chris@316: bool createCacheFile(); Chris@316: void deleteCacheFile(); Chris@208: Chris@208: static QMutex m_fileCreationMutex; Chris@208: static int m_count; Chris@208: }; Chris@208: Chris@208: #endif