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

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

trunk: Merged in qt 4.6.2 sources.

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