diff --git a/src/blackmisc/propertyindexvariantmap.h b/src/blackmisc/propertyindexvariantmap.h index 8a803473a..c399ab36d 100644 --- a/src/blackmisc/propertyindexvariantmap.h +++ b/src/blackmisc/propertyindexvariantmap.h @@ -193,7 +193,8 @@ namespace BlackMisc bool matchesVariant(const CVariant &value) const; //! True if this map matches the value - template bool matches(const T &value) const { return matchesVariant(CVariant::from(value)); } + template ::value>> + bool matches(const T &value) const { return matchesVariant(CVariant::from(value)); } //! Map const QMap &map() const { return m_values; } diff --git a/src/blackmisc/variant.cpp b/src/blackmisc/variant.cpp index f18016e3b..86be9dd29 100644 --- a/src/blackmisc/variant.cpp +++ b/src/blackmisc/variant.cpp @@ -533,6 +533,26 @@ namespace BlackMisc return toIcon().toPixmap(); } + bool CVariant::matches(const CVariant &value) const + { + if (! isValid()) { return false; } + auto *meta = getValueObjectMetaInfo(); + if (! meta) + { + CLogMessage(this).warning(u"Invalid type for CVariant::matches: %1") << typeName(); + return false; + } + try + { + return meta->matches(data(), value); + } + catch (const Private::CVariantException &ex) + { + CLogMessage(this).debug() << ex.what(); + return false; + } + } + QVariant fixQVariantFromDbusArgument(const QVariant &variant, int localUserType, const QString &typeName) { if (localUserType == static_cast(QVariant::Invalid)) diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index bcc8e3b7c..55a0774b9 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -343,6 +343,9 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::Icon::toIcon CIcon toIcon() const; + //! If this is an event subscription, return true if it matches the given event. + bool matches(const CVariant &event) const; + private: QVariant m_v; diff --git a/src/blackmisc/variantlist.cpp b/src/blackmisc/variantlist.cpp index 7dbb62b1b..84a7037fc 100644 --- a/src/blackmisc/variantlist.cpp +++ b/src/blackmisc/variantlist.cpp @@ -36,4 +36,9 @@ namespace BlackMisc QMetaType::registerConverter>([](const CVariantList &list) { return list.toVector(); }); QMetaType::registerConverter, CVariantList>([](const QVector &list) { return CSequence(list); }); } + + bool CVariantList::matches(const CVariant &event) const + { + return containsBy([ & ](const CVariant &pattern) { return pattern.matches(event); }); + } } // ns diff --git a/src/blackmisc/variantlist.h b/src/blackmisc/variantlist.h index 2fab14a1c..b3ef55648 100644 --- a/src/blackmisc/variantlist.h +++ b/src/blackmisc/variantlist.h @@ -59,6 +59,9 @@ namespace BlackMisc //! \copydoc BlackMisc::CValueObject::registerMetadata static void registerMetadata(); + + //! True if any element of the list matches the given event. + bool matches(const CVariant &event) const; }; } diff --git a/src/blackmisc/variantprivate.h b/src/blackmisc/variantprivate.h index cf10d606c..5b57931e2 100644 --- a/src/blackmisc/variantprivate.h +++ b/src/blackmisc/variantprivate.h @@ -59,6 +59,7 @@ namespace BlackMisc virtual void propertyByIndex(const void *object, CVariant &o_variant, const BlackMisc::CPropertyIndex &index) const = 0; virtual QString propertyByIndexAsString(const void *object, const CPropertyIndex &index, bool i18n) const = 0; virtual bool equalsPropertyByIndex(const void *object, const CVariant &compareValue, const CPropertyIndex &index) const = 0; + virtual bool matches(const void *object, const CVariant &value) const = 0; virtual void toIcon(const void *object, CIcon &o_icon) const = 0; }; @@ -141,6 +142,11 @@ namespace BlackMisc static void toIcon(const T &object, CIcon &o_icon, std::enable_if_t < ! std::is_same::value, decltype(static_cast(object.toIcon()), 0) >) { assign(o_icon, object.toIcon()); } template static void toIcon(const T &object, CIcon &, ...) { throw CVariantException(object, "toIcon"); } + + template + static bool matches(const T &object, const CVariant &value, decltype(static_cast(object.matches(value)), 0)) { return object.matches(value); } + template + static bool matches(const T &object, const CVariant &, ...) { throw CVariantException(object, "matches"); } }; //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. @@ -208,6 +214,10 @@ namespace BlackMisc { CValueObjectMetaInfoHelper::toIcon(cast(object), o_icon, 0); } + virtual bool matches(const void *object, const CVariant &value) const override + { + return CValueObjectMetaInfoHelper::matches(cast(object), value, 0); + } static const T &cast(const void *object) { return *static_cast(object); } static T &cast(void *object) { return *static_cast(object); } diff --git a/tests/blackmisc/testvariantandmap/testvariantandmap.cpp b/tests/blackmisc/testvariantandmap/testvariantandmap.cpp index 1a0c52505..3f871c7e5 100644 --- a/tests/blackmisc/testvariantandmap/testvariantandmap.cpp +++ b/tests/blackmisc/testvariantandmap/testvariantandmap.cpp @@ -29,6 +29,8 @@ #include "blackmisc/variant.h" #include "test.h" +#include +#include #include #include @@ -55,10 +57,32 @@ namespace BlackMiscTest //! Unit tests for variant lists void variantList(); + //! Unit tests for matches feature + void matches(); + //! Unit tests for value maps and value objects void valueMap(); }; + //! \private + class CTestMatcher : public BlackMisc::Mixin::MetaType + { + public: + bool matches(const BlackMisc::CVariant &) const { return true; } + + // methods needed for metatype registration + QString toQString(bool) const { return {}; } + friend QDBusArgument &operator <<(QDBusArgument &arg, const CTestMatcher &) { return arg; } + friend const QDBusArgument &operator >>(const QDBusArgument &arg, CTestMatcher &) { return arg; } + friend QDataStream &operator <<(QDataStream &ds, const CTestMatcher &) { return ds; } + friend QDataStream &operator >>(QDataStream &ds, CTestMatcher &) { return ds; } + }; +} // namespace + +Q_DECLARE_METATYPE(BlackMiscTest::CTestMatcher) + +namespace BlackMiscTest +{ void CTestVariantAndMap::initTestCase() { BlackMisc::registerMetadata(); @@ -134,6 +158,14 @@ namespace BlackMiscTest QVERIFY2(list == variant.to(), "Variant list converted back compares equal"); } + void CTestVariantAndMap::matches() + { + CTestMatcher::registerMetadata(); + const CTestMatcher matcher {}; + const CVariant variant = CVariant::from(matcher); + QVERIFY2(variant.matches(CVariant()), "Variant provides access to stored object's matches() method"); + } + void CTestVariantAndMap::valueMap() { // ATC station