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
// it has to be made sure, that the cast works
const QDBusArgument arg = variant.value<QDBusArgument>();
QVariant fixedVariant;
if (localUserType < static_cast<int>(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<CValueObject *>(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;
}
}

View File

@@ -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<int>(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<QString>())
{
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<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
@@ -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<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
{
@@ -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<QMetaType::Type>(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