summaryrefslogtreecommitdiffstats
path: root/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp')
-rw-r--r--src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp458
1 files changed, 458 insertions, 0 deletions
diff --git a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
new file mode 100644
index 0000000000..66a59b56ec
--- /dev/null
+++ b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
@@ -0,0 +1,458 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $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 <QByteArray>
+#include <QString>
+#include <QVarLengthArray>
+#include <QFile>
+#include <QList>
+#include <QBuffer>
+#include <QRegExp>
+#include <QVector>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "qdbusconnection.h" // for the Export* flags
+#include "qdbusconnection_p.h" // for the qDBusCheckAsyncTag
+
+// copied from dbus-protocol.h:
+static const char docTypeHeader[] =
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
+
+#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
+#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface"
+#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection"
+
+#include "qdbusmetatype_p.h"
+#include "qdbusmetatype.h"
+#include "qdbusutil_p.h"
+
+#include "moc.h"
+#include "generator.h"
+
+#define PROGRAMNAME "qdbuscpp2xml"
+#define PROGRAMVERSION "0.2"
+#define PROGRAMCOPYRIGHT "Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)."
+
+static QString outputFile;
+static int flags;
+
+static const char help[] =
+ "Usage: " PROGRAMNAME " [options...] [files...]\n"
+ "Parses the C++ source or header file containing a QObject-derived class and\n"
+ "produces the D-Bus Introspection XML."
+ "\n"
+ "Options:\n"
+ " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n"
+ " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n"
+ " -a Output all scriptable contents (equivalent to -psm)\n"
+ " -A Output all contents (equivalent to -PSM)\n"
+ " -o <filename> Write the output to file <filename>\n"
+ " -h Show this information\n"
+ " -V Show the program version and quit.\n"
+ "\n";
+
+
+int qDBusParametersForMethod(const FunctionDef &mm, QVector<int>& metaTypes)
+{
+ QList<QByteArray> parameterTypes;
+
+ foreach (const ArgumentDef &arg, mm.arguments)
+ parameterTypes.append(arg.normalizedType);
+
+ return qDBusParametersForMethod(parameterTypes, metaTypes);
+}
+
+
+static inline QString typeNameToXml(const char *typeName)
+{
+ QString plain = QLatin1String(typeName);
+ return plain.toHtmlEscaped();
+}
+
+static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
+
+ QString xml = QString::fromLatin1(" <%1 name=\"%2\">\n")
+ .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
+ .arg(QLatin1String(mm.name));
+
+ // check the return type first
+ int typeId = QMetaType::type(mm.normalizedType.constData());
+ if (typeId != QMetaType::Void) {
+ 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.normalizedType.constData()));
+ } else {
+ return QString();
+ }
+ } else if (!mm.normalizedType.isEmpty()) {
+ return QString(); // wasn't a valid type
+ }
+ }
+ QList<ArgumentDef> names = mm.arguments;
+ QVector<int> types;
+ int inputCount = qDBusParametersForMethod(mm, types);
+ if (inputCount == -1)
+ return QString(); // invalid form
+ if (isSignal && inputCount + 1 != types.count())
+ return QString(); // signal with output arguments?
+ if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
+ return QString(); // signal with QDBusMessage argument?
+
+ bool isScriptable = mm.isScriptable;
+ for (int 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).name.isEmpty())
+ name = QString::fromLatin1("name=\"%1\" ").arg(QString::fromLatin1(names.at(j - 1).name));
+
+ 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 = QMetaType::typeName(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 && !isSignal ? 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)
+ return QString();
+
+ if (qDBusCheckAsyncTag(mm.tag.constData()))
+ // add the no-reply annotation
+ xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\""
+ " value=\"true\"/>\n");
+
+ QString retval = xml;
+ retval += QString::fromLatin1(" </%1>\n")
+ .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
+
+ return retval;
+}
+
+
+static QString generateInterfaceXml(const ClassDef *mo)
+{
+ QString retval;
+
+ // start with properties:
+ if (flags & (QDBusConnection::ExportScriptableProperties |
+ QDBusConnection::ExportNonScriptableProperties)) {
+ static const char *accessvalues[] = {0, "read", "write", "readwrite"};
+ foreach (const PropertyDef &mp, mo->propertyList) {
+ if (!((!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportScriptableProperties)) ||
+ (!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportNonScriptableProperties))))
+ continue;
+
+ int access = 0;
+ if (!mp.read.isEmpty())
+ access |= 1;
+ if (!mp.write.isEmpty())
+ access |= 2;
+
+ int typeId = QMetaType::type(mp.type.constData());
+ 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) {
+ retval += QString::fromLatin1(">\n <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
+ .arg(typeNameToXml(mp.type.constData()));
+ } else {
+ retval += QLatin1String("/>\n");
+ }
+ }
+ }
+
+ // now add methods:
+
+ if (flags & (QDBusConnection::ExportScriptableSignals | QDBusConnection::ExportNonScriptableSignals)) {
+ foreach (const FunctionDef &mm, mo->signalList) {
+ if (mm.wasCloned)
+ continue;
+
+ retval += addFunction(mm, true);
+ }
+ }
+
+ if (flags & (QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportNonScriptableSlots)) {
+ foreach (const FunctionDef &slot, mo->slotList) {
+ if (slot.access == FunctionDef::Public)
+ retval += addFunction(slot);
+ }
+ foreach (const FunctionDef &method, mo->methodList) {
+ if (method.access == FunctionDef::Public)
+ retval += addFunction(method);
+ }
+ }
+ return retval;
+}
+
+QString qDBusInterfaceFromClassDef(const ClassDef *mo)
+{
+ QString interface;
+
+ foreach (ClassInfoDef cid, mo->classInfoList) {
+ if (cid.name == QCLASSINFO_DBUS_INTERFACE)
+ return QString::fromUtf8(cid.value);
+ }
+ 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("local.com.trolltech.Qt."));
+ } else {
+ interface.prepend(QLatin1String("local."));
+ }
+
+ return interface;
+}
+
+
+QString qDBusGenerateClassDefXml(const ClassDef *cdef)
+{
+ foreach (const ClassInfoDef &cid, cdef->classInfoList) {
+ if (cid.name == QCLASSINFO_DBUS_INTROSPECTION)
+ return QString::fromUtf8(cid.value);
+ }
+
+ // generate the interface name from the meta object
+ QString interface = qDBusInterfaceFromClassDef(cdef);
+
+ QString xml = generateInterfaceXml(cdef);
+
+ if (xml.isEmpty())
+ return QString(); // don't add an empty interface
+ return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n")
+ .arg(interface, xml);
+}
+
+static void showHelp()
+{
+ printf("%s", help);
+ exit(0);
+}
+
+static void showVersion()
+{
+ printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
+ printf("D-Bus QObject-to-XML converter\n");
+ exit(0);
+}
+
+static void parseCmdLine(QStringList &arguments)
+{
+ flags = 0;
+ for (int i = 0; i < arguments.count(); ++i) {
+ const QString arg = arguments.at(i);
+
+ if (arg == QLatin1String("--help"))
+ showHelp();
+
+ if (!arg.startsWith(QLatin1Char('-')))
+ continue;
+
+ char c = arg.count() == 2 ? arg.at(1).toLatin1() : char(0);
+ switch (c) {
+ case 'P':
+ flags |= QDBusConnection::ExportNonScriptableProperties;
+ // fall through
+ case 'p':
+ flags |= QDBusConnection::ExportScriptableProperties;
+ break;
+
+ case 'S':
+ flags |= QDBusConnection::ExportNonScriptableSignals;
+ // fall through
+ case 's':
+ flags |= QDBusConnection::ExportScriptableSignals;
+ break;
+
+ case 'M':
+ flags |= QDBusConnection::ExportNonScriptableSlots;
+ // fall through
+ case 'm':
+ flags |= QDBusConnection::ExportScriptableSlots;
+ break;
+
+ case 'A':
+ flags |= QDBusConnection::ExportNonScriptableContents;
+ // fall through
+ case 'a':
+ flags |= QDBusConnection::ExportScriptableContents;
+ break;
+
+ case 'o':
+ if (arguments.count() < i + 2 || arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
+ printf("-o expects a filename\n");
+ exit(1);
+ }
+ outputFile = arguments.takeAt(i + 1);
+ break;
+
+ case 'h':
+ case '?':
+ showHelp();
+ break;
+
+ case 'V':
+ showVersion();
+ break;
+
+ default:
+ printf("unknown option: \"%s\"\n", qPrintable(arg));
+ exit(1);
+ }
+ }
+
+ if (flags == 0)
+ flags = QDBusConnection::ExportScriptableContents
+ | QDBusConnection::ExportNonScriptableContents;
+}
+
+int main(int argc, char **argv)
+{
+ QStringList args;
+ for (int n = 1; n < argc; ++n)
+ args.append(QString::fromLocal8Bit(argv[n]));
+ parseCmdLine(args);
+
+ QList<ClassDef> classes;
+
+ for (int i = 0; i < args.count(); ++i) {
+ const QString arg = args.at(i);
+
+ if (arg.startsWith(QLatin1Char('-')))
+ continue;
+
+ QFile f(arg);
+ if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
+ qPrintable(arg), qPrintable(f.errorString()));
+ return 1;
+ }
+
+ Preprocessor pp;
+ Moc moc(pp);
+ pp.macros["Q_MOC_RUN"];
+ pp.macros["__cplusplus"];
+
+ const QByteArray filename = QFile::decodeName(argv[i]).toLatin1();
+
+ moc.filename = filename;
+ moc.currentFilenames.push(filename);
+
+ moc.symbols = pp.preprocessed(moc.filename, &f);
+ moc.parse();
+
+ if (moc.classList.isEmpty())
+ return 0;
+ classes = moc.classList;
+
+ f.close();
+ }
+
+ QFile output;
+ if (outputFile.isEmpty()) {
+ output.open(stdout, QIODevice::WriteOnly);
+ } else {
+ output.setFileName(outputFile);
+ if (!output.open(QIODevice::WriteOnly)) {
+ fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
+ qPrintable(outputFile), qPrintable(output.errorString()));
+ return 1;
+ }
+ }
+
+ output.write(docTypeHeader);
+ output.write("<node>\n");
+ foreach (const ClassDef &cdef, classes) {
+ QString xml = qDBusGenerateClassDefXml(&cdef);
+ output.write(xml.toLocal8Bit());
+ }
+ output.write("</node>\n");
+
+ return 0;
+}
+