From bf1837b4148194432db63f1a75e585a21d2a8cd3 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 22 Dec 2013 20:47:01 +0000 Subject: [PATCH] CValueMap generic representation of a value object as a map of QVariants refs #81 --- src/blackmisc/blackmiscfreefunctions.cpp | 25 ++++ src/blackmisc/blackmiscfreefunctions.h | 19 +++ src/blackmisc/valuemap.cpp | 119 ++++++++++++++++++ src/blackmisc/valuemap.h | 150 +++++++++++++++++++++++ src/blackmisc/valueobject.cpp | 88 +++++++++++++ src/blackmisc/valueobject.h | 65 ++++++++++ 6 files changed, 466 insertions(+) create mode 100644 src/blackmisc/valuemap.cpp create mode 100644 src/blackmisc/valuemap.h diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index ed95cc26e..3e1dca518 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -8,6 +8,7 @@ #include "pqallquantities.h" #include "mathallclasses.h" #include "geoallclasses.h" +#include "valuemap.h" /* * Metadata for PQs @@ -68,12 +69,24 @@ void BlackMisc::Geo::registerMetadata() */ void BlackMisc::registerMetadata() { + // !! make sure the first id is correctly returned by + // !! firstBlackMetaType + CValueMap::registerMetadata(); PhysicalQuantities::registerMetadata(); Aviation::registerMetadata(); Math::registerMetadata(); Geo::registerMetadata(); } +/* + * First of our ids + */ +int BlackMisc::firstBlackMetaType() +{ + // must be the first registered above + return qMetaTypeId(); +} + /* * Init resources */ @@ -279,3 +292,15 @@ QVariant BlackMisc::complexQtTypeFromDbusArgument(const QDBusArgument &argument, return QVariant(); // suppress compiler warning } +/* + * Dump all user types + */ +void BlackMisc::displayAllUserMetatypesTypes() +{ + for (int mt = QMetaType::User; mt < QMetaType::User + 1000; mt++) + { + if (!QMetaType::isRegistered(mt)) continue; + QMetaType metaType(mt); + qDebug() << "type:" << mt << "name:" << QMetaType::typeName(mt) << QMetaType::sizeOf(mt) << BlackMisc::heapSizeOf(metaType); + } +} diff --git a/src/blackmisc/blackmiscfreefunctions.h b/src/blackmisc/blackmiscfreefunctions.h index efe6a2b22..89881d17c 100644 --- a/src/blackmisc/blackmiscfreefunctions.h +++ b/src/blackmisc/blackmiscfreefunctions.h @@ -77,6 +77,20 @@ namespace BlackMisc */ void registerMetadata(); + /*! + * Deals with the issue that the BlackMisc metatype does not always start with + * the same id, e.g. with GUI enabled: + * type: 1024 name: QPaintBufferCacheEntry + * type: 1025 .... some classes I cannot foresee + * type: 1027 name: BlackMisc::CValueMap + * + * This is important when marshalling Variants via DBus among different + * binaries, as the offset has to be considered + * + * \return + */ + int firstBlackMetaType(); + /*! * \brief Init resources */ @@ -125,6 +139,11 @@ namespace BlackMisc // TODO: To be removed if a better solution is found QVariant complexQtTypeFromDbusArgument(const QDBusArgument &argument, int type); + /*! + * \brief displayAllUserTypes + */ + void displayAllUserMetatypesTypes(); + /*! * \brief Add several hash values * \param values diff --git a/src/blackmisc/valuemap.cpp b/src/blackmisc/valuemap.cpp new file mode 100644 index 000000000..865c7122d --- /dev/null +++ b/src/blackmisc/valuemap.cpp @@ -0,0 +1,119 @@ +#include "valuemap.h" +#include "blackmiscfreefunctions.h" +#include "avaltitude.h" + +namespace BlackMisc +{ + /* + * Constructor + */ + CValueMap::CValueMap(bool wildcard) : m_wildcard(wildcard) {} + + /* + * Constructor single value + */ + CValueMap::CValueMap(int index, const QVariant &value) + { + this->addValue(index, value); + } + + /* + * Convert to string + */ + QString CValueMap::convertToQString(bool i18n) const + { + if (this->isEmpty()) return "{}"; + QString s; + foreach(int index, this->m_values.keys()) + { + QVariant qv = this->m_values.value(index); + s.isEmpty() ? s.append("{") : s.append(", "); + s.append('{').append(QString::number(index)).append(": "); + s.append("(").append(QString::number(qv.userType())).append(") "); + QString qvs = BlackMisc::qVariantToString(qv, i18n); + s.append(qvs); + s.append('}'); + } + s = s.append("}"); + return s; + } + + /* + * Marshall to DBus + */ + void CValueMap::marshallToDbus(QDBusArgument &argument) const + { + // remark, tried both sending as QDbusVariant and QVariant + // does not make a difference + QList unifiedBlackTypeIds; + QList dbusVariants; + foreach(QVariant qv, m_values.values()) + { + unifiedBlackTypeIds << qv.userType() - BlackMisc::firstBlackMetaType(); + dbusVariants << QDBusVariant(qv); + } + argument << this->m_values.keys(); // indexes + argument << dbusVariants; + argument << unifiedBlackTypeIds; + } + + /* + * Unmarshall from DBus + */ + void CValueMap::unmarshallFromDbus(const QDBusArgument &argument) + { + QList indexes; + QList values; + QList unifiedBlackTypeIds; + argument >> indexes; + argument >> values; + argument >> unifiedBlackTypeIds; + QMap newMap; + for (int i = 0; i < indexes.size(); i++) + { + QVariant qv = values.at(i).variant(); + int index = indexes.at(i); + if (qv.canConvert()) + { + int userType = unifiedBlackTypeIds.at(i) + BlackMisc::firstBlackMetaType(); + QVariant concrete = BlackMisc::fixQVariantFromDbusArgument(qv, userType); + newMap.insert(index, concrete); + } + else + { + // value already OK + newMap.insert(index, qv); + } + } + // replace values in one step + this->m_values.clear(); + this->m_values.unite(newMap); + } + + /* + * Add value + */ + void CValueMap::addValue(int index, const QVariant &value) + { + this->m_values.insert(index, value); + } + + /* + * Register metadata + */ + void CValueMap::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } + + /* + * Hash + */ + uint CValueMap::getValueHash() const + { + return qHash(this); + } + + +} // namespace diff --git a/src/blackmisc/valuemap.h b/src/blackmisc/valuemap.h new file mode 100644 index 000000000..be08dbf29 --- /dev/null +++ b/src/blackmisc/valuemap.h @@ -0,0 +1,150 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * 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/. */ + +/*! + \file +*/ + +#include "valueobject.h" +#include +#include + +#ifndef BLACKMISC_VALUEMAP_H +#define BLACKMISC_VALUEMAP_H + +namespace BlackMisc +{ + /*! + * \brief Value map + */ + class CValueMap : public CValueObject + { + + public: + + /*! + * \brief Constructor + * \param wildcard when used in search, for setting values irrelevant + */ + CValueMap(bool wildcard = false); + + /*! + * \brief Single value constructor + * \param index + * \param value + */ + CValueMap(int index, const QVariant &value); + + /*! + * \brief QVariant, required for DBus QVariant lists + * \return + */ + virtual QVariant toQVariant() const + { + return QVariant::fromValue(*this); + } + + /*! + * \brief Destructor + */ + virtual ~CValueMap() {} + + /*! + * \brief Add a value + * \param index + * \param value + */ + void addValue(int index, const QVariant &value); + + /*! + * \brief Add a value as non QVariant + * \param index + * \param value + */ + template void addValue(int index, const T &value) + { + this->m_values.insert(index, QVariant::fromValue(value)); + } + + /*! + * \brief Is empty + * \return + */ + bool isEmpty() const { return this->m_values.isEmpty(); } + + /*! + * \brief Value + * \param index + * \return + */ + QVariant value(int index) const { return this->m_values.value(index); } + + /*! + * \brief Indexes + * \return + */ + QList indexes() const { return this->m_values.keys(); } + + /*! + * \brief values + * \return + */ + QList values() const { return this->m_values.values(); } + + /*! + * \brief Wildcard, only relevant when used in search + * \return + */ + bool isWildcard() const { return this->m_wildcard; } + + /*! + * \brief clear + */ + void clear() { this->m_values.clear(); } + + /*! + * \brief Map + * \return + */ + const QMap &map() const { return this->m_values; } + + /*! + * \brief Value hash + */ + virtual uint getValueHash() const; + + /*! + * \brief Metadata + */ + static void registerMetadata(); + + protected: + QMap m_values; /*!< values */ + bool m_wildcard; + + /*! + * \brief Meaningful string representation + * \param i18n + * \return + */ + virtual QString convertToQString(bool i18n = false) const; + + /*! + * \brief Stream to DBus << + * \param argument + */ + virtual void marshallToDbus(QDBusArgument &argument) const; + + /*! + * \brief Stream from DBus >> + * \param argument + */ + virtual void unmarshallFromDbus(const QDBusArgument &argument); + }; +} + +Q_DECLARE_METATYPE(BlackMisc::CValueMap) + +#endif // guard diff --git a/src/blackmisc/valueobject.cpp b/src/blackmisc/valueobject.cpp index c75ca08dc..ffd23f978 100644 --- a/src/blackmisc/valueobject.cpp +++ b/src/blackmisc/valueobject.cpp @@ -43,6 +43,35 @@ namespace BlackMisc return this->convertToQString(); } + /* + * Setter for property by index + */ + void CValueObject::setPropertyByIndex(const QVariant & /** variant **/, int /** index **/) + { + // not all classes have to implement this + qFatal("Property by index setter not implemented"); + } + + /* + * By index + */ + QVariant CValueObject::propertyByIndex(int /** index **/) const + { + // not all classes have to implement this + qFatal("Property by index not implemented"); + return QVariant("boom"); // avoid compiler warning + } + + /* + * By index as string + */ + QString CValueObject::propertyByIndexAsString(int /** index **/, bool /** i18n **/) const + { + // not all classes have to implement this + qFatal("Property by index as string not implemented"); + return QString("boom"); // avoid compiler warning + } + /* * Return backing streamable object (if any) */ @@ -70,6 +99,65 @@ namespace BlackMisc return -1; // avoid compiler warning } + /*! + * Variant map + */ + int CValueObject::apply(const BlackMisc::CValueMap &valueMap) + { + if (valueMap.isEmpty()) return 0; + int c = 0; + + QMap::const_iterator it; + const QMap &map = valueMap.map(); + for (it = map.begin(); it != map.end(); ++it) + { + this->setPropertyByIndex(it.value(), it.key()); + } + return c; + } + + /* + * Compare with value map + */ + bool operator==(const CValueMap &valueMap, const CValueObject &uc) + { + if (valueMap.isEmpty()) return valueMap.isWildcard(); + QMap::const_iterator it; + const QMap &map = valueMap.map(); + for (it = map.begin(); it != map.end(); ++it) + { + // QVariant cannot be compared directly + QVariant p = uc.propertyByIndex(it.key()); // from value object + QVariant v = it.value(); // from map + if (!BlackMisc::equalQVariants(p, v)) return false; + } + return true; + } + + /* + * Compare with value map + */ + bool operator!=(const CValueMap &valueMap, const CValueObject &uc) + { + return !(valueMap == uc); + } + + /* + * Compare with value map + */ + bool operator==(const CValueObject &uc, const CValueMap &valueMap) + { + return valueMap == uc; + } + + /* + * Compare with value map + */ + bool operator!=(const CValueObject &uc, const CValueMap &valueMap) + { + return !(valueMap == uc); + } + /* * from DBus */ diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 89dc417c0..9bce77870 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -109,6 +109,38 @@ namespace BlackMisc */ friend QDBusArgument &operator<<(QDBusArgument &argument, const CValueObject &uc); + /*! + * \brief Operator == with value map + * \param valueMap + * \param uc + * \return + */ + friend bool operator==(const CValueMap &valueMap, const CValueObject &uc); + + /*! + * \brief Operator != with value map + * \param valueMap + * \param uc + * \return + */ + friend bool operator!=(const CValueMap &valueMap, const CValueObject &uc); + + /*! + * \brief Operator == with value map + * \param uc + * \param valueMap + * \return + */ + friend bool operator==(const CValueObject &uc, const CValueMap &valueMap); + + /*! + * \brief Operator != with value map + * \param uc + * \param valueMap + * \return + */ + friend bool operator!=(const CValueObject &uc, const CValueMap &valueMap); + public: /*! * \brief Virtual destructor @@ -134,6 +166,13 @@ namespace BlackMisc */ std::string toStdString(bool i18n = false) const; + /*! + * \brief Update by variant map + * \param valueMap + * \return + */ + int apply(const BlackMisc::CValueMap &valueMap); + /*! * \brief Value hash, allows comparisons between QVariants * \return @@ -155,6 +194,32 @@ namespace BlackMisc */ virtual QVariant toQVariant() const = 0; + /*! + * \brief Set property by index + * \remarks Intentionally not abstract, avoiding all classes need to implement this method + * \param index + * \return + */ + virtual void setPropertyByIndex(const QVariant &variant, int index); + + /*! + * \brief Property by index + * \remarks Intentionally not abstract, avoiding all classes need to implement this method + * \param index + * \return + */ + virtual QVariant propertyByIndex(int index) const; + + /*! + * \brief Property by index as String + * \remarks Intentionally not abstract, avoiding all classes need to implement this method + + * \param index + * \param i18n + * \return + */ + virtual QString propertyByIndexAsString(int index, bool i18n = false) const; + /*! * \brief The stored object as CValueObject * \param qv