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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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