|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
70 static QString globalClassName; |
|
71 static QString parentClassName; |
|
72 static QString proxyFile; |
|
73 static QString adaptorFile; |
|
74 static QString inputFile; |
|
75 static bool skipNamespaces; |
|
76 static bool verbose; |
|
77 static bool includeMocs; |
|
78 static QString commandLine; |
|
79 static QStringList includes; |
|
80 static QStringList wantedInterfaces; |
|
81 |
|
82 static 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 |
|
106 static 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 |
|
114 static 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 |
|
122 static void showHelp() |
|
123 { |
|
124 printf("%s", help); |
|
125 exit(0); |
|
126 } |
|
127 |
|
128 static 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 |
|
135 static 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 |
|
145 static 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 |
|
221 static 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 |
|
242 static 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 |
|
255 static 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 |
|
271 static 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 |
|
287 static 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 |
|
298 static 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 |
|
320 enum ClassType { Proxy, Adaptor }; |
|
321 static 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 |
|
347 static 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 |
|
367 static QString nonConstRefArg(const QByteArray &arg) |
|
368 { |
|
369 return QLatin1String(arg + " &"); |
|
370 } |
|
371 |
|
372 static QString templateArg(const QByteArray &arg) |
|
373 { |
|
374 if (!arg.endsWith('>')) |
|
375 return QLatin1String(arg); |
|
376 |
|
377 return QLatin1String(arg + ' '); |
|
378 } |
|
379 |
|
380 static 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 |
|
388 static 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 |
|
414 static 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 |
|
448 static 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 |
|
458 static 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 |
|
468 static 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 |
|
484 static 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 |
|
502 static 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 |
|
808 static 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 |
|
1112 int 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 */ |