summaryrefslogtreecommitdiffstats
path: root/src/serviceframework/ipc/qservicemetaobject_dbus.cpp
diff options
context:
space:
mode:
authorAlex <qt-info@nokia.com>2011-06-15 19:14:16 +1000
committerAlex <qt-info@nokia.com>2011-06-17 17:31:52 +1000
commit0e3be465c935700019f6232f88381613d51776d7 (patch)
treee2a44eda69ebd38183ea251532e0da9fcb6020a6 /src/serviceframework/ipc/qservicemetaobject_dbus.cpp
parent79ec22e9cd8ef8f679c541c4fd76d616e737b8bf (diff)
Add first version of QtServiceFramework library
Diffstat (limited to 'src/serviceframework/ipc/qservicemetaobject_dbus.cpp')
-rw-r--r--src/serviceframework/ipc/qservicemetaobject_dbus.cpp580
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