From 50442e6f131486f10c48adeef24ecf05843e877b Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 16 Aug 2017 12:11:53 +0200 Subject: [PATCH] Ref T125, value class for remote files (i.e. files downloaded). Corresponds with backend service T132 --- src/blackmisc/network/network.h | 2 + .../network/registermetadatanetwork.cpp | 2 + src/blackmisc/network/remotefile.cpp | 115 ++++++++++++++++ src/blackmisc/network/remotefile.h | 130 ++++++++++++++++++ src/blackmisc/network/remotefilelist.cpp | 81 +++++++++++ src/blackmisc/network/remotefilelist.h | 71 ++++++++++ src/blackmisc/propertyindex.h | 43 +++--- 7 files changed, 423 insertions(+), 21 deletions(-) create mode 100644 src/blackmisc/network/remotefile.cpp create mode 100644 src/blackmisc/network/remotefile.h create mode 100644 src/blackmisc/network/remotefilelist.cpp create mode 100644 src/blackmisc/network/remotefilelist.h diff --git a/src/blackmisc/network/network.h b/src/blackmisc/network/network.h index eebb6d8ab..94ec9562a 100644 --- a/src/blackmisc/network/network.h +++ b/src/blackmisc/network/network.h @@ -24,6 +24,8 @@ #include "blackmisc/network/fsdsetup.h" #include "blackmisc/network/role.h" #include "blackmisc/network/rolelist.h" +#include "blackmisc/network/remotefile.h" +#include "blackmisc/network/remotefilelist.h" #include "blackmisc/network/server.h" #include "blackmisc/network/serverlist.h" #include "blackmisc/network/textmessage.h" diff --git a/src/blackmisc/network/registermetadatanetwork.cpp b/src/blackmisc/network/registermetadatanetwork.cpp index de6537e13..453f91959 100644 --- a/src/blackmisc/network/registermetadatanetwork.cpp +++ b/src/blackmisc/network/registermetadatanetwork.cpp @@ -21,6 +21,8 @@ namespace BlackMisc CClientList::registerMetadata(); CEntityFlags::registerMetadata(); CFsdSetup::registerMetadata(); + CRemoteFile::registerMetadata(); + CRemoteFileList::registerMetadata(); CRole::registerMetadata(); CRoleList::registerMetadata(); CServer::registerMetadata(); diff --git a/src/blackmisc/network/remotefile.cpp b/src/blackmisc/network/remotefile.cpp new file mode 100644 index 000000000..4c0382974 --- /dev/null +++ b/src/blackmisc/network/remotefile.cpp @@ -0,0 +1,115 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/network/remotefile.h" +#include "blackmisc/stringutils.h" +#include +#include + +namespace BlackMisc +{ + namespace Network + { + CRemoteFile::CRemoteFile(const QString &name, const QString &description) + : m_name(name), m_description(description) + { } + + CRemoteFile::CRemoteFile(const QString &name, qint64 size, const QString &url) + : m_name(name), m_url(url), m_size(size) + { } + + QString CRemoteFile::getNameAndSize() const + { + static const QString s("%1 (%2)"); + return s.arg(this->getName(), this->getSizeHumanReadable()); + } + + bool CRemoteFile::matchesName(const QString &name) const + { + if (name.isEmpty()) { return false; } + if (caseInsensitiveStringCompare(name, this->getName())) { return true; } + if (name.startsWith(this->getName(), Qt::CaseInsensitive)) { return true; } + if (this->getName().startsWith(name, Qt::CaseInsensitive)) { return true; } + return false; + } + + void CRemoteFile::setUrl(const QString &url) + { + this->setUrl(CUrl(url)); + } + + QString CRemoteFile::getSizeHumanReadable() const + { + return CFileUtils::humanReadableFileSize(this->getSize()); + } + + QString CRemoteFile::getFormattedCreatedYmdhms() const + { + if (this->m_created < 1) { return ""; } + const QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_created); + return dt.toString("yyyy-MM-dd HH:mm:ss"); + } + + QString CRemoteFile::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + return "Name: " + m_name + + " description: " + m_description + + " size: " + QString::number(m_size) + + " modified: " + this->getFormattedUtcTimestampYmdhms() + + " created: " + this->getFormattedUtcTimestampYmdhms() + + " URL: " + m_url.getFullUrl(); + } + + CVariant CRemoteFile::propertyByIndex(const CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexName: return CVariant::fromValue(this->m_name); + case IndexDescription: return CVariant::fromValue(this->m_description); + case IndexUrl: return CVariant::fromValue(this->m_url); + case IndexSize: return CVariant::fromValue(this->m_size); + default: return CValueObject::propertyByIndex(index); + } + } + + void CRemoteFile::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexName: this->setName(variant.value()); break; + case IndexDescription: this->setDescription(variant.value()); break; + case IndexUrl: this->m_url.setPropertyByIndex(index.copyFrontRemoved(), variant); + case IndexSize: this->setSize(variant.toInt()); + default: CValueObject::setPropertyByIndex(index, variant); break; + } + } + + CRemoteFile CRemoteFile::fromDatabaseJson(const QJsonObject &json) + { + CRemoteFile file; + file.setName(json.value("name").toString()); + file.setDescription(json.value("description").toString()); + file.setUrl(json.value("url").toString()); + const qint64 size = json.value("size").toInt(); + file.setSize(size); + const qint64 created = json.value("tsCreated").toInt(); + const qint64 lastUpdated = json.value("tsLastUpdated").toInt(); + file.m_created = created; + file.setMSecsSinceEpoch(lastUpdated); + return file; + } + } // ns +} // ns diff --git a/src/blackmisc/network/remotefile.h b/src/blackmisc/network/remotefile.h new file mode 100644 index 000000000..38f5676b8 --- /dev/null +++ b/src/blackmisc/network/remotefile.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_NETWORK_REMOTEFILE_H +#define BLACKMISC_NETWORK_REMOTEFILE_H + +#include "url.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/timestampbased.h" +#include "blackmisc/metaclass.h" +#include "blackmisc/propertyindex.h" +#include "blackmisc/valueobject.h" +#include "blackmisc/variant.h" + +#include +#include +#include + +namespace BlackMisc +{ + namespace Network + { + /*! + * Remote file, i.e. a file residing on a server + */ + class BLACKMISC_EXPORT CRemoteFile : + public CValueObject, + public BlackMisc::ITimestampBased + { + public: + //! Properties by index + enum ColumnIndex + { + IndexName = BlackMisc::CPropertyIndex::GlobalIndexCRemoteFile, + IndexDescription, + IndexUrl, + IndexSize + }; + + //! Constructor + CRemoteFile() = default; + + //! Constructor + CRemoteFile(const QString &name, const QString &description); + + //! Constructor + CRemoteFile(const QString &name, qint64 size, const QString &url); + + //! Name + const QString &getName() const { return m_name; } + + //! Name + human readable size + QString getNameAndSize() const; + + //! Name + void setName(const QString &name) { m_name = name.trimmed(); } + + //! Matching name? + bool matchesName(const QString &name) const; + + //! Description + const QString &getDescription() const { return m_description; } + + //! Description + void setDescription(const QString &description) { m_description = description.trimmed(); } + + //! Get URL + const CUrl &getUrl() const { return m_url; } + + //! Set URL + void setUrl(const CUrl &url) { m_url = url; } + + //! Set URL + void setUrl(const QString &url); + + //! Get size + qint64 getSize() const { return m_size; } + + //! Human readable file size, e.g. 2KB + QString getSizeHumanReadable() const; + + //! Set size + void setSize(qint64 size) { m_size = size; } + + //! Created timestamp + QString getFormattedCreatedYmdhms() const; + + //! \copydoc BlackMisc::Mixin::String::toQString + QString convertToQString(bool i18n = false) const; + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + + //! Role from DB JSON + static CRemoteFile fromDatabaseJson(const QJsonObject &json); + + private: + QString m_name; + QString m_description; + CUrl m_url; + qint64 m_size = 0; + qint64 m_created = 0; + + BLACK_METACLASS( + CRemoteFile, + BLACK_METAMEMBER(timestampMSecsSinceEpoch), + BLACK_METAMEMBER(name), + BLACK_METAMEMBER(description), + BLACK_METAMEMBER(url), + BLACK_METAMEMBER(size), + BLACK_METAMEMBER(created) + ); + }; + } // ns +} // ns + +Q_DECLARE_METATYPE(BlackMisc::Network::CRemoteFile) + +#endif // guard diff --git a/src/blackmisc/network/remotefilelist.cpp b/src/blackmisc/network/remotefilelist.cpp new file mode 100644 index 000000000..d89bcebc3 --- /dev/null +++ b/src/blackmisc/network/remotefilelist.cpp @@ -0,0 +1,81 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/network/remotefilelist.h" +#include "blackmisc/network/remotefile.h" +#include "blackmisc/json.h" + +#include +#include +#include + +namespace BlackMisc +{ + namespace Network + { + CRemoteFileList::CRemoteFileList() { } + + CRemoteFileList::CRemoteFileList(const CSequence &other) : + CSequence(other) + { } + + QStringList CRemoteFileList::getNames(bool sorted) const + { + QStringList fileNames; + for (const CRemoteFile &rf : *this) + { + fileNames.append(rf.getName()); + } + if (sorted) { fileNames.sort(); } + return fileNames; + } + + QStringList CRemoteFileList::getNamesPlusSize(bool sorted) const + { + QStringList fileNames; + for (const CRemoteFile &rf : *this) + { + fileNames.append(rf.getNameAndSize()); + } + if (sorted) { fileNames.sort(); } + return fileNames; + } + + CRemoteFile CRemoteFileList::findFirstByNameOrDefault(const QString &name) const + { + if (name.isEmpty()) { return CRemoteFile(); } + return this->findFirstByOrDefault(&CRemoteFile::getName, name); + } + + CRemoteFile CRemoteFileList::findFirstMatchingNameOrDefault(const QString &name) const + { + for (const CRemoteFile &file : *this) + { + if (file.matchesName(name)) { return file; } + } + return CRemoteFile(); + } + + CRemoteFileList CRemoteFileList::fromDatabaseJson(const QJsonArray &array) + { + CRemoteFileList roles; + for (const QJsonValue &value : array) + { + roles.push_back(CRemoteFile::fromDatabaseJson(value.toObject())); + } + return roles; + } + + CRemoteFileList CRemoteFileList::fromDatabaseJson(const QString &json) + { + if (json.isEmpty()) { return CRemoteFileList(); } + return CRemoteFileList::fromDatabaseJson(Json::jsonArrayFromString(json)); + } + } // namespace +} // namespace diff --git a/src/blackmisc/network/remotefilelist.h b/src/blackmisc/network/remotefilelist.h new file mode 100644 index 000000000..e7e46b935 --- /dev/null +++ b/src/blackmisc/network/remotefilelist.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_NETWORK_REMOTEFILELIST_H +#define BLACKMISC_NETWORK_REMOTEFILELIST_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/collection.h" +#include "blackmisc/network/remotefile.h" +#include "blackmisc/sequence.h" +#include "blackmisc/variant.h" + +#include +#include +#include +#include + +namespace BlackMisc +{ + namespace Network + { + class CRemoteFile; + + //! Value object encapsulating a list of servers. + class BLACKMISC_EXPORT CRemoteFileList : + public CSequence, + public BlackMisc::Mixin::MetaType + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CRemoteFileList) + + //! Default constructor. + CRemoteFileList(); + + //! Construct from a base class object. + CRemoteFileList(const CSequence &other); + + //! All file names + QStringList getNames(bool sorted = true) const; + + //! All file names plus size + QStringList getNamesPlusSize(bool sorted = true) const; + + //! First by name of default + CRemoteFile findFirstByNameOrDefault(const QString &name) const; + + //! Find first matching name of default + CRemoteFile findFirstMatchingNameOrDefault(const QString &name) const; + + //! From our database JSON format + static CRemoteFileList fromDatabaseJson(const QJsonArray &array); + + //! From our database JSON format + static CRemoteFileList fromDatabaseJson(const QString &json); + }; + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Network::CRemoteFileList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 4ae66ca83..9f5e60dc7 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -120,27 +120,28 @@ namespace BlackMisc GlobalIndexCServer = 6500, GlobalIndexCFsdSetup = 6600, GlobalIndexCUrl = 6700, - GlobalIndexCAircraftModel = 6800, - GlobalIndexCSimulatedAircraft = 6900, - GlobalIndexCTextMessage = 7000, - GlobalIndexCSimulatorInternals = 7100, - GlobalIndexCSimulatorSettings = 7200, - GlobalIndexCSwiftPluignSettings = 7300, - GlobalIndexCSimulatorMessageSettings = 7400, - GlobalIndexCModelSettings = 7500, - GlobalIndexCAircraftCfgEntries = 7600, - GlobalIndexCDistributor = 7700, - GlobalIndexCMatchingStatisticsEntry = 7800, - GlobalIndexCVPilotModelRule = 8000, - GlobalIndexCVoiceRoom = 9000, - GlobalIndexCSettingKeyboardHotkey = 10000, - GlobalIndexIDatastoreInteger = 11000, - GlobalIndexIDatastoreString = 11100, - GlobalIndexCDbInfo = 11200, - GlobalIndexCGlobalSetup = 12000, - GlobalIndexCDistribution = 12100, - GlobalIndexCVatsimSetup = 12200, - GlobalIndexCLauncherSetup = 12300, + GlobalIndexCRemoteFile = 6800, + GlobalIndexCAircraftModel = 7000, + GlobalIndexCSimulatedAircraft = 7100, + GlobalIndexCTextMessage = 7200, + GlobalIndexCSimulatorInternals = 7300, + GlobalIndexCSimulatorSettings = 7400, + GlobalIndexCSwiftPluignSettings = 7500, + GlobalIndexCSimulatorMessageSettings = 7600, + GlobalIndexCModelSettings = 7700, + GlobalIndexCAircraftCfgEntries = 7800, + GlobalIndexCDistributor = 7900, + GlobalIndexCMatchingStatisticsEntry = 8000, + GlobalIndexCVPilotModelRule = 9000, + GlobalIndexCVoiceRoom = 10000, + GlobalIndexCSettingKeyboardHotkey = 11000, + GlobalIndexIDatastoreInteger = 12000, + GlobalIndexIDatastoreString = 12100, + GlobalIndexCDbInfo = 12200, + GlobalIndexCGlobalSetup = 13000, + GlobalIndexCDistribution = 13100, + GlobalIndexCVatsimSetup = 13200, + GlobalIndexCLauncherSetup = 13300, GlobalIndexCGuiStateDbOwnModelsComponent = 14000, GlobalIndexCGuiStateDbOwnModelSetComponent = 14100, GlobalIndexCDockWidgetSettings = 14200,