diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index 84b106c8f..9843f988a 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -337,27 +337,25 @@ QVariant BlackMisc::fixQVariantFromDbusArgument(const QVariant &variant, int loc // complex, user type // it has to be made sure, that the cast works const QDBusArgument arg = variant.value(); - QVariant fixedVariant; if (localUserType < static_cast(QVariant::UserType)) { // complex Qt type, e.g. QDateTime - fixedVariant = BlackMisc::complexQtTypeFromDbusArgument(arg, localUserType); + return BlackMisc::complexQtTypeFromDbusArgument(arg, localUserType); } else { - // http://qt-project.org/doc/qt-5.0/qtcore/qmetatype.html#create - void *obByMetaId = QMetaType::create(localUserType); - - // own types, send as QDBusArgument - CValueObject *streamable = static_cast(obByMetaId); - arg >> (*streamable); - fixedVariant = streamable->toQVariant(); - QMetaType::destroy(localUserType, obByMetaId); + QVariant valueVariant(localUserType, nullptr); + auto *meta = Private::getValueObjectMetaInfo(valueVariant); + if (meta) + { + meta->unmarshall(arg, valueVariant.data()); + return valueVariant; + } } - return fixedVariant; } else { + qWarning() << "fixQVariantFromDbusArgument called with unsupported type"; return variant; } } diff --git a/src/blackmisc/variant.cpp b/src/blackmisc/variant.cpp index b92b74cc4..f09aeb4a2 100644 --- a/src/blackmisc/variant.cpp +++ b/src/blackmisc/variant.cpp @@ -34,42 +34,116 @@ namespace BlackMisc QString CVariant::toString(bool i18n) const { - if (type() == QVariant::UserType) + auto *meta = getValueObjectMetaInfo(); + if (meta) { - const CValueObject *s = CValueObject::fromQVariant(m_v); // FIXME this will return garbage if value is not a CValueObject - Q_ASSERT(s); - if (s) + return meta->toQString(data(), i18n); + } + return m_v.toString(); + } + + int BlackMisc::compare(const CVariant &a, const CVariant &b) + { + if (a.userType() < b.userType()) { return -1; } + if (a.userType() > b.userType()) { return 1; } + auto *metaA = a.getValueObjectMetaInfo(); + auto *metaB = b.getValueObjectMetaInfo(); + if (metaA && metaB) + { + const void *casted = nullptr; + if ((casted = metaA->upCastTo(a.data(), metaB->getMetaTypeId()))) { - return s->toQString(i18n); + return metaB->compare(casted, b.data()); + } + else if ((casted = metaB->upCastTo(b.data(), metaA->getMetaTypeId()))) + { + return metaA->compare(a.data(), casted); } else { - return "No CValueObject, no string conversion"; + qWarning() << "Comparing two CVariants containing unrelated value objects"; + return 0; } } - return m_v.toString(); + if (a.m_v < b.m_v) { return -1; } + if (a.m_v > b.m_v) { return 1; } + return 0; } QJsonObject CVariant::toJson() const { QJsonObject json; - json.insert("type", static_cast(this->type())); // type - json.insert("usertype", this->userType()); // user type - json.insert("typename", this->typeName()); // as tring, mainly for debugging, readablity - json.insert("value", this->toString(false)); + json.insert("type", this->typeName()); + + switch (m_v.type()) + { + case QVariant::Int: json.insert("value", m_v.toInt()); + case QVariant::UInt: json.insert("value", m_v.toInt()); + case QVariant::Bool: json.insert("value", m_v.toBool()); + case QVariant::Double: json.insert("value", m_v.toDouble()); + case QVariant::LongLong: json.insert("value", m_v.toLongLong()); + case QVariant::ULongLong: json.insert("value", m_v.toLongLong()); + case QVariant::String: json.insert("value", m_v.toString()); + case QVariant::Char: json.insert("value", m_v.toString()); + case QVariant::ByteArray: json.insert("value", m_v.toString()); + default: + { + auto *meta = getValueObjectMetaInfo(); + if (meta) + { + json.insert("value", meta->toJson(data())); + } + else if (m_v.canConvert()) + { + json.insert("value", m_v.toString()); + } + else + { + qWarning() << "Unsupported CVariant type for toJson"; + } + } + } return json; } void CVariant::fromJson(const QJsonObject &json) { - int type = json.value("type").toInt(-1); - int userType = json.value("usertype").toInt(-1); - QString typeName = json.value("typename").toString(); - QString value = json.value("value").toString(); + QString typeName = json.value("type").toString(); + int typeId = QMetaType::type(qPrintable(typeName)); - // KB: Not yet implemented, but would be possible IMHO - Q_ASSERT(false); - qDebug() << type << userType << typeName << value; + switch (typeId) + { + case QVariant::Int: m_v.setValue(json.value("value").toInt()); + case QVariant::UInt: m_v.setValue(json.value("value").toInt()); + case QVariant::Bool: m_v.setValue(json.value("value").toBool()); + case QVariant::Double: m_v.setValue(json.value("value").toDouble()); + case QVariant::LongLong: m_v.setValue(json.value("value").toInt()); // QJsonValue has no toLongLong() method??? + case QVariant::ULongLong: m_v.setValue(json.value("value").toInt()); + case QVariant::String: m_v.setValue(json.value("value").toString()); + case QVariant::Char: m_v.setValue(json.value("value").toString().size() > 0 ? json.value("value").toString().at(0) : '\0'); + case QVariant::ByteArray: m_v.setValue(json.value("value").toString().toLatin1()); + default: + { + auto *meta = Private::getValueObjectMetaInfo(typeId); + if (meta) + { + m_v = QVariant(typeId, nullptr); + meta->convertFromJson(json.value("value").toObject(), data()); + } + else if (QMetaType::hasRegisteredConverterFunction(qMetaTypeId(), typeId)) + { + m_v.setValue(json.value("value").toString()); + if (! m_v.convert(typeId)) + { + qWarning() << "Failed to convert from JSON string"; + } + } + else + { + qWarning() << "Unsupported CVariant type for fromJson"; + } + } + } } uint CVariant::getValueHash() const @@ -87,10 +161,10 @@ namespace BlackMisc case QVariant::ByteArray: return qHash(m_v.toByteArray()); default: { - const CValueObject *cv = CValueObject::fromQVariant(m_v); - if (cv) + auto *meta = getValueObjectMetaInfo(); + if (meta) { - return cv->getValueHash(); + return meta->getValueHash(data()); } else if (m_v.canConvert()) { diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index 4f452d514..1b9789999 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -25,7 +25,8 @@ namespace BlackMisc { /*! - * Wrapper class for QVariant, for more natural and transparent DBus integration. + * Wrapper around QVariant which provides transparent access to CValueObject methods + * of the contained object if it is registered with BlackMisc::registerMetaValueType. */ class CVariant { @@ -133,8 +134,7 @@ namespace BlackMisc bool isValid() const { return m_v.isValid(); } //! Return the metatype ID of the value in this variant, or QMetaType::User if it is a user type. - // KB we should think about returning QMetaType::Type directly, as QVariant::Type is deprecated - QVariant::Type type() const { return m_v.type(); } + QMetaType::Type type() const { return static_cast(m_v.type()); } //! Return the typename of the value in this variant. const char *typeName() const { return m_v.typeName(); } @@ -149,28 +149,35 @@ namespace BlackMisc void fromJson(const QJsonObject &json); //! Equal operator. - bool operator ==(const CVariant &other) const { return m_v == other.m_v; } + friend bool operator ==(const CVariant &a, const CVariant &b) { return compare(a, b) == 0; } //! Not equal operator. - bool operator !=(const CVariant &other) const { return m_v != other.m_v; } + friend bool operator !=(const CVariant &a, const CVariant &b) { return !(a == b); } //! Less than operator. - bool operator <(const CVariant &other) const { return m_v < other.m_v; } + friend bool operator <(const CVariant &a, const CVariant &b) { return compare(a, b) < 0; } //! Less than or equal operator. - bool operator <=(const CVariant &other) const { return m_v <= other.m_v; } + friend bool operator <=(const CVariant &a, const CVariant &b) { return !(b < a); } //! Greater than operator. - bool operator >(const CVariant &other) const { return m_v > other.m_v; } + friend bool operator >(const CVariant &a, const CVariant &b) { return b < a; } //! Greater than or equal operator. - bool operator >=(const CVariant &other) const { return m_v >= other.m_v; } + friend bool operator >=(const CVariant &a, const CVariant &b) { return !(a < b); } + + //! Arbitrary comparison + friend int compare(const CVariant &a, const CVariant &b); //! Register metadata. static void registerMetadata(); private: QVariant m_v; + + Private::IValueObjectMetaInfo *getValueObjectMetaInfo() const { return Private::getValueObjectMetaInfo(m_v); } + void *data() { return m_v.data(); } + const void *data() const { return m_v.data(); } }; //! Marshall a variant to DBus. @@ -234,4 +241,4 @@ namespace BlackMisc Q_DECLARE_METATYPE(BlackMisc::CVariant) -#endif // guard +#endif