* qt/: Update to Subversion r548032.
[platform/upstream/dbus.git] / qt / tools / dbusidl2cpp.cpp
1 /* -*- C++ -*-
2  *
3  * Copyright (C) 2006 Trolltech AS. All rights reserved.
4  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include <sys/types.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #include <QtCore/qbytearray.h>
28 #include <QtCore/qdatetime.h>
29 #include <QtCore/qfile.h>
30 #include <QtCore/qstring.h>
31 #include <QtCore/qstringlist.h>
32 #include <QtCore/qtextstream.h>
33 #include <QtCore/qset.h>
34
35 #include <dbus/qdbus.h>
36 #include "../src/qdbusmetaobject_p.h"
37 #include "../src/qdbusintrospection_p.h"
38
39 #define PROGRAMNAME     "dbusidl2cpp"
40 #define PROGRAMVERSION  "0.4"
41 #define PROGRAMCOPYRIGHT "Copyright (C) 2006 Trolltech AS. All rights reserved."
42
43 #define ANNOTATION_NO_WAIT      "org.freedesktop.DBus.Method.NoReply"
44
45 static const char cmdlineOptions[] = "a:c:hmNp:vV";
46 static const char *globalClassName;
47 static const char *proxyFile;
48 static const char *adaptorFile;
49 static const char *inputFile;
50 static bool skipNamespaces;
51 static bool verbose;
52 static bool includeMocs;
53 static QStringList wantedInterfaces;
54
55 static const char help[] =
56     "Usage: " PROGRAMNAME " [options...] [idl-or-xml-file] [interfaces...]\n"
57     "Produces the C++ code to implement the interfaces defined in the input file.\n"
58     "If no options are given, the code is written to the standard output.\n"
59     "\n"
60     "Options:\n"
61     "  -a <filename>    Write the adaptor code to <filename>\n"
62     "  -c <classname>   Use <classname> as the class name for the generated classes\n"
63     "  -h               Show this information\n"
64     "  -m               Generate #include \"filename.moc\" statements in the .cpp files\n"
65     "  -N               Don't use namespaces\n"
66     "  -p <filename>    Write the proxy code to <filename>\n"
67     "  -v               Be verbose.\n"
68     "  -V               Show the program version and quit.\n"
69     "\n"
70     "If the file name given to the options -a and -p does not end in .cpp or .h, the\n"
71     "program will automatically append the suffixes and produce both files.\n";
72
73 static const char includeList[] =
74     "#include <QtCore/QByteArray>\n"
75     "#include <QtCore/QList>\n"
76     "#include <QtCore/QMap>\n"
77     "#include <QtCore/QString>\n"
78     "#include <QtCore/QStringList>\n"
79     "#include <QtCore/QVariant>\n";
80
81 static const char forwardDeclarations[] =
82     "class QByteArray;\n"
83     "template<class T> class QList;\n"
84     "template<class Key, class Value> class QMap;\n"
85     "class QString;\n"
86     "class QStringList;\n"
87     "class QVariant;\n";
88
89 static void showHelp()
90 {
91     printf("%s", help);
92     exit(0);
93 }
94
95 static void showVersion()
96 {
97     printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
98     printf("D-Bus binding tool for Qt\n");
99     exit(0);
100 }
101
102 static void parseCmdLine(int argc, char **argv)
103 {
104     int c;
105     opterr = true;
106     while ((c = getopt(argc, argv, cmdlineOptions)) != -1)
107         switch (c)
108         {
109         case 'a':
110             adaptorFile = optarg;
111             break;
112
113         case 'c':
114             globalClassName = optarg;
115             break;
116             
117         case 'v':
118             verbose = true;
119             break;
120
121         case 'm':
122             includeMocs = true;
123             break;
124
125         case 'N':
126             skipNamespaces = true;
127             break;
128             
129         case 'h':
130             showHelp();
131             break;
132
133         case 'V':
134             showVersion();
135             break;
136
137         case 'p':
138             proxyFile = optarg;
139             break;
140
141         case '?':
142             exit(1);
143         default:
144             abort();
145         }
146
147     if (optind != argc)
148         inputFile = argv[optind++];
149
150     while (optind != argc)
151         wantedInterfaces << QString::fromLocal8Bit(argv[optind++]);
152 }
153
154 static QDBusIntrospection::Interfaces readInput()
155 {
156     QFile input(QFile::decodeName(inputFile));
157     if (inputFile && QLatin1String("-") != inputFile) 
158         input.open(QIODevice::ReadOnly);
159     else
160         input.open(stdin, QIODevice::ReadOnly);
161
162     QByteArray data = input.readAll();
163
164     // check if the input is already XML
165     data = data.trimmed();
166     if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") ||
167         data.startsWith("<node") || data.startsWith("<interface"))
168         // already XML
169         return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data));
170
171     fprintf(stderr, "Cannot process input. Stop.\n");
172     exit(1);
173 }
174
175 static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
176 {
177     if (!wantedInterfaces.isEmpty()) {
178         QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();
179         while (it != interfaces.end())
180             if (!wantedInterfaces.contains(it.key()))
181                 it = interfaces.erase(it);
182             else
183                 ++it;
184     }
185 }        
186
187 // produce a header name from the file name
188 static QString header(const char *name)
189 {
190     if (!name || (name[0] == '-' && name[1] == '\0'))
191         return QString();
192
193     QString retval = QFile::decodeName(name);
194     if (!retval.endsWith(".h") && !retval.endsWith(".cpp") && !retval.endsWith(".cc"))
195         retval.append(".h");
196
197     return retval;
198 }
199
200 // produce a cpp name from the file name
201 static QString cpp(const char *name)
202 {
203     if (!name || (name[0] == '-' && name[1] == '\0'))
204         return QString();
205
206     QString retval = QFile::decodeName(name);
207     if (!retval.endsWith(".h") && !retval.endsWith(".cpp") && !retval.endsWith(".cc"))
208         retval.append(".cpp");
209
210     return retval;
211 }
212
213 static QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost)
214 {
215     ts << "/*" << endl
216        << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << endl
217        << " * when processing input file " << (inputFile ? inputFile : "<stdin>") << endl
218        << " *" << endl
219        << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << endl
220        << " *" << endl
221        << " * This is an auto-generated file." << endl;
222
223     if (changesWillBeLost)
224         ts << " * Do not edit! All changes made to it will be lost." << endl;
225
226     ts << " */" << endl
227        << endl;
228
229     return ts;
230 }
231
232 enum ClassType { Proxy, Adaptor };
233 static QString classNameForInterface(const QString &interface, ClassType classType)
234 {
235     if (globalClassName)
236         return QLatin1String(globalClassName);
237
238     QStringList parts = interface.split('.');
239
240     QString retval;
241     if (classType == Proxy)
242         foreach (QString part, parts) {
243             part[0] = part[0].toUpper();
244             retval += part;
245         }
246     else {
247         retval = parts.last();
248         retval[0] = retval[0].toUpper();
249     }
250
251     if (classType == Proxy)
252         retval += "Interface";
253     else
254         retval += "Adaptor";
255
256     return retval;
257 }
258
259 static QByteArray qtTypeName(const QString &signature)
260 {
261     QVariant::Type type = QDBusUtil::signatureToType(signature);
262     if (type == QVariant::Invalid)
263         qFatal("Got unknown type `%s'", qPrintable(signature));
264     
265     return QVariant::typeToName(type);
266 }
267
268 static QString nonConstRefArg(const QByteArray &arg)
269 {
270     return QLatin1String(arg + " &");
271 }
272
273 static QString templateArg(const QByteArray &arg)
274 {
275     if (!arg.endsWith('>'))
276         return QLatin1String(arg);
277
278     return QLatin1String(arg + ' ');
279 }
280
281 static QString constRefArg(const QByteArray &arg)
282 {
283     if (!arg.startsWith('Q'))
284         return QLatin1String(arg + ' ');
285     else
286         return QString("const %1 &").arg( QLatin1String(arg) );
287 }
288
289 static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs,
290                                 const QDBusIntrospection::Arguments &outputArgs =
291                                 QDBusIntrospection::Arguments())
292 {
293     QStringList retval;
294     for (int i = 0; i < inputArgs.count(); ++i) {
295         const QDBusIntrospection::Argument &arg = inputArgs.at(i);
296         QString name = arg.name;
297         if (name.isEmpty())
298             name = QString("in%1").arg(i);
299         while (retval.contains(name))
300             name += "_";
301         retval << name;
302     }
303     for (int i = 0; i < outputArgs.count(); ++i) {
304         const QDBusIntrospection::Argument &arg = outputArgs.at(i);
305         QString name = arg.name;
306         if (name.isEmpty())
307             name = QString("out%1").arg(i);
308         while (retval.contains(name))
309             name += "_";
310         retval << name;
311     }
312     return retval;
313 }
314
315 static void writeArgList(QTextStream &ts, const QStringList &argNames,
316                          const QDBusIntrospection::Arguments &inputArgs,
317                          const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments())
318 {
319     // input args:
320     bool first = true;
321     int argPos = 0;
322     for (int i = 0; i < inputArgs.count(); ++i) {
323         const QDBusIntrospection::Argument &arg = inputArgs.at(i);
324         QString type = constRefArg(qtTypeName(arg.type));
325         
326         if (!first)
327             ts << ", ";
328         ts << type << argNames.at(argPos++);
329         first = false;
330     }
331
332     argPos++;
333     
334     // output args
335     // yes, starting from 1
336     for (int i = 1; i < outputArgs.count(); ++i) {
337         const QDBusIntrospection::Argument &arg = outputArgs.at(i);
338         QString name = arg.name;
339
340         if (!first)
341             ts << ", ";
342         ts << nonConstRefArg(qtTypeName(arg.type)) << argNames.at(argPos++);
343         first = false;
344     }
345 }
346
347 static QString propertyGetter(const QDBusIntrospection::Property &property)
348 {    
349     QString getter = property.annotations.value("com.trolltech.QtDBus.propertyGetter");
350     if (getter.isEmpty()) {
351         getter =  property.name;
352         getter[0] = getter[0].toLower();
353     }
354     return getter;
355 }
356
357 static QString propertySetter(const QDBusIntrospection::Property &property)
358 {
359     QString setter = property.annotations.value("com.trolltech.QtDBus.propertySetter");
360     if (setter.isEmpty()) {
361         setter = "set" + property.name;
362         setter[3] = setter[3].toUpper();
363     }
364     return setter;
365 }
366
367 static QString stringify(const QString &data)
368 {
369     QString retval;
370     int i;
371     for (i = 0; i < data.length(); ++i) {
372         retval += '\"';
373         for ( ; i < data.length() && data[i] != QChar('\n'); ++i)
374             if (data[i] == '\"')
375                 retval += "\\\"";
376             else
377                 retval += data[i];
378         retval += "\\n\"\n";
379     }
380     return retval;
381 }
382
383 static void writeProxy(const char *proxyFile, const QDBusIntrospection::Interfaces &interfaces)
384 {
385     // open the file
386     QString headerName = header(proxyFile);
387     QFile file(headerName);
388     if (!headerName.isEmpty())
389         file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
390     else
391         file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
392     QTextStream hs(&file);
393
394     QString cppName = cpp(proxyFile);
395     QByteArray cppData;
396     QTextStream cs(&cppData);
397
398     // write the header:
399     writeHeader(hs, true);
400
401     // include guards:
402     QString includeGuard;
403     if (!headerName.isEmpty()) {
404         includeGuard = headerName.toUpper().replace(QChar('.'), QChar('_'));
405         int pos = includeGuard.lastIndexOf('/');
406         if (pos != -1)
407             includeGuard = includeGuard.mid(pos + 1);
408     } else {
409         includeGuard = QString("QDBUSIDL2CPP_PROXY");
410     }
411     includeGuard = QString("%1_%2%3")
412                    .arg(includeGuard)
413                    .arg(getpid())
414                    .arg(QDateTime::currentDateTime().toTime_t());
415     hs << "#ifndef " << includeGuard << endl
416        << "#define " << includeGuard << endl
417        << endl;
418
419     // include our stuff:
420     hs << "#include <QtCore/QObject>" << endl
421        << includeList
422        << "#include <dbus/qdbus.h>" << endl
423        << endl;
424
425     if (cppName != headerName) {
426         writeHeader(cs, false);        
427         cs << "#include \"" << headerName << "\"" << endl
428            << endl;
429     }
430     
431     foreach (const QDBusIntrospection::Interface *interface, interfaces) {
432         QString className = classNameForInterface(interface->name, Proxy);
433
434         // comment:
435         hs << "/*" << endl
436            << " * Proxy class for interface " << interface->name << endl
437            << " */" << endl;
438         cs << "/*" << endl
439            << " * Implementation of interface class " << className << endl
440            << " */" << endl
441            << endl;
442
443         // class header:
444         hs << "class " << className << ": public QDBusAbstractInterface" << endl
445            << "{" << endl
446            << "    Q_OBJECT" << endl;
447         
448         // the interface name
449         hs << "public:" << endl
450            << "    static inline const char *staticInterfaceName()" << endl
451            << "    { return \"" << interface->name << "\"; }" << endl
452            << endl;
453         
454         // constructors/destructors:
455         hs << "public:" << endl
456            << "    explicit " << className << "(QDBusAbstractInterfacePrivate *p);" << endl
457            << endl
458            << "    ~" << className << "();" << endl
459            << endl;
460         cs << className << "::" << className << "(QDBusAbstractInterfacePrivate *p)" << endl
461            << "    : QDBusAbstractInterface(p)" << endl
462            << "{" << endl
463            << "}" << endl
464            << endl
465            << className << "::~" << className << "()" << endl
466            << "{" << endl
467            << "}" << endl
468            << endl;
469
470         // properties:
471         foreach (const QDBusIntrospection::Property &property, interface->properties) {
472             QByteArray type = qtTypeName(property.type);
473             QString templateType = templateArg(type);
474             QString constRefType = constRefArg(type);
475             QString getter = propertyGetter(property);
476             QString setter = propertySetter(property);
477
478             hs << "    Q_PROPERTY(" << type << " " << property.name;
479
480             // getter:
481             if (property.access != QDBusIntrospection::Property::Write)
482                 // it's readble
483                 hs << " READ " << getter;
484
485             // setter
486             if (property.access != QDBusIntrospection::Property::Read)
487                 // it's writeable
488                 hs << " WRITE " << setter;
489
490             hs << ")" << endl;
491
492             // getter:
493             if (property.access != QDBusIntrospection::Property::Write) {
494                 hs << "    inline " << type << " " << getter << "() const" << endl;
495                 if (type != "QVariant")
496                     hs << "    { return qvariant_cast< " << type << " >(internalPropGet(\""
497                        << property.name << "\")); }" << endl;
498                 else
499                     hs << "    { return internalPropGet(\"" << property.name << "\"); }" << endl;
500             }
501
502             // setter:
503             if (property.access != QDBusIntrospection::Property::Read) {
504                 hs << "    inline void " << setter << "(" << constRefArg(type) << "value)" << endl
505                    << "    { internalPropSet(\"" << property.name
506                    << "\", qVariantFromValue(value)); }" << endl;
507             }
508
509             hs << endl;
510         }
511
512         // methods:
513         hs << "public Q_SLOTS: // METHODS" << endl;
514         foreach (const QDBusIntrospection::Method &method, interface->methods) {
515             bool isAsync = method.annotations.value(ANNOTATION_NO_WAIT) == "true";
516             if (isAsync && !method.outputArgs.isEmpty()) {
517                 fprintf(stderr, "warning: method %s in interface %s is marked 'async' but has output arguments.\n",
518                         qPrintable(method.name), qPrintable(interface->name));
519                 continue;
520             }
521             
522             hs << "    inline ";
523
524             if (method.annotations.value("org.freedesktop.DBus.Deprecated") == "true")
525                 hs << "Q_DECL_DEPRECATED ";
526
527             if (isAsync)
528                 hs << "Q_ASYNC void ";
529             else if (method.outputArgs.isEmpty())
530                 hs << "QDBusReply<void> ";
531             else {
532                 hs << "QDBusReply<" << templateArg(qtTypeName(method.outputArgs.first().type)) << "> ";
533             }
534
535             hs << method.name << "(";
536
537             QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
538             writeArgList(hs, argNames, method.inputArgs, method.outputArgs);
539
540             hs << ")" << endl
541                << "    {" << endl;
542
543             if (method.outputArgs.count() > 1)
544                 hs << "        QDBusMessage reply = call(QLatin1String(\"";
545             else if (!isAsync)
546                 hs << "        return call(QLatin1String(\"";
547             else
548                 hs << "        call(NoWaitForReply, QLatin1String(\"";
549
550             // rebuild the method input signature:
551             QString signature = QChar('.');
552             foreach (const QDBusIntrospection::Argument &arg, method.inputArgs)
553                 signature += arg.type;
554             if (signature.length() == 1)
555                 signature.clear();
556             hs << method.name << signature << "\")";
557
558             int argPos = 0;
559             for (int i = 0; i < method.inputArgs.count(); ++i)
560                 hs << ", " << argNames.at(argPos++);
561
562             // close the QDBusIntrospection::call call
563             hs << ");" << endl;
564
565             argPos++;
566             if (method.outputArgs.count() > 1) {
567                 hs << "        if (reply.type() == QDBusMessage::ReplyMessage) {" << endl;
568                 
569                 // yes, starting from 1
570                 for (int i = 1; i < method.outputArgs.count(); ++i)
571                     hs << "            " << argNames.at(argPos++) << " = qvariant_cast<"
572                        << templateArg(qtTypeName(method.outputArgs.at(i).type))
573                        << ">(reply.at(" << i << "));" << endl;
574                 hs << "        }" << endl
575                    << "        return reply;" << endl;
576             }
577
578             // close the function:
579             hs << "    }" << endl
580                << endl;
581         }
582
583         hs << "Q_SIGNALS: // SIGNALS" << endl;
584         foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
585             hs << "    ";
586             if (signal.annotations.value("org.freedesktop.DBus.Deprecated") == "true")
587                 hs << "Q_DECL_DEPRECATED ";
588             
589             hs << "void " << signal.name << "(";
590
591             QStringList argNames = makeArgNames(signal.outputArgs);
592             writeArgList(hs, argNames, signal.outputArgs);
593
594             hs << ");" << endl; // finished for header
595         }
596         
597         // close the class:
598         hs << "};" << endl
599            << endl;
600     }
601
602     if (!skipNamespaces) {
603         QStringList last;
604         QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin();
605         do
606         {
607             QStringList current;
608             QString name;
609             if (it != interfaces.constEnd()) {
610                 current = it->constData()->name.split('.');
611                 name = current.takeLast();
612             }
613             
614             int i = 0;
615             while (i < current.count() && i < last.count() && current.at(i) == last.at(i))
616                 ++i;
617         
618             // i parts matched
619             // close last.count() - i namespaces:
620             for (int j = i; j < last.count(); ++j)
621                 hs << QString((last.count() - j - 1 + i) * 2, ' ') << "}" << endl;
622
623             // open current.count() - i namespaces
624             for (int j = i; j < current.count(); ++j)
625                 hs << QString(j * 2, ' ') << "namespace " << current.at(j) << " {" << endl;
626
627             // add this class:
628             if (!name.isEmpty()) {
629                 hs << QString(current.count() * 2, ' ')
630                    << "typedef ::" << classNameForInterface(it->constData()->name, Proxy)
631                    << " " << name << ";" << endl;
632             }
633
634             if (it == interfaces.constEnd())
635                 break;
636             ++it;
637             last = current;
638         } while (true);
639     }
640
641     // close the include guard
642     hs << "#endif" << endl;
643
644     if (includeMocs)
645         cs << endl
646            << "#include \"" << proxyFile << ".moc\"" << endl;
647
648     cs.flush();
649     hs.flush();
650     if (headerName == cppName)
651         file.write(cppData);
652     else {
653         // write to cpp file
654         QFile f(cppName);
655         f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
656         f.write(cppData);
657     }
658 }
659
660 static void writeAdaptor(const char *adaptorFile, const QDBusIntrospection::Interfaces &interfaces)
661 {
662     // open the file
663     QString headerName = header(adaptorFile);
664     QFile file(headerName);
665     if (!headerName.isEmpty())
666         file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
667     else
668         file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
669     QTextStream hs(&file);
670     
671     QString cppName = cpp(adaptorFile);
672     QByteArray cppData;
673     QTextStream cs(&cppData);
674
675     // write the headers
676     writeHeader(hs, false);
677
678     // include guards:
679     QString includeGuard;
680     if (!headerName.isEmpty()) {
681         includeGuard = headerName.toUpper().replace(QChar('.'), QChar('_'));
682         int pos = includeGuard.lastIndexOf('/');
683         if (pos != -1)
684             includeGuard = includeGuard.mid(pos + 1);
685     } else {
686         includeGuard = QString("QDBUSIDL2CPP_ADAPTOR");
687     }
688     includeGuard = QString("%1_%2%3")
689                    .arg(includeGuard)
690                    .arg(getpid())
691                    .arg(QDateTime::currentDateTime().toTime_t());
692     hs << "#ifndef " << includeGuard << endl
693        << "#define " << includeGuard << endl
694        << endl;
695
696     // include our stuff:
697     hs << "#include <QtCore/QObject>" << endl;
698     if (cppName == headerName)
699         hs << "#include <QtCore/QMetaObject>" << endl
700            << "#include <QtCore/QVariant>" << endl;
701     hs << "#include <dbus/qdbus.h>" << endl;
702     
703     if (cppName != headerName) {
704         writeHeader(cs, false);        
705         cs << "#include \"" << headerName << "\"" << endl
706            << "#include <QtCore/QMetaObject>" << endl
707            << includeList
708            << endl;
709         hs << forwardDeclarations;
710     } else {
711         hs << includeList;
712     }
713
714     hs << endl;
715
716     foreach (const QDBusIntrospection::Interface *interface, interfaces) {
717         QString className = classNameForInterface(interface->name, Adaptor);
718
719         // comment:
720         hs << "/*" << endl
721            << " * Adaptor class for interface " << interface->name << endl
722            << " */" << endl;
723         cs << "/*" << endl
724            << " * Implementation of adaptor class " << className << endl
725            << " */" << endl
726            << endl;
727
728         // class header:
729         hs << "class " << className << ": public QDBusAbstractAdaptor" << endl
730            << "{" << endl
731            << "    Q_OBJECT" << endl
732            << "    Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << endl
733            << "    Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << endl
734            << stringify(interface->introspection)
735            << "        \"\")" << endl
736            << "public:" << endl
737            << "    " << className << "(QObject *parent);" << endl
738            << "    virtual ~" << className << "();" << endl
739            << endl;
740
741         // constructor/destructor
742         cs << className << "::" << className << "(QObject *parent)" << endl
743            << "   : QDBusAbstractAdaptor(parent)" << endl
744            << "{" << endl
745            << "    // constructor" << endl
746            << "    setAutoRelaySignals(true);" << endl
747            << "}" << endl
748            << endl
749            << className << "::~" << className << "()" << endl
750            << "{" << endl
751            << "    // destructor" << endl
752            << "}" << endl
753            << endl;
754
755         hs << "public: // PROPERTIES" << endl;
756         foreach (const QDBusIntrospection::Property &property, interface->properties) {
757             QByteArray type = qtTypeName(property.type);
758             QString constRefType = constRefArg(type);
759             QString getter = propertyGetter(property);
760             QString setter = propertySetter(property);
761             
762             hs << "    Q_PROPERTY(" << type << " " << property.name;
763             if (property.access != QDBusIntrospection::Property::Write)
764                 hs << " READ " << getter;
765             if (property.access != QDBusIntrospection::Property::Read)
766                 hs << " WRITE " << setter;
767             hs << ")" << endl;
768
769             // getter:
770             if (property.access != QDBusIntrospection::Property::Write) {
771                 hs << "    " << type << " " << getter << "() const;" << endl;
772                 cs << type << " "
773                    << className << "::" << getter << "() const" << endl
774                    << "{" << endl
775                    << "    // get the value of property " << property.name << endl
776                    << "    return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));" << endl
777                    << "}" << endl
778                    << endl;
779             }
780
781             // setter
782             if (property.access != QDBusIntrospection::Property::Read) {
783                 hs << "    void " << setter << "(" << constRefType << "value);" << endl;
784                 cs << "void " << className << "::" << setter << "(" << constRefType << "value)" << endl
785                    << "{" << endl
786                    << "    // set the value of property " << property.name << endl
787                    << "    parent()->setProperty(\"" << property.name << "\", value);" << endl
788                    << "}" << endl
789                    << endl;
790             }
791
792             hs << endl;
793         }
794
795         hs << "public Q_SLOTS: // METHODS" << endl;
796         foreach (const QDBusIntrospection::Method &method, interface->methods) {
797             bool isAsync = method.annotations.value(ANNOTATION_NO_WAIT) == "true";
798             if (isAsync && !method.outputArgs.isEmpty()) {
799                 fprintf(stderr, "warning: method %s in interface %s is marked 'async' but has output arguments.\n",
800                         qPrintable(method.name), qPrintable(interface->name));
801                 continue;
802             }
803
804             hs << "    ";
805             if (method.annotations.value("org.freedesktop.DBus.Deprecated") == "true")
806                 hs << "Q_DECL_DEPRECATED ";
807
808             QByteArray returnType;
809             if (isAsync) {
810                 hs << "Q_ASYNC void ";
811                 cs << "void ";
812             } else if (method.outputArgs.isEmpty()) {
813                 hs << "void ";
814                 cs << "void ";
815             } else {
816                 returnType = qtTypeName(method.outputArgs.first().type);
817                 hs << returnType << " ";
818                 cs << returnType << " ";
819             }
820
821             QString name = method.name;
822             hs << name << "(";
823             cs << className << "::" << name << "(";
824
825             QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
826             writeArgList(hs, argNames, method.inputArgs, method.outputArgs);
827             writeArgList(cs, argNames, method.inputArgs, method.outputArgs);
828
829             hs << ");" << endl; // finished for header
830             cs << ")" << endl
831                << "{" << endl
832                << "    // handle method call " << interface->name << "." << method.name << endl;
833
834             // create the return type
835             int j = method.inputArgs.count();
836             if (!returnType.isEmpty())
837                 cs << "    " << returnType << " " << argNames.at(j) << ";" << endl;
838
839             // make the call
840             if (method.inputArgs.count() <= 10 && method.outputArgs.count() <= 1) {
841                 // we can use QMetaObject::invokeMethod
842                 static const char invoke[] = "    QMetaObject::invokeMethod(parent(), \"";
843                 cs << invoke << name << "\"";
844
845                 if (!method.outputArgs.isEmpty())
846                     cs << ", Q_RETURN_ARG("
847                        << qtTypeName(method.outputArgs.at(0).type)
848                        << ", "
849                        << argNames.at(method.inputArgs.count())
850                        << ")";
851                 
852                 for (int i = 0; i < method.inputArgs.count(); ++i)
853                     cs << ", Q_ARG("
854                        << qtTypeName(method.inputArgs.at(i).type)
855                        << ", "
856                        << argNames.at(i)
857                        << ")";
858                     
859                 cs << ");" << endl;
860             }
861
862             cs << endl
863                << "    // Alternative:" << endl
864                << "    //";
865             if (!method.outputArgs.isEmpty())
866                 cs << argNames.at(method.inputArgs.count()) << " = ";
867             cs << "static_cast<YourObjectType *>(parent())->" << name << "(";
868             
869             int argPos = 0;
870             bool first = true;
871             for (int i = 0; i < method.inputArgs.count(); ++i) {
872                 cs << (first ? "" : ", ") << argNames.at(argPos++);
873                 first = false;
874             }
875             ++argPos;           // skip retval, if any
876             for (int i = 1; i < method.outputArgs.count(); ++i) {
877                 cs << (first ? "" : ", ") << argNames.at(argPos++);
878                 first = false;
879             }
880
881             cs << ");" << endl;
882             if (!method.outputArgs.isEmpty())
883                 cs << "    return " << argNames.at(method.inputArgs.count()) << ";" << endl;
884             cs << "}" << endl
885                << endl;
886         }
887
888         hs << "Q_SIGNALS: // SIGNALS" << endl;
889         foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
890             hs << "    ";
891             if (signal.annotations.value("org.freedesktop.DBus.Deprecated") == "true")
892                 hs << "Q_DECL_DEPRECATED ";
893             
894             hs << "void " << signal.name << "(";
895
896             QStringList argNames = makeArgNames(signal.outputArgs);
897             writeArgList(hs, argNames, signal.outputArgs);
898
899             hs << ");" << endl; // finished for header
900         }
901
902         // close the class:
903         hs << "};" << endl
904            << endl;        
905     }
906
907     // close the include guard
908     hs << "#endif" << endl;
909
910     if (includeMocs)
911         cs << endl
912            << "#include \"" << adaptorFile << ".moc\"" << endl;
913     
914     cs.flush();
915     hs.flush();
916     if (headerName == cppName)
917         file.write(cppData);
918     else {
919         // write to cpp file
920         QFile f(cppName);
921         f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
922         f.write(cppData);
923     }
924 }
925
926 int main(int argc, char **argv)
927 {
928     parseCmdLine(argc, argv);
929
930     QDBusIntrospection::Interfaces interfaces = readInput();
931     cleanInterfaces(interfaces);
932
933     writeProxy(proxyFile, interfaces);
934
935     if (adaptorFile)
936         writeAdaptor(adaptorFile, interfaces);
937
938     return 0;
939 }
940     
941 /*!
942     \page dbusidl2cpp.html
943     \title QtDBus IDL compiler (dbusidl2cpp)
944
945     The QtDBus IDL compiler is a tool that can be used to parse interface descriptions and produce
946     static code representing those interfaces, which can then be used to make calls to remote
947     objects or implement said interfaces.
948
949     \c dbusidl2dcpp has two modes of operation, that correspond to the two possible outputs it can
950     produce: the interface (proxy) class or the adaptor class.The latter consists of both a C++
951     header and a source file, which are meant to be edited and adapted to your needs.
952
953     The \c dbusidl2dcpp tool is not meant to be run every time you compile your
954     application. Instead, it's meant to be used when developing the code or when the interface
955     changes.
956
957     The adaptor classes generated by \c dbusidl2cpp are just a skeleton that must be completed. It
958     generates, by default, calls to slots with the same name on the object the adaptor is attached
959     to. However, you may modify those slots or the property accessor functions to suit your needs.
960 */