source: branches/vendor/trolltech/qt/current/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 12 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 39.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtCore/qbytearray.h>
43#include <QtCore/qcoreapplication.h>
44#include <QtCore/qdatetime.h>
45#include <QtCore/qdebug.h>
46#include <QtCore/qfile.h>
47#include <QtCore/qstring.h>
48#include <QtCore/qstringlist.h>
49#include <QtCore/qtextstream.h>
50#include <QtCore/qset.h>
51
52#include <QtDBus/QtDBus>
53#include "private/qdbusmetaobject_p.h"
54#include "private/qdbusintrospection_p.h"
55
56#include <sys/types.h>
57#include <stdio.h>
58#include <stdlib.h>
59
60#ifdef Q_WS_WIN
61#include <process.h>
62#endif
63
64#define PROGRAMNAME     "qdbusxml2cpp"
65#define PROGRAMVERSION  "0.7"
66#define PROGRAMCOPYRIGHT "Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)."
67
68#define ANNOTATION_NO_WAIT      "org.freedesktop.DBus.Method.NoReply"
69
70static QString globalClassName;
71static QString parentClassName;
72static QString proxyFile;
73static QString adaptorFile;
74static QString inputFile;
75static bool skipNamespaces;
76static bool verbose;
77static bool includeMocs;
78static QString commandLine;
79static QStringList includes;
80static QStringList wantedInterfaces;
81
82static const char help[] =
83    "Usage: " PROGRAMNAME " [options...] [xml-or-xml-file] [interfaces...]\n"
84    "Produces the C++ code to implement the interfaces defined in the input file.\n"
85    "If no options are given, the code is written to the standard output.\n"
86    "\n"
87    "Options:\n"
88    "  -a <filename>    Write the adaptor code to <filename>\n"
89    "  -c <classname>   Use <classname> as the class name for the generated classes\n"
90    "  -h               Show this information\n"
91    "  -i <filename>    Add #include to the output\n"
92    "  -l <classname>   When generating an adaptor, use <classname> as the parent class\n"
93    "  -m               Generate #include \"filename.moc\" statements in the .cpp files\n"
94    "  -N               Don't use namespaces\n"
95    "  -p <filename>    Write the proxy code to <filename>\n"
96    "  -v               Be verbose.\n"
97    "  -V               Show the program version and quit.\n"
98    "\n"
99    "If the file name given to the options -a and -p does not end in .cpp or .h, the\n"
100    "program will automatically append the suffixes and produce both files.\n"
101    "You can also use a colon (:) to separate the header name from the source file\n"
102    "name, as in '-a filename_p.h:filename.cpp'.\n";
103
104static const char includeList[] =
105    "#include <QtCore/QByteArray>\n"
106    "#include <QtCore/QList>\n"
107    "#include <QtCore/QMap>\n"
108    "#include <QtCore/QString>\n"
109    "#include <QtCore/QStringList>\n"
110    "#include <QtCore/QVariant>\n";
111
112static const char forwardDeclarations[] =
113    "class QByteArray;\n"
114    "template<class T> class QList;\n"
115    "template<class Key, class Value> class QMap;\n"
116    "class QString;\n"
117    "class QStringList;\n"
118    "class QVariant;\n";
119
120static void showHelp()
121{
122    printf("%s", help);
123    exit(0);
124}
125
126static void showVersion()
127{
128    printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
129    printf("D-Bus binding tool for Qt\n");
130    exit(0);
131}
132
133static QString nextArg(QStringList &args, int i, char opt)
134{
135    QString arg = args.value(i);
136    if (arg.isEmpty()) {
137        printf("-%c needs at least one argument\n", opt);
138        exit(1);
139    }
140    return args.takeAt(i);
141}
142
143static void parseCmdLine(QStringList args)
144{
145    args.takeFirst();
146
147    commandLine = QLatin1String(PROGRAMNAME " ");
148    commandLine += args.join(QLatin1String(" "));
149
150    int i = 0;
151    while (i < args.count()) {
152
153        if (!args.at(i).startsWith(QLatin1Char('-'))) {
154            ++i;
155            continue;
156        }
157        QString arg = args.takeAt(i);
158
159        char c = '\0';
160        if (arg.length() == 2)
161            c = arg.at(1).toLatin1();
162        else if (arg == QLatin1String("--help"))
163            c = 'h';
164
165        switch (c) {
166        case 'a':
167            adaptorFile = nextArg(args, i, 'a');
168            break;
169
170        case 'c':
171            globalClassName = nextArg(args, i, 'c');
172            break;
173
174        case 'v':
175            verbose = true;
176            break;
177
178        case 'i':
179            includes << nextArg(args, i, 'i');
180            break;
181
182        case 'l':
183            parentClassName = nextArg(args, i, 'l');
184            break;
185
186        case 'm':
187            includeMocs = true;
188            break;
189
190        case 'N':
191            skipNamespaces = true;
192            break;
193
194        case '?':
195        case 'h':
196            showHelp();
197            break;
198
199        case 'V':
200            showVersion();
201            break;
202
203        case 'p':
204            proxyFile = nextArg(args, i, 'p');
205            break;
206
207        default:
208            printf("unknown option: '%s'\n", qPrintable(arg));
209            exit(1);
210        }
211    }
212
213    if (!args.isEmpty())
214        inputFile = args.takeFirst();
215
216    wantedInterfaces << args;
217}
218
219static QDBusIntrospection::Interfaces readInput()
220{
221    QFile input(inputFile);
222    if (inputFile.isEmpty() || inputFile == QLatin1String("-"))
223        input.open(stdin, QIODevice::ReadOnly);
224    else
225        input.open(QIODevice::ReadOnly);
226
227    QByteArray data = input.readAll();
228
229    // check if the input is already XML
230    data = data.trimmed();
231    if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") ||
232        data.startsWith("<node") || data.startsWith("<interface"))
233        // already XML
234        return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data));
235
236    fprintf(stderr, "Cannot process input: '%s'. Stop.\n", qPrintable(inputFile));
237    exit(1);
238}
239
240static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
241{
242    if (!wantedInterfaces.isEmpty()) {
243        QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();
244        while (it != interfaces.end())
245            if (!wantedInterfaces.contains(it.key()))
246                it = interfaces.erase(it);
247            else
248                ++it;
249    }
250}
251
252// produce a header name from the file name
253static QString header(const QString &name)
254{
255    QStringList parts = name.split(QLatin1Char(':'));
256    QString retval = parts.first();
257
258    if (retval.isEmpty() || retval == QLatin1String("-"))
259        return retval;
260
261    if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) &&
262        !retval.endsWith(QLatin1String(".cc")))
263        retval.append(QLatin1String(".h"));
264
265    return retval;
266}
267
268// produce a cpp name from the file name
269static QString cpp(const QString &name)
270{
271    QStringList parts = name.split(QLatin1Char(':'));
272    QString retval = parts.last();
273
274    if (retval.isEmpty() || retval == QLatin1String("-"))
275        return retval;
276
277    if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) &&
278        !retval.endsWith(QLatin1String(".cc")))
279        retval.append(QLatin1String(".cpp"));
280
281    return retval;
282}
283
284// produce a moc name from the file name
285static QString moc(const QString &name)
286{
287    QString retval = header(name);
288    if (retval.isEmpty())
289        return retval;
290
291    retval.truncate(retval.length() - 1); // drop the h in .h
292    retval += QLatin1String("moc");
293    return retval;
294}
295
296static QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost)
297{
298    ts << "/*" << endl
299       << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << endl
300       << " * Command line was: " << commandLine << endl
301       << " *" << endl
302       << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << endl
303       << " *" << endl
304       << " * This is an auto-generated file." << endl;
305
306    if (changesWillBeLost)
307        ts << " * Do not edit! All changes made to it will be lost." << endl;
308    else
309        ts << " * This file may have been hand-edited. Look for HAND-EDIT comments" << endl
310           << " * before re-generating it." << endl;
311
312    ts << " */" << endl
313       << endl;
314
315    return ts;
316}
317
318enum ClassType { Proxy, Adaptor };
319static QString classNameForInterface(const QString &interface, ClassType classType)
320{
321    if (!globalClassName.isEmpty())
322        return globalClassName;
323
324    QStringList parts = interface.split(QLatin1Char('.'));
325
326    QString retval;
327    if (classType == Proxy)
328        foreach (QString part, parts) {
329            part[0] = part[0].toUpper();
330            retval += part;
331        }
332    else {
333        retval = parts.last();
334        retval[0] = retval[0].toUpper();
335    }
336
337    if (classType == Proxy)
338        retval += QLatin1String("Interface");
339    else
340        retval += QLatin1String("Adaptor");
341
342    return retval;
343}
344
345static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection::Annotations &annotations, int paramId = -1, const char *direction = "Out")
346{
347    int type = QDBusMetaType::signatureToType(signature.toLatin1());
348    if (type == QVariant::Invalid) {
349        QString annotationName = QString::fromLatin1("com.trolltech.QtDBus.QtTypeName");
350        if (paramId >= 0)
351            annotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId);
352        QString qttype = annotations.value(annotationName);
353        if (!qttype.isEmpty())
354            return qttype.toLatin1();
355
356        fprintf(stderr, "Got unknown type `%s'\n", qPrintable(signature));
357        fprintf(stderr, "You should add <annotation name=\"%s\" value=\"<type>\"/> to the XML description\n",
358                qPrintable(annotationName));
359        exit(1);
360    }
361
362    return QVariant::typeToName(QVariant::Type(type));
363}
364
365static QString nonConstRefArg(const QByteArray &arg)
366{
367    return QLatin1String(arg + " &");
368}
369
370static QString templateArg(const QByteArray &arg)
371{
372    if (!arg.endsWith('>'))
373        return QLatin1String(arg);
374
375    return QLatin1String(arg + ' ');
376}
377
378static QString constRefArg(const QByteArray &arg)
379{
380    if (!arg.startsWith('Q'))
381        return QLatin1String(arg + ' ');
382    else
383        return QString( QLatin1String("const %1 &") ).arg( QLatin1String(arg) );
384}
385
386static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs,
387                                const QDBusIntrospection::Arguments &outputArgs =
388                                QDBusIntrospection::Arguments())
389{
390    QStringList retval;
391    for (int i = 0; i < inputArgs.count(); ++i) {
392        const QDBusIntrospection::Argument &arg = inputArgs.at(i);
393        QString name = arg.name;
394        if (name.isEmpty())
395            name = QString( QLatin1String("in%1") ).arg(i);
396        while (retval.contains(name))
397            name += QLatin1String("_");
398        retval << name;
399    }
400    for (int i = 0; i < outputArgs.count(); ++i) {
401        const QDBusIntrospection::Argument &arg = outputArgs.at(i);
402        QString name = arg.name;
403        if (name.isEmpty())
404            name = QString( QLatin1String("out%1") ).arg(i);
405        while (retval.contains(name))
406            name += QLatin1String("_");
407        retval << name;
408    }
409    return retval;
410}
411
412static void writeArgList(QTextStream &ts, const QStringList &argNames,
413                         const QDBusIntrospection::Annotations &annotations,
414                         const QDBusIntrospection::Arguments &inputArgs,
415                         const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments())
416{
417    // input args:
418    bool first = true;
419    int argPos = 0;
420    for (int i = 0; i < inputArgs.count(); ++i) {
421        const QDBusIntrospection::Argument &arg = inputArgs.at(i);
422        QString type = constRefArg(qtTypeName(arg.type, annotations, i, "In"));
423
424        if (!first)
425            ts << ", ";
426        ts << type << argNames.at(argPos++);
427        first = false;
428    }
429
430    argPos++;
431
432    // output args
433    // yes, starting from 1
434    for (int i = 1; i < outputArgs.count(); ++i) {
435        const QDBusIntrospection::Argument &arg = outputArgs.at(i);
436        QString name = arg.name;
437
438        if (!first)
439            ts << ", ";
440        ts << nonConstRefArg(qtTypeName(arg.type, annotations, i, "Out"))
441           << argNames.at(argPos++);
442        first = false;
443    }
444}
445
446static QString propertyGetter(const QDBusIntrospection::Property &property)
447{
448    QString getter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertyGetter"));
449    if (getter.isEmpty()) {
450        getter =  property.name;
451        getter[0] = getter[0].toLower();
452    }
453    return getter;
454}
455
456static QString propertySetter(const QDBusIntrospection::Property &property)
457{
458    QString setter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertySetter"));
459    if (setter.isEmpty()) {
460        setter = QLatin1String("set") + property.name;
461        setter[3] = setter[3].toUpper();
462    }
463    return setter;
464}
465
466static QString stringify(const QString &data)
467{
468    QString retval;
469    int i;
470    for (i = 0; i < data.length(); ++i) {
471        retval += QLatin1Char('\"');
472        for ( ; i < data.length() && data[i] != QLatin1Char('\n'); ++i)
473            if (data[i] == QLatin1Char('\"'))
474                retval += QLatin1String("\\\"");
475            else
476                retval += data[i];
477        retval += QLatin1String("\\n\"\n");
478    }
479    return retval;
480}
481
482static void openFile(const QString &fileName, QFile &file)
483{
484    if (fileName.isEmpty())
485        return;
486
487    bool isOk = false;
488    if (fileName == QLatin1String("-")) {
489        isOk = file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
490    } else {
491        file.setFileName(fileName);
492        isOk = file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
493    }
494
495    if (!isOk)
496        fprintf(stderr, "Unable to open '%s': %s\n", qPrintable(fileName),
497                qPrintable(file.errorString()));
498}
499
500static void writeProxy(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
501{
502    // open the file
503    QString headerName = header(filename);
504    QByteArray headerData;
505    QTextStream hs(&headerData);
506
507    QString cppName = cpp(filename);
508    QByteArray cppData;
509    QTextStream cs(&cppData);
510
511    // write the header:
512    writeHeader(hs, true);
513    if (cppName != headerName)
514        writeHeader(cs, false);
515
516    // include guards:
517    QString includeGuard;
518    if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
519        includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
520        int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
521        if (pos != -1)
522            includeGuard = includeGuard.mid(pos + 1);
523    } else {
524        includeGuard = QLatin1String("QDBUSXML2CPP_PROXY");
525    }
526    includeGuard = QString(QLatin1String("%1_%2"))
527                   .arg(includeGuard)
528                   .arg(QDateTime::currentDateTime().toTime_t());
529    hs << "#ifndef " << includeGuard << endl
530       << "#define " << includeGuard << endl
531       << endl;
532
533    // include our stuff:
534    hs << "#include <QtCore/QObject>" << endl
535       << includeList
536       << "#include <QtDBus/QtDBus>" << endl;
537
538    foreach (QString include, includes) {
539        hs << "#include \"" << include << "\"" << endl;
540        if (headerName.isEmpty())
541            cs << "#include \"" << include << "\"" << endl;
542    }
543
544    hs << endl;
545
546    if (cppName != headerName) {
547        if (!headerName.isEmpty() && headerName != QLatin1String("-"))
548            cs << "#include \"" << headerName << "\"" << endl << endl;
549    }
550
551    foreach (const QDBusIntrospection::Interface *interface, interfaces) {
552        QString className = classNameForInterface(interface->name, Proxy);
553
554        // comment:
555        hs << "/*" << endl
556           << " * Proxy class for interface " << interface->name << endl
557           << " */" << endl;
558        cs << "/*" << endl
559           << " * Implementation of interface class " << className << endl
560           << " */" << endl
561           << endl;
562
563        // class header:
564        hs << "class " << className << ": public QDBusAbstractInterface" << endl
565           << "{" << endl
566           << "    Q_OBJECT" << endl;
567
568        // the interface name
569        hs << "public:" << endl
570           << "    static inline const char *staticInterfaceName()" << endl
571           << "    { return \"" << interface->name << "\"; }" << endl
572           << endl;
573
574        // constructors/destructors:
575        hs << "public:" << endl
576           << "    " << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);" << endl
577           << endl
578           << "    ~" << className << "();" << endl
579           << endl;
580        cs << className << "::" << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)" << endl
581           << "    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)" << endl
582           << "{" << endl
583           << "}" << endl
584           << endl
585           << className << "::~" << className << "()" << endl
586           << "{" << endl
587           << "}" << endl
588           << endl;
589
590        // properties:
591        foreach (const QDBusIntrospection::Property &property, interface->properties) {
592            QByteArray type = qtTypeName(property.type, property.annotations);
593            QString templateType = templateArg(type);
594            QString constRefType = constRefArg(type);
595            QString getter = propertyGetter(property);
596            QString setter = propertySetter(property);
597
598            hs << "    Q_PROPERTY(" << type << " " << property.name;
599
600            // getter:
601            if (property.access != QDBusIntrospection::Property::Write)
602                // it's readble
603                hs << " READ " << getter;
604
605            // setter
606            if (property.access != QDBusIntrospection::Property::Read)
607                // it's writeable
608                hs << " WRITE " << setter;
609
610            hs << ")" << endl;
611
612            // getter:
613            if (property.access != QDBusIntrospection::Property::Write) {
614                hs << "    inline " << type << " " << getter << "() const" << endl;
615                if (type != "QVariant")
616                    hs << "    { return qvariant_cast< " << type << " >(internalPropGet(\""
617                       << property.name << "\")); }" << endl;
618                else
619                    hs << "    { return internalPropGet(\"" << property.name << "\"); }" << endl;
620            }
621
622            // setter:
623            if (property.access != QDBusIntrospection::Property::Read) {
624                hs << "    inline void " << setter << "(" << constRefArg(type) << "value)" << endl
625                   << "    { internalPropSet(\"" << property.name
626                   << "\", qVariantFromValue(value)); }" << endl;
627            }
628
629            hs << endl;
630        }
631
632        // methods:
633        hs << "public Q_SLOTS: // METHODS" << endl;
634        foreach (const QDBusIntrospection::Method &method, interface->methods) {
635            bool isDeprecated = method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true");
636            bool isNoReply =
637                method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
638            if (isNoReply && !method.outputArgs.isEmpty()) {
639                fprintf(stderr, "warning: method %s in interface %s is marked 'no-reply' but has output arguments.\n",
640                        qPrintable(method.name), qPrintable(interface->name));
641                continue;
642            }
643
644            hs << "    inline "
645               << (isDeprecated ? "Q_DECL_DEPRECATED " : "");
646
647            if (isNoReply) {
648                hs << "Q_NOREPLY void ";
649            } else {
650                hs << "QDBusPendingReply<";
651                for (int i = 0; i < method.outputArgs.count(); ++i)
652                    hs << (i > 0 ? ", " : "")
653                       << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"));
654                hs << "> ";
655            }
656
657            hs << method.name << "(";
658
659            QStringList argNames = makeArgNames(method.inputArgs);
660            writeArgList(hs, argNames, method.annotations, method.inputArgs);
661
662            hs << ")" << endl
663               << "    {" << endl
664               << "        QList<QVariant> argumentList;" << endl;
665
666            if (!method.inputArgs.isEmpty()) {
667                hs << "        argumentList";
668                for (int argPos = 0; argPos < method.inputArgs.count(); ++argPos)
669                    hs << " << qVariantFromValue(" << argNames.at(argPos) << ')';
670                hs << ";" << endl;
671            }
672
673            if (isNoReply)
674                hs << "        callWithArgumentList(QDBus::NoBlock, "
675                   <<  "QLatin1String(\"" << method.name << "\"), argumentList);" << endl;
676            else
677                hs << "        return asyncCallWithArgumentList(QLatin1String(\""
678                   << method.name << "\"), argumentList);" << endl;
679
680            // close the function:
681            hs << "    }" << endl;
682
683            if (method.outputArgs.count() > 1) {
684                // generate the old-form QDBusReply methods with multiple incoming parameters
685                hs << "    inline "
686                   << (isDeprecated ? "Q_DECL_DEPRECATED " : "")
687                   << "QDBusReply<"
688                   << templateArg(qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out")) << "> ";
689                hs << method.name << "(";
690
691                QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
692                writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
693
694                hs << ")" << endl
695                   << "    {" << endl
696                   << "        QList<QVariant> argumentList;" << endl;
697
698                int argPos = 0;
699                if (!method.inputArgs.isEmpty()) {
700                    hs << "        argumentList";
701                    for (argPos = 0; argPos < method.inputArgs.count(); ++argPos)
702                        hs << " << qVariantFromValue(" << argNames.at(argPos) << ')';
703                    hs << ";" << endl;
704                }
705
706                hs << "        QDBusMessage reply = callWithArgumentList(QDBus::Block, "
707                   <<  "QLatin1String(\"" << method.name << "\"), argumentList);" << endl;
708
709                argPos++;
710                hs << "        if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == "
711                   << method.outputArgs.count() << ") {" << endl;
712
713                // yes, starting from 1
714                for (int i = 1; i < method.outputArgs.count(); ++i)
715                    hs << "            " << argNames.at(argPos++) << " = qdbus_cast<"
716                       << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"))
717                       << ">(reply.arguments().at(" << i << "));" << endl;
718                hs << "        }" << endl
719                   << "        return reply;" << endl
720                   << "    }" << endl;
721            }
722
723            hs << endl;
724        }
725
726        hs << "Q_SIGNALS: // SIGNALS" << endl;
727        foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
728            hs << "    ";
729            if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
730                QLatin1String("true"))
731                hs << "Q_DECL_DEPRECATED ";
732
733            hs << "void " << signal.name << "(";
734
735            QStringList argNames = makeArgNames(signal.outputArgs);
736            writeArgList(hs, argNames, signal.annotations, signal.outputArgs);
737
738            hs << ");" << endl; // finished for header
739        }
740
741        // close the class:
742        hs << "};" << endl
743           << endl;
744    }
745
746    if (!skipNamespaces) {
747        QStringList last;
748        QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin();
749        do
750        {
751            QStringList current;
752            QString name;
753            if (it != interfaces.constEnd()) {
754                current = it->constData()->name.split(QLatin1Char('.'));
755                name = current.takeLast();
756            }
757
758            int i = 0;
759            while (i < current.count() && i < last.count() && current.at(i) == last.at(i))
760                ++i;
761
762            // i parts matched
763            // close last.arguments().count() - i namespaces:
764            for (int j = i; j < last.count(); ++j)
765                hs << QString((last.count() - j - 1 + i) * 2, QLatin1Char(' ')) << "}" << endl;
766
767            // open current.arguments().count() - i namespaces
768            for (int j = i; j < current.count(); ++j)
769                hs << QString(j * 2, QLatin1Char(' ')) << "namespace " << current.at(j) << " {" << endl;
770
771            // add this class:
772            if (!name.isEmpty()) {
773                hs << QString(current.count() * 2, QLatin1Char(' '))
774                   << "typedef ::" << classNameForInterface(it->constData()->name, Proxy)
775                   << " " << name << ";" << endl;
776            }
777
778            if (it == interfaces.constEnd())
779                break;
780            ++it;
781            last = current;
782        } while (true);
783    }
784
785    // close the include guard
786    hs << "#endif" << endl;
787
788    QString mocName = moc(filename);
789    if (includeMocs && !mocName.isEmpty())
790        cs << endl
791           << "#include \"" << mocName << "\"" << endl;
792
793    cs.flush();
794    hs.flush();
795
796    QFile file;
797    openFile(headerName, file);
798    file.write(headerData);
799
800    if (headerName == cppName) {
801        file.write(cppData);
802    } else {
803        QFile cppFile;
804        openFile(cppName, cppFile);
805        cppFile.write(cppData);
806    }
807}
808
809static void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
810{
811    // open the file
812    QString headerName = header(filename);
813    QByteArray headerData;
814    QTextStream hs(&headerData);
815
816    QString cppName = cpp(filename);
817    QByteArray cppData;
818    QTextStream cs(&cppData);
819
820    // write the headers
821    writeHeader(hs, false);
822    if (cppName != headerName)
823        writeHeader(cs, true);
824
825    // include guards:
826    QString includeGuard;
827    if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
828        includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
829        int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
830        if (pos != -1)
831            includeGuard = includeGuard.mid(pos + 1);
832    } else {
833        includeGuard = QLatin1String("QDBUSXML2CPP_ADAPTOR");
834    }
835    includeGuard = QString(QLatin1String("%1_%2"))
836                   .arg(includeGuard)
837                   .arg(QDateTime::currentDateTime().toTime_t());
838    hs << "#ifndef " << includeGuard << endl
839       << "#define " << includeGuard << endl
840       << endl;
841
842    // include our stuff:
843    hs << "#include <QtCore/QObject>" << endl;
844    if (cppName == headerName)
845        hs << "#include <QtCore/QMetaObject>" << endl
846           << "#include <QtCore/QVariant>" << endl;
847    hs << "#include <QtDBus/QtDBus>" << endl;
848
849    foreach (QString include, includes) {
850        hs << "#include \"" << include << "\"" << endl;
851        if (headerName.isEmpty())
852            cs << "#include \"" << include << "\"" << endl;
853    }
854
855    if (cppName != headerName) {
856        if (!headerName.isEmpty() && headerName != QLatin1String("-"))
857            cs << "#include \"" << headerName << "\"" << endl;
858
859        cs << "#include <QtCore/QMetaObject>" << endl
860           << includeList
861           << endl;
862        hs << forwardDeclarations;
863    } else {
864        hs << includeList;
865    }
866
867    hs << endl;
868
869    QString parent = parentClassName;
870    if (parentClassName.isEmpty())
871        parent = QLatin1String("QObject");
872
873    foreach (const QDBusIntrospection::Interface *interface, interfaces) {
874        QString className = classNameForInterface(interface->name, Adaptor);
875
876        // comment:
877        hs << "/*" << endl
878           << " * Adaptor class for interface " << interface->name << endl
879           << " */" << endl;
880        cs << "/*" << endl
881           << " * Implementation of adaptor class " << className << endl
882           << " */" << endl
883           << endl;
884
885        // class header:
886        hs << "class " << className << ": public QDBusAbstractAdaptor" << endl
887           << "{" << endl
888           << "    Q_OBJECT" << endl
889           << "    Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << endl
890           << "    Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << endl
891           << stringify(interface->introspection)
892           << "        \"\")" << endl
893           << "public:" << endl
894           << "    " << className << "(" << parent << " *parent);" << endl
895           << "    virtual ~" << className << "();" << endl
896           << endl;
897
898        if (!parentClassName.isEmpty())
899            hs << "    inline " << parent << " *parent() const" << endl
900               << "    { return static_cast<" << parent << " *>(QObject::parent()); }" << endl
901               << endl;
902
903        // constructor/destructor
904        cs << className << "::" << className << "(" << parent << " *parent)" << endl
905           << "    : QDBusAbstractAdaptor(parent)" << endl
906           << "{" << endl
907           << "    // constructor" << endl
908           << "    setAutoRelaySignals(true);" << endl
909           << "}" << endl
910           << endl
911           << className << "::~" << className << "()" << endl
912           << "{" << endl
913           << "    // destructor" << endl
914           << "}" << endl
915           << endl;
916
917        hs << "public: // PROPERTIES" << endl;
918        foreach (const QDBusIntrospection::Property &property, interface->properties) {
919            QByteArray type = qtTypeName(property.type, property.annotations);
920            QString constRefType = constRefArg(type);
921            QString getter = propertyGetter(property);
922            QString setter = propertySetter(property);
923
924            hs << "    Q_PROPERTY(" << type << " " << property.name;
925            if (property.access != QDBusIntrospection::Property::Write)
926                hs << " READ " << getter;
927            if (property.access != QDBusIntrospection::Property::Read)
928                hs << " WRITE " << setter;
929            hs << ")" << endl;
930
931            // getter:
932            if (property.access != QDBusIntrospection::Property::Write) {
933                hs << "    " << type << " " << getter << "() const;" << endl;
934                cs << type << " "
935                   << className << "::" << getter << "() const" << endl
936                   << "{" << endl
937                   << "    // get the value of property " << property.name << endl
938                   << "    return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));" << endl
939                   << "}" << endl
940                   << endl;
941            }
942
943            // setter
944            if (property.access != QDBusIntrospection::Property::Read) {
945                hs << "    void " << setter << "(" << constRefType << "value);" << endl;
946                cs << "void " << className << "::" << setter << "(" << constRefType << "value)" << endl
947                   << "{" << endl
948                   << "    // set the value of property " << property.name << endl
949                   << "    parent()->setProperty(\"" << property.name << "\", qVariantFromValue(value";
950                if (constRefType.contains(QLatin1String("QDBusVariant")))
951                    cs << ".variant()";
952                cs << "));" << endl
953                   << "}" << endl
954                   << endl;
955            }
956
957            hs << endl;
958        }
959
960        hs << "public Q_SLOTS: // METHODS" << endl;
961        foreach (const QDBusIntrospection::Method &method, interface->methods) {
962            bool isNoReply =
963                method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
964            if (isNoReply && !method.outputArgs.isEmpty()) {
965                fprintf(stderr, "warning: method %s in interface %s is marked 'no-reply' but has output arguments.\n",
966                        qPrintable(method.name), qPrintable(interface->name));
967                continue;
968            }
969
970            hs << "    ";
971            if (method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
972                QLatin1String("true"))
973                hs << "Q_DECL_DEPRECATED ";
974
975            QByteArray returnType;
976            if (isNoReply) {
977                hs << "Q_NOREPLY void ";
978                cs << "void ";
979            } else if (method.outputArgs.isEmpty()) {
980                hs << "void ";
981                cs << "void ";
982            } else {
983                returnType = qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out");
984                hs << returnType << " ";
985                cs << returnType << " ";
986            }
987
988            QString name = method.name;
989            hs << name << "(";
990            cs << className << "::" << name << "(";
991
992            QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
993            writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
994            writeArgList(cs, argNames, method.annotations, method.inputArgs, method.outputArgs);
995
996            hs << ");" << endl; // finished for header
997            cs << ")" << endl
998               << "{" << endl
999               << "    // handle method call " << interface->name << "." << method.name << endl;
1000
1001            // make the call
1002            bool usingInvokeMethod = false;
1003            if (parentClassName.isEmpty() && method.inputArgs.count() <= 10
1004                && method.outputArgs.count() <= 1)
1005                usingInvokeMethod = true;
1006
1007            if (usingInvokeMethod) {
1008                // we are using QMetaObject::invokeMethod
1009                if (!returnType.isEmpty())
1010                    cs << "    " << returnType << " " << argNames.at(method.inputArgs.count())
1011                       << ";" << endl;
1012
1013                static const char invoke[] = "    QMetaObject::invokeMethod(parent(), \"";
1014                cs << invoke << name << "\"";
1015
1016                if (!method.outputArgs.isEmpty())
1017                    cs << ", Q_RETURN_ARG("
1018                       << qtTypeName(method.outputArgs.at(0).type, method.annotations,
1019                                     0, "Out")
1020                       << ", "
1021                       << argNames.at(method.inputArgs.count())
1022                       << ")";
1023
1024                for (int i = 0; i < method.inputArgs.count(); ++i)
1025                    cs << ", Q_ARG("
1026                       << qtTypeName(method.inputArgs.at(i).type, method.annotations,
1027                                     i, "In")
1028                       << ", "
1029                       << argNames.at(i)
1030                       << ")";
1031
1032                cs << ");" << endl;
1033
1034                if (!returnType.isEmpty())
1035                    cs << "    return " << argNames.at(method.inputArgs.count()) << ";" << endl;
1036            } else {
1037                if (parentClassName.isEmpty())
1038                    cs << "    //";
1039                else
1040                    cs << "    ";
1041
1042                if (!method.outputArgs.isEmpty())
1043                    cs << "return ";
1044
1045                if (parentClassName.isEmpty())
1046                    cs << "static_cast<YourObjectType *>(parent())->";
1047                else
1048                    cs << "parent()->";
1049                cs << name << "(";
1050
1051                int argPos = 0;
1052                bool first = true;
1053                for (int i = 0; i < method.inputArgs.count(); ++i) {
1054                    cs << (first ? "" : ", ") << argNames.at(argPos++);
1055                    first = false;
1056                }
1057                ++argPos;           // skip retval, if any
1058                for (int i = 1; i < method.outputArgs.count(); ++i) {
1059                    cs << (first ? "" : ", ") << argNames.at(argPos++);
1060                    first = false;
1061                }
1062
1063                cs << ");" << endl;
1064            }
1065            cs << "}" << endl
1066               << endl;
1067        }
1068
1069        hs << "Q_SIGNALS: // SIGNALS" << endl;
1070        foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
1071            hs << "    ";
1072            if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
1073                QLatin1String("true"))
1074                hs << "Q_DECL_DEPRECATED ";
1075
1076            hs << "void " << signal.name << "(";
1077
1078            QStringList argNames = makeArgNames(signal.outputArgs);
1079            writeArgList(hs, argNames, signal.annotations, signal.outputArgs);
1080
1081            hs << ");" << endl; // finished for header
1082        }
1083
1084        // close the class:
1085        hs << "};" << endl
1086           << endl;
1087    }
1088
1089    // close the include guard
1090    hs << "#endif" << endl;
1091
1092    QString mocName = moc(filename);
1093    if (includeMocs && !mocName.isEmpty())
1094        cs << endl
1095           << "#include \"" << mocName << "\"" << endl;
1096
1097    cs.flush();
1098    hs.flush();
1099
1100    QFile file;
1101    openFile(headerName, file);
1102    file.write(headerData);
1103
1104    if (headerName == cppName) {
1105        file.write(cppData);
1106    } else {
1107        QFile cppFile;
1108        openFile(cppName, cppFile);
1109        cppFile.write(cppData);
1110    }
1111}
1112
1113int main(int argc, char **argv)
1114{
1115    QCoreApplication app(argc, argv);
1116    parseCmdLine(app.arguments());
1117
1118    QDBusIntrospection::Interfaces interfaces = readInput();
1119    cleanInterfaces(interfaces);
1120
1121    if (!proxyFile.isEmpty() || adaptorFile.isEmpty())
1122        writeProxy(proxyFile, interfaces);
1123
1124    if (!adaptorFile.isEmpty())
1125        writeAdaptor(adaptorFile, interfaces);
1126
1127    return 0;
1128}
1129
1130/*!
1131    \page qdbusxml2cpp.html
1132    \title QtDBus XML compiler (qdbusxml2cpp)
1133    \keyword qdbusxml2cpp
1134
1135    The QtDBus XML compiler is a tool that can be used to parse interface descriptions and produce
1136    static code representing those interfaces, which can then be used to make calls to remote
1137    objects or implement said interfaces.
1138
1139    \c qdbusxml2dcpp has two modes of operation, that correspond to the two possible outputs it can
1140    produce: the interface (proxy) class or the adaptor class. The latter consists of both a C++
1141    header and a source file, which are meant to be edited and adapted to your needs.
1142
1143    The \c qdbusxml2dcpp tool is not meant to be run every time you compile your
1144    application. Instead, it's meant to be used when developing the code or when the interface
1145    changes.
1146
1147    The adaptor classes generated by \c qdbusxml2cpp are just a skeleton that must be completed. It
1148    generates, by default, calls to slots with the same name on the object the adaptor is attached
1149    to. However, you may modify those slots or the property accessor functions to suit your needs.
1150*/
Note: See TracBrowser for help on using the repository browser.