From d46c5edb9f146a9e0f94497845cc32c0ba26c9c8 Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Mon, 30 Jun 2014 00:15:16 +0200 Subject: [PATCH] Generic CDictionary as associative container refs #281 --- src/blackmisc/containerbase.h | 29 ++- src/blackmisc/dictionary.h | 421 ++++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+), 8 deletions(-) create mode 100644 src/blackmisc/dictionary.h diff --git a/src/blackmisc/containerbase.h b/src/blackmisc/containerbase.h index a80aeb6e4..e4fd13c30 100644 --- a/src/blackmisc/containerbase.h +++ b/src/blackmisc/containerbase.h @@ -21,6 +21,26 @@ namespace BlackMisc { + //! Class providing static helper methods for different containers + class CContainerHelper + { + public: + //! Stringify value object + template static QString stringify(const U &obj, bool i18n) { return obj.toQString(i18n); } + //! Stringify int + static QString stringify(int n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } + //! Stringify uint + static QString stringify(uint n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } + //! Stringify qlonglong + static QString stringify(qlonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } + //! Stringify qulonglong + static QString stringify(qulonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } + //! Stringify double + static QString stringify(double n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } + //! Stringify QString + static QString stringify(QString str, bool /*i18n*/) { return str; } + }; + /*! * \brief Base class for CCollection and CSequence implementing their algorithms. */ @@ -190,7 +210,7 @@ namespace BlackMisc { QString str; // qualifying stringify with this-> to workaround bug in GCC 4.7.2 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402 - std::for_each(derived().cbegin(), derived().cend(), [ & ](const T & value) { str += (str.isEmpty() ? "{" : ", ") + this->stringify(value, i18n); }); + std::for_each(derived().cbegin(), derived().cend(), [ & ](const T & value) { str += (str.isEmpty() ? "{" : ", ") + CContainerHelper::stringify(value, i18n); }); if (str.isEmpty()) { str = "{"; } return str += "}"; } @@ -238,13 +258,6 @@ namespace BlackMisc private: C &derived() { return static_cast &>(*this); } const C &derived() const { return static_cast &>(*this); } - - template static QString stringify(const U &obj, bool i18n) { return obj.toQString(i18n); } - static QString stringify(int n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } - static QString stringify(uint n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } - static QString stringify(qlonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } - static QString stringify(qulonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } - static QString stringify(double n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); } }; } diff --git a/src/blackmisc/dictionary.h b/src/blackmisc/dictionary.h new file mode 100644 index 000000000..27e8c4519 --- /dev/null +++ b/src/blackmisc/dictionary.h @@ -0,0 +1,421 @@ +/* Copyright (C) 2013 VATSIM Community / contributors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BLACKMISC_DICTIONARY_H +#define BLACKMISC_DICTIONARY_H + +#include "valueobject.h" +#include "sequence.h" +#include "collection.h" +#include + +namespace BlackMisc +{ + //! Associative container with value semantics, chooses a sensible default implementation container type + template class Impl> + class CDictionary : public CValueObject + { + + public: + + //! \brief STL compatibility + //! @{ + typedef Key key_type; + typedef Value value_type; + typedef Value &reference; + typedef const Value &const_reference; + typedef typename Impl::size_type size_type; + + typedef typename Impl::iterator iterator; + typedef typename Impl::const_iterator const_iterator; + //! @} + + + //! Return keys as collection + CCollection keysCollection() const + { + CCollection collection; + for (auto it = m_impl.begin(); it != m_impl.end(); ++it) + { + collection.push_back(it.key()); + } + return collection; + } + + //! Return values as collection + CCollection valuesCollection() const + { + CCollection collection; + for (auto it = m_impl.begin(); it != m_impl.end(); ++it) + { + collection.push_back(it.value()); + } + return collection; + } + + //! Return keys as sequence + CSequence keysSequence() const + { + CSequence sequence; + for (auto it = m_impl.begin(); it != m_impl.end(); ++it) + { + sequence.push_back(it.key()); + } + return sequence; + } + + //! Return values as sequence + CSequence valuesSequence() const + { + CSequence sequence; + for (auto it = m_impl.begin(); it != m_impl.end(); ++it) + { + sequence.push_back(it.value()); + } + return sequence; + } + + + //! Return a copy containing only those elements for which the dictionary keys return true for a given predicate. + template + CDictionary findKeyBy(Predicate p) const + { + CDictionary result = *this; + for (auto it = result.begin(); it != result.end();) + { + if (!p(it.key())) { it = result.erase(it); } + else { ++it; } + } + return result; + } + + /*! + * \brief Return a copy containing only those elements which key matches a particular pair. + * \param pairs Pairs of { pointer to member function of Value, return value to compare it against }. + */ + template + CDictionary findKeyBy(Pairs... pairs) const + { + return findKeyBy(BlackMisc::Predicates::MemberEqual(pairs...)); + } + + /*! + * \brief Return a copy containing only those elements for which a given predicate returns true. + */ + template + CDictionary findValueBy(Predicate p) const + { + CDictionary result = *this; + for (auto it = result.begin(); it != result.end();) + { + if (!p(it.value())) { it = result.erase(it); } + else { ++it; } + } + return result; + } + + /*! + * \brief Return a copy containing only those elements which value matches a particular pair. + * \param pairs Pairs of { pointer to member function of Value, return value to compare it against }. + */ + template + CDictionary findValueBy(Pairs... pairs) const + { + return findValueBy(BlackMisc::Predicates::MemberEqual(pairs...)); + } + + /*! + * \brief Return true if there is an element for which a given predicate returns true. + */ + template + bool containsByKey(Predicate p) const + { + auto keys = m_impl.keys(); + return std::any_of(keys.cbegin(), keys.cend(), p); + } + + //! Return true if there is an element which key matches a given pair. + template + bool containsByKey(MembFunc membFunc, ReturnValue returnValue) const + { + return containsByKey(BlackMisc::Predicates::MemberEqual(membFunc, returnValue)); + } + + //! Return true if there is an element for which a given predicate returns true. + template + bool containsByValue(Predicate p) const + { + return std::any_of(m_impl.cbegin(), m_impl.cend(), p); + } + + //! Return true if there is an element which value matches a given pair. + template + bool containsByValue(MembFunc membFunc, ReturnValue returnValue) const + { + return containsByValue(BlackMisc::Predicates::MemberEqual(membFunc, returnValue)); + } + + //! Remove elements for which a given predicate for value returns true. + template + void removeByKeyIf(Predicate p) + { + for (auto it = m_impl.begin(); it != m_impl.end();) + { + if (p(it.key())) { it = m_impl.erase(it); } + else { ++it; } + } + } + + //! Remove elements for which a given predicate for key returns true. + template + void removeByValueIf(Predicate p) + { + for (auto it = m_impl.begin(); it != m_impl.end();) + { + if (p(it.value())) { it = m_impl.erase(it); } + else { ++it; } + } + } + + //! Remove elements for which key matches a particular pair. + template + void removeByKeyIf(MembFunc membFunc, ReturnValue returnValue) + { + removeByKeyIf(BlackMisc::Predicates::MemberEqual(membFunc, returnValue)); + } + + //! Remove elements for which value matches a particular pair. + template + void removeByValueIf(MembFunc membFunc, ReturnValue returnValue) + { + removeByValueIf(BlackMisc::Predicates::MemberEqual(membFunc, returnValue)); + } + + //! \copydoc BlackMisc::CValueObject::toQVariant + virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); } + + //! \copydoc BlackMisc::CValueObject::getValueHash + virtual uint getValueHash() const override { return qHash(this); } + + //! \copydoc CValueObject::toJson + virtual QJsonObject toJson() const override + { + QJsonArray array; + QJsonObject json; + + for (auto it = m_impl.cbegin(); it != m_impl.cend(); ++it) + { + array << it.key() << it.value(); + } + json.insert("associativecontainerbase", array); + return json; + } + + //! \copydoc CValueObject::fromJson + void fromJson(const QJsonObject &json) override + { + QJsonArray array = json.value("associativecontainerbase").toArray(); + for (auto it = array.begin(); it != array.end(); ++it) + { + QJsonValueRef jsonKey = (*it); + ++it; + QJsonValueRef jsonValue = (*it); + Key key; + Value value; + jsonKey >> key; + jsonValue >> value; + m_impl.insert(key, value); + } + } + + //! Default constructor. + CDictionary() {} + + //! Copy constructor + CDictionary(const CDictionary &) = default; + + //! Virtual destructor + virtual ~CDictionary() {} + + //! Returns iterator at the beginning of the dictionary + iterator begin() { return m_impl.begin(); } + + //! Returns const iterator at the beginning of the dictionary + const_iterator begin() const { return m_impl.begin(); } + + //! Returns const iterator at the beginning of the dictionary + const_iterator cbegin() const { return m_impl.cbegin(); } + + //! Returns iterator at the end of the dictionary + iterator end() { return m_impl.end(); } + + //! Returns const iterator at the end of the dictionary + const_iterator end() const { return m_impl.end(); } + + //! Returns const iterator at the end of the dictionary + const_iterator cend() const { return m_impl.cend(); } + + //! Removes all items from the dictionary + void clear() { m_impl.clear(); } + + //! Returns const iterator at the beginning of the dictionary + const_iterator constBegin() const { return m_impl.constBegin(); } + + //! Returns const iterator at the end of the dictionary + const_iterator constEnd() const { return m_impl.constEnd(); } + + /*! + * \brief Returns an const iterator pointing to the item with the key. + * \param key + * \return If key is not found, the function returns constEnd() + */ + const_iterator constFind (const Key &key) const { return m_impl.constFind(key); } + + /*! + * \brief Returns an const iterator pointing to the item with the key. + * \param key + * \return If key is not found, the function returns end() + */ + const_iterator find(const Key & key) const { return m_impl.find(key); } + + /*! + * \brief Returns an iterator pointing to the item with the key. + * \param key + * \return If key is not found, the function returns end() + */ + iterator find(const Key &key) { return m_impl.find(key); } + + //! Returns true if dictionary contains an item with key, otherwise false + bool contains (const Key &key) const {return m_impl.contains(); } + + //! Returns the number of items with key + int count(const Key &key) const { return m_impl.count(key); } + + //! Returns the size of the dictionary + int count() const { return m_impl.count(); } + + //! Returns true if the + bool empty() const { return m_impl.empty(); } + + //! Removes the key/value pair iterator is currently pointing to and returns an iterator to the next item. + iterator erase(iterator pos) { return m_impl.erase(pos); } + + //! Insert new item with key and value + iterator insert(const Key &key, const Value &value) { return m_impl.insert(key, value); } + + //! Returns true if dictionary is empty + bool isEmpty() const { return m_impl.isEmpty(); } + + //! Return key assigned to value + const Key key(const Value &value) const { return m_impl.key(value); } + + //! Return key assigned to value or if key is not found defaultKey + const Key key(const Value &value, const Key & defaultKey) const { return m_impl.key(value, defaultKey); } + + //! Return a collection of all keys + CCollection keys() const { return this->keysCollection(); } + + //! Remove all items with key from the dictionary + int remove(const Key &key) { return m_impl.remove(key); } + + //! Returns the number of items in the hash. + int size() const { return m_impl.size(); } + + //! Swaps hash other with this hash. This operation is very fast and never fails. + void swap(CDictionary &other) { m_impl.swap(other); } + + //! Returns the value associated with the key. + const Value value(const Key &key) const { return m_impl.value(key); } + + //! Returns the value associated with the key or if key is not found defaultValue + const Value value(const Key &key, const Value &defaultValue) const { return m_impl.value(key); } + + //! Return a collection of all values + CCollection values() const { return this->valuesCollection(); } + + //! Copy assignment. + CDictionary &operator =(const CDictionary &other) { m_impl = other.m_impl; return *this; } + + //! Move assignment + CDictionary &operator =(CDictionary && other) { m_impl = other.m_impl; return *this; } + + /*! + * \brief Access an element by its key. + * \note + * If dictionary does not contain any item with key, a default constructed + * value will be inserted + */ + Value &operator [](const Key &key) { return m_impl[key]; } + + /*! + * \brief Access an element by its key. + * \note + * If dictionary does not contain any item with key, a default constructed + * value will be inserted + */ + const Value operator [](const Key &key) const { return m_impl[key]; } + + //! Test for equality. + bool operator ==(const CDictionary &other) const { return m_impl == other.m_impl; } + + //! Test for inequality. + bool operator !=(const CDictionary &other) const { return !(*this == other); } + + protected: + //! \copydoc BlackMisc::CValueObject::convertToQString + //! \todo Fix brackets + virtual QString convertToQString(bool i18n = false) const override + { + QString str = "{"; + for (auto it = m_impl.cbegin(); it != m_impl.end(); ++it) + { + str += "{"; + str += CContainerHelper::stringify(it.key(), i18n) + "," + CContainerHelper::stringify(it.value(), i18n); + str += "}"; + } + if (str.isEmpty()) { str = "{"; } + return str += "}"; + } + + //! \copydoc BlackMisc::CValueObject::getMetaTypeId + virtual int getMetaTypeId() const override { return qMetaTypeId(); } + + //! \copydoc BlackMisc::CValueObject::isA + virtual bool isA(int metaTypeId) const override + { + if (metaTypeId == qMetaTypeId()) { return true; } + return CValueObject::isA(metaTypeId); + } + + //! \copydoc BlackMisc::CValueObject::compareImpl + virtual int compareImpl(const CValueObject &other) const override + { + const auto &o = static_cast(other); + if (m_impl.size() < o.m_impl.size()) { return -1; } + if (m_impl.size() > o.m_impl.size()) { return 1; } + return 0; + } + + //! \copydoc BlackMisc::CValueObject::marshallToDbus + virtual void marshallToDbus(QDBusArgument &argument) const override + { + argument << m_impl; + } + + //! \copydoc BlackMisc::CValueObject::unmarshallFromDbus + virtual void unmarshallFromDbus(const QDBusArgument &argument) override + { + argument >> m_impl; + } + + private: + + Impl m_impl; + }; + +} // namespace BlackMisc + +#endif // BLACKMISC_DICTIONARY_H +