diff options
author | Alex <qt-info@nokia.com> | 2011-06-15 19:14:16 +1000 |
---|---|---|
committer | Alex <qt-info@nokia.com> | 2011-06-17 17:31:52 +1000 |
commit | 0e3be465c935700019f6232f88381613d51776d7 (patch) | |
tree | e2a44eda69ebd38183ea251532e0da9fcb6020a6 /src/serviceframework/ipc/qservicemetaobject_dbus.cpp | |
parent | 79ec22e9cd8ef8f679c541c4fd76d616e737b8bf (diff) |
Add first version of QtServiceFramework library
Diffstat (limited to 'src/serviceframework/ipc/qservicemetaobject_dbus.cpp')
-rw-r--r-- | src/serviceframework/ipc/qservicemetaobject_dbus.cpp | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/src/serviceframework/ipc/qservicemetaobject_dbus.cpp b/src/serviceframework/ipc/qservicemetaobject_dbus.cpp new file mode 100644 index 00000000..6dbb79c1 --- /dev/null +++ b/src/serviceframework/ipc/qservicemetaobject_dbus.cpp @@ -0,0 +1,580 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicemetaobject_dbus_p.h" +#include "qmetaobjectbuilder_p.h" +#include "qsignalintercepter_p.h" +#include <QDebug> + +QTM_BEGIN_NAMESPACE + +class ServiceMetaSignalIntercepter : public QSignalIntercepter +{ + +public: + ServiceMetaSignalIntercepter(QObject* sender, const QByteArray& signal, QServiceMetaObjectDBus* parent) + : QSignalIntercepter(sender, signal, parent), serviceDBus(parent) + { + + } + + void setMetaIndex(int index) + { + metaIndex = index; + } + +protected: + void activated( const QList<QVariant>& args ) + { + serviceDBus->activateMetaSignal(metaIndex, args); + } +private: + QServiceMetaObjectDBus* serviceDBus; + int metaIndex; +}; + + +class QServiceMetaObjectDBusPrivate +{ +public: + QObject *service; + const QMetaObject *serviceMeta; + const QMetaObject *dbusMeta; +}; + +QServiceMetaObjectDBus::QServiceMetaObjectDBus(QObject* service, bool signalsObject) + : QDBusAbstractAdaptor(service) +{ + // Register our DBus custom type object + qRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + qDBusRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + + // Generate our DBus meta object + d = new QServiceMetaObjectDBusPrivate(); + d->service = service; + d->serviceMeta = service->metaObject(); + d->dbusMeta = dbusMetaObject(signalsObject); + + // Relay signals from the service to the constructed DBus meta object + connectMetaSignals(signalsObject); +} + +QServiceMetaObjectDBus::~QServiceMetaObjectDBus() +{ + if (d->dbusMeta) + qFree(const_cast<QMetaObject*>(d->dbusMeta)); + + delete d; +} + +/*! + Relays all signals from the service object to the converted DBus service adaptor so + that when a service signal is emitted the DBus object will emit the counterpart signal +*/ +void QServiceMetaObjectDBus::connectMetaSignals(bool signalsObject) { + if (!signalsObject) { + // Automatically relay signals from service object to adaptor + setAutoRelaySignals(true); + + // Connect signals with custom arguments + int methodCount = d->serviceMeta->methodCount(); + for (int i = 0; i < methodCount; i++) { + QMetaMethod mm = d->serviceMeta->method(i); + + + if (mm.methodType() == QMetaMethod::Signal) { + QByteArray sig(mm.signature()); + bool customType = false; + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + + // Ignore all QObject calls + const QMetaObject *mo = QObject::metaObject(); + int qobjectIndex = mo->indexOfMethod(sig); + if (qobjectIndex >= 0) + continue; + + // Detects custom types as passed arguments + for (int arg = 0; arg < pTypesCount; arg++) { + const QByteArray& type = pTypes[arg]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + customType = true; + } + } + + // Connects the service signal to the corresponding DBus service signal + if (customType) { + QByteArray signal = mm.signature(); + ServiceMetaSignalIntercepter *intercept = + new ServiceMetaSignalIntercepter(d->service, "2"+signal, this); + intercept->setMetaIndex(i); + } + } + } + } +} + +/*! + Relays the activation to the DBus object signal with the given id and arguments list when the + intercepter for signals with custom arguments has been activated. This bypasses the metacall + which usually does the relaying for signals with standard arguments since no pre-connection + conversions are required. +*/ +void QServiceMetaObjectDBus::activateMetaSignal(int id, const QVariantList& args) +{ + QMetaMethod method = d->serviceMeta->method(id); + + // Converts the argument list to values supported by the QtDBus type system + QVariantList convertedList = args; + QByteArray sig(method.signature()); + QList<QByteArray> params = method.parameterTypes(); + + for (int i = 0; i < params.size(); i++) { + QVariant dbusVariant = args[i]; + + // Convert custom types + const QByteArray& type = params[i]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(type); + + if (variantType >= QMetaType::User) { + // Wrap custom types in a QDBusVariant of the type name and + // a buffer of its variant-wrapped data + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append); + stream << args[i]; + + QServiceUserTypeDBus customType; + customType.typeName = type; + customType.variantBuffer = buffer; + + QDBusVariant replacement(QVariant::fromValue(customType)); + convertedList.replace(i, QVariant::fromValue(replacement)); + } + + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + } + } + + // Activate the DBus object signal call + const int numArgs = convertedList.size(); + QVarLengthArray<void *, 32> a( numArgs+1 ); + a[0] = 0; + + const QList<QByteArray> pTypes = method.parameterTypes(); + for ( int arg = 0; arg < numArgs; ++arg ) { + if (pTypes.at(arg) == "QVariant") + a[arg+1] = (void *)&( convertedList[arg] ); + else + a[arg+1] = (void *)( convertedList[arg].data() ); + } + + int dbusIndex = d->dbusMeta->indexOfSignal(sig); + QMetaObject::activate(this, dbusIndex, a.data()); +} + + +/*! + Build a metaobject that represents the service object as a valid service that + satisfies the QtDBus type system. +*/ +const QMetaObject* QServiceMetaObjectDBus::dbusMetaObject(bool signalsObject) const +{ + // Create a meta-object to represent all the contents of our service on DBus + QMetaObjectBuilder builder; + + builder.setClassName(d->serviceMeta->className()); + builder.setSuperClass(d->serviceMeta->superClass()); // needed? + + const QMetaObject* mo = d->serviceMeta; + while (mo && strcmp(mo->className(), "QObject")) { + // Add our methods, signals and slots + for (int i = mo->methodOffset(); i < mo->methodCount(); i++) { + QMetaMethod mm = mo->method(i); + + if (signalsObject && mm.methodType() != QMetaMethod::Signal) + continue; + + // Convert QVariant and custom return types to QDBusVariants + QByteArray ret(mm.typeName()); + const QByteArray& type = mm.typeName(); + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + ret = QByteArray("QDBusVariant"); + } + + // Convert QVariant and custom argument types to QDBusVariants + QByteArray sig(mm.signature()); + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + for (int i=0; i < pTypesCount; i++) { + const QByteArray& type = pTypes[i]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + } + } + + // Add a MetaMethod with converted signature to our builder + QMetaMethodBuilder method; + switch (mm.methodType()) { + case QMetaMethod::Method: + method = builder.addMethod(sig); + break; + case QMetaMethod::Slot: + method = builder.addSlot(sig); + break; + case QMetaMethod::Signal: + method = builder.addSignal(sig); + break; + default: + break; + } + + // Make sure our built MetaMethod is identical, excluding conversion + method.setReturnType(ret); + method.setParameterNames(mm.parameterNames()); + method.setTag(mm.tag()); + method.setAccess(mm.access()); + method.setAttributes(mm.attributes()); + } + + if (signalsObject) + return builder.toMetaObject(); + + // Add our property accessor methods + // NOTE: required because read/reset properties over DBus require adaptors + // otherwise a metacall won't be invoked as QMetaObject::ReadProperty + // or QMetaObject::ResetProperty + QMetaMethodBuilder readProp; + readProp = builder.addMethod(QByteArray("propertyRead(QString)")); + readProp.setReturnType(QByteArray("QDBusVariant")); + QList<QByteArray> params; + params << QByteArray("name"); + readProp.setParameterNames(params); + + QMetaMethodBuilder resetProp; + resetProp = builder.addMethod(QByteArray("propertyReset(QString)")); + QList<QByteArray> paramsReset; + paramsReset << QByteArray("name"); + resetProp.setParameterNames(paramsReset); + + + // Add our properties/enums + int propCount = d->serviceMeta->propertyCount(); + for (int i=0; i<propCount; i++) { + QMetaProperty mp = d->serviceMeta->property(i); + + QMetaPropertyBuilder property = builder.addProperty(mp.name(), mp.typeName()); + property.setReadable(mp.isReadable()); + property.setWritable(mp.isWritable()); + property.setResettable(mp.isResettable()); + property.setDesignable(mp.isDesignable()); + property.setScriptable(mp.isScriptable()); + property.setStored(mp.isStored()); + property.setEditable(mp.isEditable()); + property.setUser(mp.isUser()); + property.setStdCppSet(mp.hasStdCppSet()); + property.setEnumOrFlag(mp.isEnumType()); + } + + // Needs Enumerators support when QtDBus supports + + mo = mo->superClass(); + } + + // return our constructed dbus metaobject + return builder.toMetaObject(); +} + +/*! + Provide custom Q_OBJECT implementation of the metaObject +*/ +const QMetaObject* QServiceMetaObjectDBus::metaObject() const +{ + // Provide our construected DBus service metaobject + return d->dbusMeta; +} + +/*! + Overrides metacall which relays the DBus service call to the actual service + meta object. Positive return will indicate that the metacall was unsuccessful +*/ +int QServiceMetaObjectDBus::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + int dbusIndex = id; + + // Relay the meta-object call to the service object + if (c == QMetaObject::InvokeMetaMethod) { + // METHOD CALL + QMetaMethod method = d->dbusMeta->method(id); + + const bool isSignal = (method.methodType() == QMetaMethod::Signal); + + ///////////////////// CHECK SPECIAL PROPERTY /////////////////////// + // This is required because property READ/RESET doesn't function + // as desired over DBus without the use of adaptors. Temporary + // methods propertyRead and propertyReset are added to the published + // meta object and relay the correct property call + QString methodName(QLatin1String(method.signature())); + methodName.truncate(methodName.indexOf(QLatin1String("("))); + + if (methodName == QLatin1String("propertyRead")) { + QString propertyName = *reinterpret_cast<QString*>(a[1]); + int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData()); + id = qt_metacall(QMetaObject::ReadProperty, index, a); + return id; + + } else if (methodName == QLatin1String("propertyReset")) { + QString propertyName = *reinterpret_cast<QString*>(a[1]); + int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData()); + id = qt_metacall(QMetaObject::ResetProperty, index, a); + return id; + } + //////////////////////////////////////////////////////////////////// + + // Find the corresponding signature to our service object + QByteArray sig(method.signature()); + int count = methodName.size() + 1; + const QList<QByteArray> xTypes = method.parameterTypes(); + const int xTypesCount = xTypes.count(); + for (int i=0; i < xTypesCount; i++) { + const QByteArray& t = xTypes[i]; + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(t); + } + + // Check for QVariants or custom types, represented as QDBusVariants + if (t == "QDBusVariant") { + // Convert QDBusVariant to QVariant + QVariant convert = QVariant(variantType, a[i+1]); + QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(convert); + QVariant variant = dbusVariant.variant(); + + // Is a custom argument if castable to QDBusArgument + bool hasCustomType = variant.canConvert<QDBusArgument>(); + QByteArray replacement("QVariant"); + + // Custom types will have QDBusArgument streaming operators for + // the QServiceUserTypeDBus object. Extract the real type name + // and buffered QVariant from this + if (hasCustomType) { + QDBusArgument demarshall = variant.value<QDBusArgument>(); + QServiceUserTypeDBus userType = qdbus_cast<QServiceUserTypeDBus>(demarshall); + *reinterpret_cast<QVariant*>(a[i+1]) = userType.variantBuffer; + + replacement = userType.typeName; + } + + // Replace "QDBusVariant" with "QVariant" or custom type name + sig.replace(count, 12, replacement); + count += replacement.size(); + + } else { + // Supported type so skip this paramater + count += t.size(); + } + + // Skips the comma if not last parameter + if (i < xTypesCount) + count += 1; + } + + // Find the corresponding method metaindex to our service object + id = d->serviceMeta->indexOfMethod(sig); + QMetaMethod mm = d->serviceMeta->method(id); + + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + + const char* typeNames[] = {0,0,0,0,0,0,0,0,0,0}; + const void* params[] = {0,0,0,0,0,0,0,0,0,0}; + bool hasCustomType = false; + + // Process arguments + for (int i=0; i < pTypesCount; i++) { + const QByteArray& t = pTypes[i]; + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(t); + } + + if (variantType >= QMetaType::User) { + // Custom argument + QVariant convert = QVariant(QVariant::ByteArray, a[i+1]); + QByteArray buffer = convert.toByteArray(); + QDataStream stream(&buffer, QIODevice::ReadWrite); + + // Load our buffered variant-wrapped custom type + QVariant *customType = new QVariant(variantType, (const void*)0); + QMetaType::load(stream, QMetaType::type("QVariant"), customType); + + typeNames[i] = customType->typeName(); + params[i] = customType->constData(); + hasCustomType = true; + } + } + + // Check if this is a signal emit and activate + if (isSignal) { + QMetaObject::activate(this, dbusIndex, a); + return id; + } + + // Check for custom return types and make the metacall + const QByteArray& type = mm.typeName(); + int retType = QVariant::nameToType(type); + retType = QMetaType::type(type); + if (retType >= QMetaType::User) { + // Invoke the object method directly for custom return types + bool result = false; + QVariant returnValue = QVariant(retType, (const void*)0); + QGenericReturnArgument ret(type, returnValue.data()); + result = mm.invoke(d->service, ret, + QGenericArgument(typeNames[0], params[0]), + QGenericArgument(typeNames[1], params[1]), + QGenericArgument(typeNames[2], params[2]), + QGenericArgument(typeNames[3], params[3]), + QGenericArgument(typeNames[4], params[4]), + QGenericArgument(typeNames[5], params[5]), + QGenericArgument(typeNames[6], params[6]), + QGenericArgument(typeNames[7], params[7]), + QGenericArgument(typeNames[8], params[8]), + QGenericArgument(typeNames[9], params[9])); + + if (result) { + // Wrap custom return type in a QDBusVariant of the type + // and a buffer of its variant-wrapped data + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::WriteOnly | QIODevice::Append); + stream << returnValue; + + QServiceUserTypeDBus customType; + customType.typeName = type; + customType.variantBuffer = buffer; + + QDBusVariant replacement(QVariant::fromValue(customType)); + *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; + + // Return negative id to say metacall was handled externally + return -1; + } + + } else { + // Void or standard return types + if (hasCustomType == true) { + // Invoke the object method directly for custom arguments + bool result = false; + result = mm.invoke(d->service, + QGenericArgument(typeNames[0], params[0]), + QGenericArgument(typeNames[1], params[1]), + QGenericArgument(typeNames[2], params[2]), + QGenericArgument(typeNames[3], params[3]), + QGenericArgument(typeNames[4], params[4]), + QGenericArgument(typeNames[5], params[5]), + QGenericArgument(typeNames[6], params[6]), + QGenericArgument(typeNames[7], params[7]), + QGenericArgument(typeNames[8], params[8]), + QGenericArgument(typeNames[9], params[9])); + if (result) { + // Return negative id to say metacall was handled externally + return -1; + } + } else { + // Relay standard metacall to service object + id = d->service->qt_metacall(c, id, a); + } + } + + } else { + // PROPERTY CALL + + // Find the corresponding property metaindex of our service object + QMetaProperty property = d->dbusMeta->property(id); + QByteArray name(property.name()); + id = d->serviceMeta->indexOfProperty(name); + + if (c == QMetaObject::ReadProperty) { + // Convert to DBusVariant + QMetaProperty mp = d->serviceMeta->property(id); + QVariant value = mp.read(d->service); + QDBusVariant replacement(value); + *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; + + // Return negative id to say metacall was handled externally + return -1; + } + + // Metacall our service object property + id = d->service->qt_metacall(c, id, a); + } + + return id; +} + +void *QServiceMetaObjectDBus::qt_metacast(const char* className) +{ + if (!className) return 0; + //this object should not be castable to anything but QObject + return QObject::qt_metacast(className); +} + +QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType) +{ + argument.beginStructure(); + argument << myType.typeName << myType.variantBuffer; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType) +{ + argument.beginStructure(); + argument >> myType.typeName >> myType.variantBuffer; + argument.endStructure(); + return argument; +} + +QTM_END_NAMESPACE |