source: trunk/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp@ 274

Last change on this file since 274 was 2, checked in by Dmitry A. Kuminov, 16 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 ([email protected])
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 [email protected].
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.