diff --git a/src/blackmisc/avaltitude.cpp b/src/blackmisc/avaltitude.cpp index f4944b4b3..789d2447b 100644 --- a/src/blackmisc/avaltitude.cpp +++ b/src/blackmisc/avaltitude.cpp @@ -58,6 +58,28 @@ namespace BlackMisc return !((*this) == other); } + /* + * Compare + */ + int CAltitude::compare(const QVariant &qv) const + { + Q_ASSERT(qv.canConvert() || qv.canConvert()); + Q_ASSERT(qv.isValid() && !qv.isNull()); + if (qv.canConvert()) + { + CAltitude other = qv.value(); + if (this->isMeanSeaLevel() && other.isAboveGroundLevel()) return 1; + if (this->isAboveGroundLevel() && other.isMeanSeaLevel()) return -1; + return this->compare(other); + } + else if (qv.canConvert()) + { + return this->compare(qv.value()); + } + qFatal("Invalid comparison"); + return 0; // just for compiler + } + /* * Register metadata */ diff --git a/src/blackmisc/avaltitude.h b/src/blackmisc/avaltitude.h index fbca318cc..8a957c92a 100644 --- a/src/blackmisc/avaltitude.h +++ b/src/blackmisc/avaltitude.h @@ -122,6 +122,17 @@ namespace BlackMisc return m_datum; } + /*! + * \copydoc BlackObject::compare + */ + virtual int compare(const QVariant &qv) const; + + /*! + * \todo this is a hack, to avoid hiding inherited names in CPhysicalQuantity + * (see Effective C++ item 33) CPhysicalQuantity::compare is the real culprit + */ + int compare(const CLength &other) const { return static_cast(this)->compare(other); } + /*! * \brief Register metadata */ diff --git a/src/blackmisc/aviocomsystem.h b/src/blackmisc/aviocomsystem.h index 31cfcbcdb..1b91cd0cd 100644 --- a/src/blackmisc/aviocomsystem.h +++ b/src/blackmisc/aviocomsystem.h @@ -87,6 +87,15 @@ namespace BlackMisc return QVariant::fromValue(*this); } + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const + { + return CModulator::getValueHash(); + } + /*! * \brief Constructor * \param name diff --git a/src/blackmisc/aviomodulator.cpp b/src/blackmisc/aviomodulator.cpp index b69265fa4..a3851e4fa 100644 --- a/src/blackmisc/aviomodulator.cpp +++ b/src/blackmisc/aviomodulator.cpp @@ -7,6 +7,7 @@ #include "blackmisc/aviocomsystem.h" #include "blackmisc/avionavsystem.h" #include "blackmisc/avioadfsystem.h" +#include "blackmisc/blackmiscfreefunctions.h" using BlackMisc::PhysicalQuantities::CFrequency; using BlackMisc::PhysicalQuantities::CFrequencyUnit; @@ -76,6 +77,18 @@ namespace BlackMisc argument >> this->m_digits; } + /* + * Value hash + */ + template uint CModulator::getValueHash() const + { + QList hashs; + hashs << this->m_frequencyActive.getValueHash(); + hashs << this->m_frequencyStandby.getValueHash(); + hashs << qHash(this->m_digits); + return BlackMisc::calculateHash(hashs, "CModulator"); + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CModulator; diff --git a/src/blackmisc/aviomodulator.h b/src/blackmisc/aviomodulator.h index cd36fb0db..7bad48538 100644 --- a/src/blackmisc/aviomodulator.h +++ b/src/blackmisc/aviomodulator.h @@ -214,6 +214,12 @@ namespace BlackMisc */ virtual void unmarshallFromDbus(const QDBusArgument &argument); + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const; + public: /*! * \brief Virtual destructor diff --git a/src/blackmisc/aviotransponder.cpp b/src/blackmisc/aviotransponder.cpp index 446a31fd8..9bdffde55 100644 --- a/src/blackmisc/aviotransponder.cpp +++ b/src/blackmisc/aviotransponder.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "blackmisc/aviotransponder.h" +#include "blackmisc/blackmiscfreefunctions.h" namespace BlackMisc { @@ -136,6 +137,18 @@ namespace BlackMisc this->m_transponderMode = static_cast(tm); } + /* + * Value hash + */ + uint CTransponder::getValueHash() const + { + QList hashs; + hashs << qHash(this->m_name); + hashs << qHash(this->m_transponderCode); + hashs << qHash(this->m_transponderMode); + return BlackMisc::calculateHash(hashs, "CTransponder"); + } + /* * Register metadata of unit and quantity */ diff --git a/src/blackmisc/aviotransponder.h b/src/blackmisc/aviotransponder.h index b87c51cd2..1500562ae 100644 --- a/src/blackmisc/aviotransponder.h +++ b/src/blackmisc/aviotransponder.h @@ -331,6 +331,12 @@ namespace BlackMisc return CTransponder::tryGetTransponder(o_transponder, "Transponder", transponderCode, mode); } + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const; + /*! * \brief Register metadata of unit and quantity */ diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index 97fce7a45..ed95cc26e 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -81,6 +81,98 @@ void BlackMisc::initResources() { initBlackMiscResources(); } + +/* + * Stupid extension bo be able to compare 2 QVariants + */ +bool BlackMisc::equalQVariants(const QVariant &v1, const QVariant &v2) +{ + // prephase, shortcuts + if (v1 == v2) return true; // compares on primitives or on address + if (!v1.isValid() || !v2.isValid()) return false; + if (v1.type() != v2.type()) return false; + if (v1.userType() != v2.userType()) return false; + + // I have same types now + const CValueObject *cs1 = CValueObject::fromQVariant(v1); + const CValueObject *cs2 = CValueObject::fromQVariant(v2); + if (cs1 && cs2) + { + uint h1 = cs1->getValueHash(); + uint h2 = cs2->getValueHash(); + return h1 == h2; + } + return false; +} + +/* + * Compare values + */ +int BlackMisc:: compareQVariants(const QVariant &v1, const QVariant &v2) +{ + if (v1 == v2) return 0; // compares on primitives or on address + + if (!v1.isValid() || !v2.isValid()) qFatal("Invalid variants"); + if (v1.type() != v2.type()) qFatal("Mismatching types"); + if (v1.userType() != v2.userType()) qFatal("Mismatching user types"); + + switch (v1.type()) + { + case QMetaType::QString: + { + QString s1 = v1.value(); + QString s2 = v2.value(); + return s1.compare(s2); + } + case QMetaType::QDateTime: + { + QDateTime dt1 = v1.value(); + QDateTime dt2 = v2.value(); + if (dt1 == dt2) return 0; + return dt1 < dt2 ? -1 : 1; + } + case QMetaType::QDate: + { + QDate d1 = v1.value(); + QDate d2 = v2.value(); + if (d1 == d2) return 0; + return d1 < d2 ? -1 : 1; + } + case QMetaType::QTime: + { + QTime t1 = v1.value(); + QTime t2 = v2.value(); + if (t1 == t2) return 0; + return t1 < t2 ? -1 : 1; + } + default: + break; + } + + // BlackObject + if (v1.type() == QVariant::UserType) + { + const CValueObject *cs1 = CValueObject::fromQVariant(v1); + const CValueObject *cs2 = CValueObject::fromQVariant(v2); + if (cs1 && cs2) + { + return cs1->compare(v2); // Note, that I have to compare against QVariant + } + } + + // all kind of numeric values + if (v1.canConvert()) + { + double d1 = v1.value(); + double d2 = v2.value(); + if (d1 == d2) return 0; + return d1 < d2 ? -1 : 1; + } + + qFatal("Unknown type for compare"); + return -1; +} + /* * To string */ @@ -98,6 +190,27 @@ QString BlackMisc::qVariantToString(const QVariant &qv, bool i18n) } } +/* + * Add hash values + */ +uint BlackMisc::calculateHash(const QList &values, const char *className) +{ + // http://stackoverflow.com/questions/113511/hash-code-implementation/113600#113600 + if (values.isEmpty()) return 0; + uint hash = values.first(); + for (int i = 1; i < values.size(); i++) + { + hash = 37 * hash + values.at(i); + } + + // same values, but different class? + if (className) + { + hash = 37 * hash + qHash(QString(className)); + } + return hash; +} + /* * Fix QVariant if it comes from DBus and contains QDBusArgument */ diff --git a/src/blackmisc/blackmiscfreefunctions.h b/src/blackmisc/blackmiscfreefunctions.h index 3e39680fc..efe6a2b22 100644 --- a/src/blackmisc/blackmiscfreefunctions.h +++ b/src/blackmisc/blackmiscfreefunctions.h @@ -7,6 +7,7 @@ #define BLACKMISC_FREEFUNCTIONS_H #include // for Q_INIT_RESOURCE +#include #include #include @@ -81,6 +82,21 @@ namespace BlackMisc */ void initResources(); + /*! + * \brief Compare 2 QVariants + * \param v1 + * \param v2 + */ + bool equalQVariants(const QVariant &v1, const QVariant &v2); + + /*! + * \brief Compare QVariants + * \param v1 + * \param v2 + * \return + */ + int compareQVariants(const QVariant &v1, const QVariant &v2); + /*! * \brief QVariant to string, allows to stringify CValueObject * \param qv @@ -109,6 +125,14 @@ namespace BlackMisc // TODO: To be removed if a better solution is found QVariant complexQtTypeFromDbusArgument(const QDBusArgument &argument, int type); + /*! + * \brief Add several hash values + * \param values + * \param classTypeId + * \return + */ + uint calculateHash(const QList &values, const char *className); + } // BlackMisc #endif // guard diff --git a/src/blackmisc/coordinategeodetic.cpp b/src/blackmisc/coordinategeodetic.cpp index 71471e6ae..f21eaf4f2 100644 --- a/src/blackmisc/coordinategeodetic.cpp +++ b/src/blackmisc/coordinategeodetic.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "blackmisc/coordinategeodetic.h" +#include "blackmisc/blackmiscfreefunctions.h" namespace BlackMisc { @@ -67,6 +68,18 @@ namespace BlackMisc qDBusRegisterMetaType(); } + /* + * Hash + */ + uint CCoordinateGeodetic::getValueHash() const + { + QList hashs; + hashs << this->m_latitude.getValueHash(); + hashs << this->m_longitude.getValueHash(); + hashs << this->m_height.getValueHash(); + return BlackMisc::calculateHash(hashs, "CCoordinateGeodetic"); + } + /* * Great circle distance diff --git a/src/blackmisc/coordinategeodetic.h b/src/blackmisc/coordinategeodetic.h index dfabb3ad4..9bc834d66 100644 --- a/src/blackmisc/coordinategeodetic.h +++ b/src/blackmisc/coordinategeodetic.h @@ -217,6 +217,11 @@ namespace BlackMisc */ bool operator !=(const CCoordinateGeodetic &other) const; + /*! + * \brief value Hash + */ + virtual uint getValueHash() const; + /*! * Register metadata */ diff --git a/src/blackmisc/geoearthangle.cpp b/src/blackmisc/geoearthangle.cpp index b5a06481a..456549858 100644 --- a/src/blackmisc/geoearthangle.cpp +++ b/src/blackmisc/geoearthangle.cpp @@ -71,6 +71,18 @@ namespace BlackMisc return LATorLON(a); } + /* + * Compare + */ + template int CEarthAngle::compare(const QVariant &qv) const + { + Q_ASSERT(qv.canConvert() || qv.canConvert()); + Q_ASSERT(qv.isValid() && !qv.isNull()); + if (qv.canConvert()) + return this->toAngle().compare(qv.value().toAngle()); + else + return this->toAngle().compare(qv.value()); + } // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html diff --git a/src/blackmisc/geoearthangle.h b/src/blackmisc/geoearthangle.h index 97dc8385a..a2e5284b0 100644 --- a/src/blackmisc/geoearthangle.h +++ b/src/blackmisc/geoearthangle.h @@ -184,6 +184,11 @@ namespace BlackMisc return BlackMisc::PhysicalQuantities::CAngle(static_cast(*this)); } + /*! + * Compare + */ + int compare(const QVariant &qv) const; + /*! * Register metadata */ diff --git a/src/blackmisc/mathmatrixbase.cpp b/src/blackmisc/mathmatrixbase.cpp index 82bbdaac2..1ecfb4579 100644 --- a/src/blackmisc/mathmatrixbase.cpp +++ b/src/blackmisc/mathmatrixbase.cpp @@ -138,6 +138,23 @@ namespace BlackMisc return s; } + /* + * Hash + */ + template uint CMatrixBase::getValueHash() const + { + const QList l = this->toList(); + QList hashs; + + // there is an issue with the signature of QList, so I use + // individual values + foreach(double v, l) + { + hashs << qHash(static_cast(v)); + } + return BlackMisc::calculateHash(hashs, "CMatrixBase"); + } + /* * To DBus */ diff --git a/src/blackmisc/mathmatrixbase.h b/src/blackmisc/mathmatrixbase.h index d7f3b67b8..c373def3b 100644 --- a/src/blackmisc/mathmatrixbase.h +++ b/src/blackmisc/mathmatrixbase.h @@ -89,6 +89,12 @@ namespace BlackMisc */ QList toList() const; + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const; + /*! * \brief List of values * \return diff --git a/src/blackmisc/mathvector3dbase.cpp b/src/blackmisc/mathvector3dbase.cpp index 08d4d7da4..6eb34e284 100644 --- a/src/blackmisc/mathvector3dbase.cpp +++ b/src/blackmisc/mathvector3dbase.cpp @@ -144,6 +144,18 @@ namespace BlackMisc argument >> this->m_k; } + /*! + * \brief Stream to DBus + * \param argument + */ + template uint CVector3DBase::getValueHash() const + { + QList hashs; + hashs << qHash(static_cast(this->m_i)); + hashs << qHash(static_cast(this->m_j)); + hashs << qHash(static_cast(this->m_k)); + return BlackMisc::calculateHash(hashs, "CVector3DBase"); + } /* * Register metadata diff --git a/src/blackmisc/mathvector3dbase.h b/src/blackmisc/mathvector3dbase.h index 61299bbb8..d95afa2bb 100644 --- a/src/blackmisc/mathvector3dbase.h +++ b/src/blackmisc/mathvector3dbase.h @@ -323,6 +323,12 @@ namespace BlackMisc */ CMatrix3x1 toMatrix3x1() const; + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const; + /*! * \brief length / magnitude * \return diff --git a/src/blackmisc/pqbase.h b/src/blackmisc/pqbase.h index 011ab449f..797db009e 100644 --- a/src/blackmisc/pqbase.h +++ b/src/blackmisc/pqbase.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace BlackMisc { @@ -301,6 +302,14 @@ namespace BlackMisc return i18n ? QCoreApplication::translate("CMeasurementUnit", this->m_symbol.toStdString().c_str()) : this->m_symbol; } + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const + { + return qHash(this->getName()); + } /*! * \brief Rounded value diff --git a/src/blackmisc/pqphysicalquantity.cpp b/src/blackmisc/pqphysicalquantity.cpp index ef82f5cec..b8f52a4c0 100644 --- a/src/blackmisc/pqphysicalquantity.cpp +++ b/src/blackmisc/pqphysicalquantity.cpp @@ -254,6 +254,36 @@ namespace BlackMisc return this->valueRoundedWithUnit(this->getUnit(), -1, i18n); } + /* + * Hash + */ + template uint CPhysicalQuantity::getValueHash() const + { + QList hashs; + hashs << this->m_unit.getValueHash(); + hashs << qHash(static_cast(this->m_value)); + return BlackMisc::calculateHash(hashs, "PQ"); + } + + /* + * Compare + */ + template int CPhysicalQuantity::compare(const QVariant &qv) const + { + Q_ASSERT(qv.canConvert()); + Q_ASSERT(!qv.isNull() && qv.isValid()); + return this->compare(qv.value()); + } + + /* + * Compare + */ + template int CPhysicalQuantity::compare(const PQ &other) const + { + if (other == (*this)) return 0; + return ((*this) < other) ? -1 : 1; + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CPhysicalQuantity; diff --git a/src/blackmisc/pqphysicalquantity.h b/src/blackmisc/pqphysicalquantity.h index 11413c361..260cea049 100644 --- a/src/blackmisc/pqphysicalquantity.h +++ b/src/blackmisc/pqphysicalquantity.h @@ -347,11 +347,26 @@ namespace BlackMisc */ virtual void unmarshallFromDbus(const QDBusArgument &argument); + /*! + * \brief Value hash + * \return + */ + virtual uint getValueHash() const; + /*! * \brief Register metadata of unit and quantity */ static void registerMetadata(); + /*! + * \copydoc BlackObject::compare + */ + virtual int compare(const QVariant &qv) const; + + /*! + * \copydoc BlackObject::compare + */ + virtual int compare(const PQ &other) const; }; } // namespace diff --git a/src/blackmisc/valueobject.cpp b/src/blackmisc/valueobject.cpp index b14b7e44d..c75ca08dc 100644 --- a/src/blackmisc/valueobject.cpp +++ b/src/blackmisc/valueobject.cpp @@ -60,6 +60,16 @@ namespace BlackMisc return vo; } + /* + * Compare + */ + int CValueObject::compare(const QVariant & /** qv **/) const + { + // not all classes have to implement this + qFatal("Property by index as string not implemented"); + return -1; // avoid compiler warning + } + /* * from DBus */ diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 553b6c66b..89dc417c0 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -12,6 +12,8 @@ namespace BlackMisc { + // forward declaration + class CValueMap; /*! * \brief Base class for value objects. @@ -132,6 +134,21 @@ namespace BlackMisc */ std::string toStdString(bool i18n = false) const; + /*! + * \brief Value hash, allows comparisons between QVariants + * \return + */ + virtual uint getValueHash() const = 0; + + /*! + * Compares with QVariant with this object + * and returns an integer less than, equal to, or greater than zero + * if this is less than, equal to, or greater than QVariant. + * \remarks allows sorting among QVariants, not all classes implement this + * \return + */ + virtual int compare(const QVariant &qv) const; + /*! * \brief Virtual method to return QVariant, used with DBUS QVariant lists * \return @@ -216,6 +233,48 @@ namespace BlackMisc return argument << static_cast(uc); } + /*! + * Allow comparison with QVariant, e.g. + * QVariant == CFrequency ? + */ + template typename std::enable_if::value, bool>::type + operator==(const QVariant &variant, const T &uc) + { + if (!variant.canConvert()) return false; + T vuc = variant.value(); + return vuc == uc; + } + + /*! + * Allow comparison with QVariant, e.g. + * QVariant != CFrequency ? + */ + template typename std::enable_if::value, bool>::type + operator!=(const QVariant &variant, const T &uc) + { + return !(variant == uc); + } + + /*! + * Allow comparison with QVariant, e.g. + * QVariant == CFrequency ? + */ + template typename std::enable_if::value, bool>::type + operator==(const T &uc, const QVariant &variant) + { + return variant == uc; + } + + /*! + * Allow comparison with QVariant, e.g. + * QVariant != CFrequency ? + */ + template typename std::enable_if::value, bool>::type + operator!=(const T &uc, const QVariant &variant) + { + return variant != uc; + } + } // namespace #endif // guard