src/dbus/qdbusxmlgenerator.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dbus/qdbusxmlgenerator.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qstringlist.h>
+
+#include "qdbusinterface_p.h"   // for ANNOTATION_NO_WAIT
+#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
+#include "qdbusconnection_p.h"  // for the flags
+#include "qdbusmetatype_p.h"
+#include "qdbusmetatype.h"
+#include "qdbusutil_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
+                                                       const QMetaObject *base, int flags);
+
+static inline QString typeNameToXml(const char *typeName)
+{
+    // ### copied from qtextdocument.cpp
+    // ### move this into QtCore at some point
+    QString plain = QLatin1String(typeName);
+    QString rich;
+    rich.reserve(int(plain.length() * 1.1));
+    for (int i = 0; i < plain.length(); ++i) {
+        if (plain.at(i) == QLatin1Char('<'))
+            rich += QLatin1String("&lt;");
+        else if (plain.at(i) == QLatin1Char('>'))
+            rich += QLatin1String("&gt;");
+        else if (plain.at(i) == QLatin1Char('&'))
+            rich += QLatin1String("&amp;");
+        else
+            rich += plain.at(i);
+    }
+    return rich;
+}
+
+// implement the D-Bus org.freedesktop.DBus.Introspectable interface
+// we do that by analysing the metaObject of all the adaptor interfaces
+
+static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
+{
+    QString retval;
+
+    // start with properties:
+    if (flags & (QDBusConnection::ExportScriptableProperties |
+                 QDBusConnection::ExportNonScriptableProperties)) {
+        for (int i = propOffset; i < mo->propertyCount(); ++i) {
+            static const char *accessvalues[] = {0, "read", "write", "readwrite"};
+
+            QMetaProperty mp = mo->property(i);
+
+            if (!((mp.isScriptable() && (flags & QDBusConnection::ExportScriptableProperties)) ||
+                  (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties))))
+                continue;
+
+            int access = 0;
+            if (mp.isReadable())
+                access |= 1;
+            if (mp.isWritable())
+                access |= 2;
+
+            int typeId = qDBusNameToTypeId(mp.typeName());
+            if (!typeId)
+                continue;
+            const char *signature = QDBusMetaType::typeToSignature(typeId);
+            if (!signature)
+                continue;
+
+            retval += QString::fromLatin1("    <property name=\"%1\" type=\"%2\" access=\"%3\"")
+                      .arg(QLatin1String(mp.name()))
+                      .arg(QLatin1String(signature))
+                      .arg(QLatin1String(accessvalues[access]));
+
+            if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
+                const char *typeName = QVariant::typeToName(QVariant::Type(typeId));
+                retval += QString::fromLatin1(">\n      <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"%3\"/>\n    </property>\n")
+                          .arg(typeNameToXml(typeName));
+            } else {
+                retval += QLatin1String("/>\n");
+            }
+        }
+    }
+
+    // now add methods:
+    for (int i = methodOffset; i < mo->methodCount(); ++i) {
+        QMetaMethod mm = mo->method(i);
+        QByteArray signature = mm.signature();
+        int paren = signature.indexOf('(');
+
+        bool isSignal;
+        if (mm.methodType() == QMetaMethod::Signal)
+            // adding a signal
+            isSignal = true;
+        else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public)
+            isSignal = false;
+        else
+            continue;           // neither signal nor public slot
+
+        if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals |
+                                   QDBusConnection::ExportNonScriptableSignals)))
+            continue;           // we're not exporting any signals
+        if (!isSignal && !(flags & (QDBusConnection::ExportScriptableSlots |
+                                    QDBusConnection::ExportNonScriptableSlots)))
+            continue;           // we're not exporting any slots
+
+        QString xml = QString::fromLatin1("    <%1 name=\"%2\">\n")
+                      .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
+                      .arg(QLatin1String(signature.left(paren)));
+
+        // check the return type first
+        int typeId = qDBusNameToTypeId(mm.typeName());
+        if (typeId) {
+            const char *typeName = QDBusMetaType::typeToSignature(typeId);
+            if (typeName) {
+                xml += QString::fromLatin1("      <arg type=\"%1\" direction=\"out\"/>\n")
+                       .arg(typeNameToXml(typeName));
+
+                // do we need to describe this argument?
+                if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
+                    xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
+                           .arg(typeNameToXml(mm.typeName()));
+            } else
+                continue;
+        }
+        else if (*mm.typeName())
+            continue;           // wasn't a valid type
+
+        QList<QByteArray> names = mm.parameterNames();
+        QList<int> types;
+        int inputCount = qDBusParametersForMethod(mm, types);
+        if (inputCount == -1)
+            continue;           // invalid form
+        if (isSignal && inputCount + 1 != types.count())
+            continue;           // signal with output arguments?
+        if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
+            continue;           // signal with QDBusMessage argument?
+        if (isSignal && mm.attributes() & QMetaMethod::Cloned)
+            continue;           // cloned signal?
+
+        int j;
+        bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
+        for (j = 1; j < types.count(); ++j) {
+            // input parameter for a slot or output for a signal
+            if (types.at(j) == QDBusMetaTypeId::message) {
+                isScriptable = true;
+                continue;
+            }
+
+            QString name;
+            if (!names.at(j - 1).isEmpty())
+                name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));
+
+            bool isOutput = isSignal || j > inputCount;
+
+            const char *signature = QDBusMetaType::typeToSignature(types.at(j));
+            xml += QString::fromLatin1("      <arg %1type=\"%2\" direction=\"%3\"/>\n")
+                   .arg(name)
+                   .arg(QLatin1String(signature))
+                   .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
+
+            // do we need to describe this argument?
+            if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
+                const char *typeName = QVariant::typeToName( QVariant::Type(types.at(j)) );
+                xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
+                       .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
+                       .arg(isOutput ? j - inputCount : j - 1)
+                       .arg(typeNameToXml(typeName));
+            }
+        }
+
+        int wantedMask;
+        if (isScriptable)
+            wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
+                                  : QDBusConnection::ExportScriptableSlots;
+        else
+            wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
+                                  : QDBusConnection::ExportNonScriptableSlots;
+        if ((flags & wantedMask) != wantedMask)
+            continue;
+
+        if (qDBusCheckAsyncTag(mm.tag()))
+            // add the no-reply annotation
+            xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
+                                 " value=\"true\"/>\n");
+
+        retval += xml;
+        retval += QString::fromLatin1("    </%1>\n")
+                  .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
+    }
+
+    return retval;
+}
+
+QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
+                                   const QMetaObject *base, int flags)
+{
+    if (interface.isEmpty())
+        // generate the interface name from the meta object
+        interface = qDBusInterfaceFromMetaObject(mo);
+
+    QString xml;
+    int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
+    if (idx >= mo->classInfoOffset())
+        return QString::fromUtf8(mo->classInfo(idx).value());
+    else
+        xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
+
+    if (xml.isEmpty())
+        return QString();       // don't add an empty interface
+    return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
+        .arg(interface, xml);
+}
+#if 0
+QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
+                                   int flags)
+{
+    if (interface.isEmpty()) {
+        // generate the interface name from the meta object
+        int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
+        if (idx >= mo->classInfoOffset()) {
+            interface = QLatin1String(mo->classInfo(idx).value());
+        } else {
+            interface = QLatin1String(mo->className());
+            interface.replace(QLatin1String("::"), QLatin1String("."));
+
+            if (interface.startsWith(QLatin1String("QDBus"))) {
+                interface.prepend(QLatin1String("com.trolltech.QtDBus."));
+            } else if (interface.startsWith(QLatin1Char('Q')) &&
+                       interface.length() >= 2 && interface.at(1).isUpper()) {
+                // assume it's Qt
+                interface.prepend(QLatin1String("com.trolltech.Qt."));
+            } else if (!QCoreApplication::instance()||
+                       QCoreApplication::instance()->applicationName().isEmpty()) {
+                interface.prepend(QLatin1String("local."));
+            } else {
+                interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName());
+                QStringList domainName =
+                    QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'),
+                                                                             QString::SkipEmptyParts);
+                if (domainName.isEmpty())
+                    interface.prepend(QLatin1String("local."));
+                else
+                    for (int i = 0; i < domainName.count(); ++i)
+                        interface.prepend(QLatin1Char('.')).prepend(domainName.at(i));
+            }
+        }
+    }
+
+    QString xml;
+    int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
+    if (idx >= mo->classInfoOffset())
+        return QString::fromUtf8(mo->classInfo(idx).value());
+    else
+        xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
+
+    if (xml.isEmpty())
+        return QString();       // don't add an empty interface
+    return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
+        .arg(interface, xml);
+}
+
+#endif
+
+QT_END_NAMESPACE