/* Copyright (C) 2013 * 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_STATUSMESSAGE_H #define BLACKMISC_STATUSMESSAGE_H #include "blackmiscexport.h" #include "icon.h" #include "propertyindex.h" #include "logcategorylist.h" #include "timestampbased.h" #include "typetraits.h" #include namespace BlackMisc { class CStatusException; namespace Private { //! Like QString::arg() but accepts a QStringList of args. BLACKMISC_EXPORT QString arg(const QString &format, const QStringList &args); } /*! * Status severities */ enum StatusSeverity { SeverityDebug, SeverityInfo, SeverityWarning, SeverityError }; /*! * Base class for CStatusMessage and CLogMessage. */ template class CMessageBase { public: //! Default constructor. CMessageBase() {} //! Construct a message with some specific category. explicit CMessageBase(const CLogCategory &category) : m_categories({ category }) {} //! Construct a message with some specific categories. explicit CMessageBase(const CLogCategoryList &categories) : m_categories(categories) {} //! Construct a message with some specific categories. CMessageBase(const CLogCategoryList &categories, const CLogCategory &extra) : CMessageBase(categories) { m_categories.push_back(extra); } //! Construct a message with some specific categories. CMessageBase(const CLogCategoryList &categories, const CLogCategoryList &extra) : CMessageBase(categories) { m_categories.push_back(extra); } //! Set the severity to debug. Derived &debug() { return setSeverityAndMessage(SeverityDebug, ""); } //! Set the severity to info, providing a format string. Derived &info(QString format) { return setSeverityAndMessage(SeverityInfo, format); } //! Set the severity to warning, providing a format string. Derived &warning(QString format) { return setSeverityAndMessage(SeverityWarning, format); } //! Set the severity to error, providing a format string. Derived &error(QString format) { return setSeverityAndMessage(SeverityError, format); } //! Set the severity to info, providing a format string, and adding the validation category. Derived &validationInfo(QString format) { setValidation(); return setSeverityAndMessage(SeverityInfo, format); } //! Set the severity to warning, providing a format string, and adding the validation category. Derived &validationWarning(QString format) { setValidation(); return setSeverityAndMessage(SeverityWarning, format); } //! Set the severity to error, providing a format string, and adding the validation category. Derived &validationError(QString format) { setValidation(); return setSeverityAndMessage(SeverityError, format); } //! Streaming operators. //! \details If the format string is empty, the message will consist of all streamed values separated by spaces. //! Otherwise, the streamed values will replace the place markers %1, %2, %3... in the format string. //! \see QString::arg //! @{ Derived &operator <<(const QString &v) { return arg(v); } Derived &operator <<(int v) { return arg(QString::number(v)); } Derived &operator <<(uint v) { return arg(QString::number(v)); } Derived &operator <<(long v) { return arg(QString::number(v)); } Derived &operator <<(ulong v) { return arg(QString::number(v)); } Derived &operator <<(qlonglong v) { return arg(QString::number(v)); } Derived &operator <<(qulonglong v) { return arg(QString::number(v)); } Derived &operator <<(short v) { return arg(QString::number(v)); } Derived &operator <<(ushort v) { return arg(QString::number(v)); } Derived &operator <<(QChar v) { return arg(v); } Derived &operator <<(char v) { return arg(QChar(v)); } Derived &operator <<(double v) { return arg(QString::number(v)); } template ::value>> Derived &operator <<(const T &v) { return arg(v.toQString()); } //! @} private: void setValidation() { m_categories.remove(CLogCategory::uncategorized()); m_categories.push_back(CLogCategory::validation()); } Derived &setSeverityAndMessage(StatusSeverity s, const QString &m) { m_message = m; m_severity = s; return derived(); } Derived &arg(QString value) { m_args.push_back(value); return derived(); } Derived &derived() { return static_cast(*this); } protected: //! \private //! @{ QString m_message; QStringList m_args; CLogCategoryList m_categories = CLogCategoryList { CLogCategory::uncategorized() }; StatusSeverity m_severity = SeverityDebug; QString message() const { return Private::arg(m_message, m_args); } //! @} }; /*! * Streamable status message, e.g. from Core -> GUI */ class BLACKMISC_EXPORT CStatusMessage : public CValueObject, public CMessageBase, public ITimestampBased { public: //! \copydoc BlackMisc::StatusSeverity //! @{ using StatusSeverity = BlackMisc::StatusSeverity; constexpr static auto SeverityDebug = BlackMisc::SeverityDebug; constexpr static auto SeverityInfo = BlackMisc::SeverityInfo; constexpr static auto SeverityWarning = BlackMisc::SeverityWarning; constexpr static auto SeverityError = BlackMisc::SeverityError; //! @} //! Properties by index enum ColumnIndex { IndexCategoriesAsString = BlackMisc::CPropertyIndex::GlobalIndexCStatusMessage, IndexCategoriesHumanReadableAsString, IndexCategoryHumanReadableOrTechnicalAsString, IndexSeverity, IndexSeverityAsString, IndexMessage }; //! Inheriting constructors. using CMessageBase::CMessageBase; //! Constructor CStatusMessage(); //! Copy constructor (because of mutex) CStatusMessage(const CStatusMessage &other); //! Copy assignment (because of mutex) CStatusMessage &operator =(const CStatusMessage &other); //! Constructor CStatusMessage(const QString &message); //! Constructor CStatusMessage(StatusSeverity severity, const QString &message); //! Constructor, also a verification messsage can be directly created CStatusMessage(const CLogCategoryList &categories, StatusSeverity severity, const QString &message, bool verification = false); //! Construct from a Qt logging triple //! \sa QtMessageHandler CStatusMessage(QtMsgType type, const QMessageLogContext &context, const QString &message); //! Convert to a Qt logging triple //! \sa QtMessageHandler void toQtLogTriple(QtMsgType *o_type, QString *o_category, QString *o_message) const; //! Return a throwable exception object containing this status message. CStatusException asException() const; //! If message is empty then do nothing, otherwise throw a CStatusException. void maybeThrow() const; //! Message categories const CLogCategoryList &getCategories() const { return this->m_categories; } //! Message categories as string QString getCategoriesAsString() const; //! Human readable category QString getHumanReadablePattern() const; //! All human readable categories QStringList getHumanReadablePatterns() const; //! The human or technical categories QString getHumanOrTechnicalCategoriesAsString() const; //! Message severity StatusSeverity getSeverity() const { return this->m_severity; } //! Info or debug, no warning or error bool isSeverityInfoOrLess() const { return this->m_severity == SeverityInfo || this->m_severity == SeverityDebug; } //! Warning or above bool isWarningOrAbove() const { return this->m_severity == SeverityWarning || this->m_severity == SeverityError; } //! Operation considered successful bool isSuccess() const; //! Operation considered unsuccessful bool isFailure() const; //! Message QString getMessage() const { return this->message(); } //! Prepend message void prependMessage(const QString &msg); //! Append message void appendMessage(const QString &msg); //! Message empty bool isEmpty() const { return this->m_message.isEmpty() && this->m_args.isEmpty(); } //! Returns true if this message was sent by an instance of class T. template bool isFromClass(const T *pointer = nullptr) const { CLogCategoryList classCategories(pointer); return std::all_of(classCategories.begin(), classCategories.end(), [this](const CLogCategory & cat) { return m_categories.contains(cat); }); } //! Mark the message as having been handled by the given object void markAsHandledBy(const QObject *object) const; //! Returns true if the message was marked as having been handled by the given object bool wasHandledBy(const QObject *object) const; //! Severity void setSeverity(StatusSeverity severity) { this->m_severity = severity; } //! Add category void addCategory(const CLogCategory &category) { this->m_categories.push_back(category); } //! Adds verification as category void addVerificationCategory(); //! Add categories void addCategories(const CLogCategoryList &categories) { this->m_categories.push_back(categories); } //! Reset category void setCategory(const CLogCategory &category) { this->m_categories = CLogCategoryList { category }; } //! Reset categories void setCategories(const CLogCategoryList &categories) { this->m_categories = categories; } //! Representing icon CIcon toIcon() const { return convertToIcon(*this); } //! Severity as string const QString &getSeverityAsString() const; //! Severity as string static const QString &severityToString(StatusSeverity severity); //! Severity set as string static QString severitiesToString(const QSet &severities); //! Severity as string, if not possible to convert \sa CSeverityInfo static StatusSeverity stringToSeverity(const QString &severity); //! Severities as strings static const QStringList &allSeverityStrings(); //! \copydoc BlackMisc::Mixin::Index::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); //! Compare for index int comparePropertyByIndex(const CStatusMessage &compareValue, const CPropertyIndex &index) const; //! To HTML QString toHtml() const; //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; //! Representing icon static const CIcon &convertToIcon(const CStatusMessage &statusMessage); //! Representing icon static const CIcon &convertToIcon(CStatusMessage::StatusSeverity severity); //! Object from JSON static CStatusMessage fromDatabaseJson(const QJsonObject &json); //! \copydoc BlackMisc::CValueObject::registerMetadata static void registerMetadata(); private: BLACK_ENABLE_TUPLE_CONVERSION(CStatusMessage) mutable QVector m_handledByObjects; mutable QReadWriteLock m_lock; //!< lock (because of mutable members) }; } // namespace BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CStatusMessage, ( o.m_categories, o.m_severity, o.m_message, o.m_args, o.m_timestampMSecsSinceEpoch )) Q_DECLARE_METATYPE(BlackMisc::CStatusMessage) Q_DECLARE_METATYPE(BlackMisc::CStatusMessage::StatusSeverity) #endif // guard