refs #247 CVariant uses our new metadata system to provide access to CValueObject operations in a safe manner.

This commit is contained in:
Mathew Sutcliffe
2014-11-13 22:29:15 +00:00
parent f52cdd596e
commit f68fc32f3d
3 changed files with 121 additions and 42 deletions

View File

@@ -337,27 +337,25 @@ QVariant BlackMisc::fixQVariantFromDbusArgument(const QVariant &variant, int loc
// complex, user type // complex, user type
// it has to be made sure, that the cast works // it has to be made sure, that the cast works
const QDBusArgument arg = variant.value<QDBusArgument>(); const QDBusArgument arg = variant.value<QDBusArgument>();
QVariant fixedVariant;
if (localUserType < static_cast<int>(QVariant::UserType)) if (localUserType < static_cast<int>(QVariant::UserType))
{ {
// complex Qt type, e.g. QDateTime // complex Qt type, e.g. QDateTime
fixedVariant = BlackMisc::complexQtTypeFromDbusArgument(arg, localUserType); return BlackMisc::complexQtTypeFromDbusArgument(arg, localUserType);
} }
else else
{ {
// http://qt-project.org/doc/qt-5.0/qtcore/qmetatype.html#create QVariant valueVariant(localUserType, nullptr);
void *obByMetaId = QMetaType::create(localUserType); auto *meta = Private::getValueObjectMetaInfo(valueVariant);
if (meta)
// own types, send as QDBusArgument {
CValueObject *streamable = static_cast<CValueObject *>(obByMetaId); meta->unmarshall(arg, valueVariant.data());
arg >> (*streamable); return valueVariant;
fixedVariant = streamable->toQVariant(); }
QMetaType::destroy(localUserType, obByMetaId);
} }
return fixedVariant;
} }
else else
{ {
qWarning() << "fixQVariantFromDbusArgument called with unsupported type";
return variant; return variant;
} }
} }

View File

@@ -34,42 +34,116 @@ namespace BlackMisc
QString CVariant::toString(bool i18n) const 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 return meta->toQString(data(), i18n);
Q_ASSERT(s); }
if (s) 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 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 CVariant::toJson() const
{ {
QJsonObject json; QJsonObject json;
json.insert("type", static_cast<int>(this->type())); // type json.insert("type", this->typeName());
json.insert("usertype", this->userType()); // user type
json.insert("typename", this->typeName()); // as tring, mainly for debugging, readablity switch (m_v.type())
json.insert("value", this->toString(false)); {
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<QString>())
{
json.insert("value", m_v.toString());
}
else
{
qWarning() << "Unsupported CVariant type for toJson";
}
}
}
return json; return json;
} }
void CVariant::fromJson(const QJsonObject &json) void CVariant::fromJson(const QJsonObject &json)
{ {
int type = json.value("type").toInt(-1); QString typeName = json.value("type").toString();
int userType = json.value("usertype").toInt(-1); int typeId = QMetaType::type(qPrintable(typeName));
QString typeName = json.value("typename").toString();
QString value = json.value("value").toString();
// KB: Not yet implemented, but would be possible IMHO switch (typeId)
Q_ASSERT(false); {
qDebug() << type << userType << typeName << value; case QVariant::Int: m_v.setValue(json.value("value").toInt());
case QVariant::UInt: m_v.setValue<uint>(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<qlonglong>(json.value("value").toInt()); // QJsonValue has no toLongLong() method???
case QVariant::ULongLong: m_v.setValue<qulonglong>(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<QString>(), 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 uint CVariant::getValueHash() const
@@ -87,10 +161,10 @@ namespace BlackMisc
case QVariant::ByteArray: return qHash(m_v.toByteArray()); case QVariant::ByteArray: return qHash(m_v.toByteArray());
default: default:
{ {
const CValueObject *cv = CValueObject::fromQVariant(m_v); auto *meta = getValueObjectMetaInfo();
if (cv) if (meta)
{ {
return cv->getValueHash(); return meta->getValueHash(data());
} }
else if (m_v.canConvert<QString>()) else if (m_v.canConvert<QString>())
{ {

View File

@@ -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 class CVariant
{ {
@@ -133,8 +134,7 @@ namespace BlackMisc
bool isValid() const { return m_v.isValid(); } 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. //! 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 QMetaType::Type type() const { return static_cast<QMetaType::Type>(m_v.type()); }
QVariant::Type type() const { return m_v.type(); }
//! Return the typename of the value in this variant. //! Return the typename of the value in this variant.
const char *typeName() const { return m_v.typeName(); } const char *typeName() const { return m_v.typeName(); }
@@ -149,28 +149,35 @@ namespace BlackMisc
void fromJson(const QJsonObject &json); void fromJson(const QJsonObject &json);
//! Equal operator. //! 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. //! 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. //! 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. //! 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. //! 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. //! 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. //! Register metadata.
static void registerMetadata(); static void registerMetadata();
private: private:
QVariant m_v; 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. //! Marshall a variant to DBus.
@@ -234,4 +241,4 @@ namespace BlackMisc
Q_DECLARE_METATYPE(BlackMisc::CVariant) Q_DECLARE_METATYPE(BlackMisc::CVariant)
#endif // guard #endif