blob: f7972bed6cbe2c1fe876a4a6c41a71b4cfa07662 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001/*
2 *
3 * D-Bus++ - C++ bindings for D-Bus
4 *
5 * Copyright (C) 2005-2007 Paolo Durante <shackan@gmail.com>
6 *
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <iostream>
25#include <fstream>
26#include <cstdlib>
27#include <algorithm>
28
29#include "generator_utils.h"
30#include "generate_adaptor.h"
31
32using namespace std;
33using namespace DBus;
34
35extern const char *tab;
36extern const char *header;
37extern const char *dbus_includes;
38
39/*! Generate adaptor code for a XML introspection
40 */
41void generate_adaptor(Xml::Document &doc, const char *filename)
42{
43 ostringstream body;
44 ostringstream head;
45 vector <string> include_vector;
46
47 head << header;
48 string filestring = filename;
49 underscorize(filestring);
50
51 string cond_comp = "__dbusxx__" + filestring + "__ADAPTOR_MARSHAL_H";
52
53 head << "#ifndef " << cond_comp << endl
54 << "#define " << cond_comp << endl;
55
56 head << dbus_includes;
57
58 Xml::Node &root = *(doc.root);
59 Xml::Nodes interfaces = root["interface"];
60
61 // iterate over all interface definitions
62 for (Xml::Nodes::iterator i = interfaces.begin(); i != interfaces.end(); ++i)
63 {
64 Xml::Node &iface = **i;
65 Xml::Nodes methods = iface["method"];
66 Xml::Nodes signals = iface["signal"];
67 Xml::Nodes properties = iface["property"];
68 Xml::Nodes ms;
69 ms.insert(ms.end(), methods.begin(), methods.end());
70 ms.insert(ms.end(), signals.begin(), signals.end());
71
72 // gets the name of a interface: <interface name="XYZ">
73 string ifacename = iface.get("name");
74
75 // these interface names are skipped.
76 if (ifacename == "org.freedesktop.DBus.Introspectable"
77 || ifacename == "org.freedesktop.DBus.Properties")
78 {
79 cerr << "skipping interface " << ifacename << endl;
80 continue;
81 }
82
83 istringstream ss(ifacename);
84 string nspace;
85 unsigned int nspaces = 0;
86
87 // prints all the namespaces defined with <interface name="X.Y.Z">
88 while (ss.str().find('.', ss.tellg()) != string::npos)
89 {
90 getline(ss, nspace, '.');
91
92 body << "namespace " << nspace << " {" << endl;
93
94 ++nspaces;
95 }
96 body << endl;
97
98 string ifaceclass;
99
100 getline(ss, ifaceclass);
101
102 // a "_adaptor" is added to class name to distinguish between proxy and adaptor
103 ifaceclass += "_adaptor";
104
105 cerr << "generating code for interface " << ifacename << "..." << endl;
106
107 // the code from class definiton up to opening of the constructor is generated...
108 body << "class " << ifaceclass << endl
109 << ": public ::DBus::InterfaceAdaptor" << endl
110 << "{" << endl
111 << "public:" << endl
112 << endl
113 << tab << ifaceclass << "()" << endl
114 << tab << ": ::DBus::InterfaceAdaptor(\"" << ifacename << "\")" << endl
115 << tab << "{" << endl;
116
117 // generates code to bind the properties
118 for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
119 {
120 Xml::Node &property = **pi;
121
122 body << tab << tab << "bind_property("
123 << property.get("name") << ", "
124 << "\"" << property.get("type") << "\", "
125 << (property.get("access").find("read") != string::npos
126 ? "true"
127 : "false")
128 << ", "
129 << (property.get("access").find("write") != string::npos
130 ? "true"
131 : "false")
132 << ");" << endl;
133 }
134
135 // generate code to register all methods
136 for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
137 {
138 Xml::Node &method = **mi;
139
140 body << tab << tab << "register_method("
141 << ifaceclass << ", " << method.get("name") << ", " << stub_name(method.get("name"))
142 << ");" << endl;
143 }
144
145 body << tab << "}" << endl
146 << endl;
147
148 body << tab << "::DBus::IntrospectedInterface *introspect() const " << endl
149 << tab << "{" << endl;
150
151 // generate the introspect arguments
152 for (Xml::Nodes::iterator mi = ms.begin(); mi != ms.end(); ++mi)
153 {
154 Xml::Node &method = **mi;
155 Xml::Nodes args = method["arg"];
156
157 body << tab << tab << "static ::DBus::IntrospectedArgument " << method.get("name") << "_args[] = " << endl
158 << tab << tab << "{" << endl;
159
160 for (Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai)
161 {
162 Xml::Node &arg = **ai;
163
164 body << tab << tab << tab << "{ ";
165
166 if (arg.get("name").length())
167 {
168 body << "\"" << arg.get("name") << "\", ";
169 }
170 else
171 {
172 body << "0, ";
173 }
174 body << "\"" << arg.get("type") << "\", "
175 << (arg.get("direction") == "in" ? "true" : "false")
176 << " }," << endl;
177 }
178 body << tab << tab << tab << "{ 0, 0, 0 }" << endl
179 << tab << tab << "};" << endl;
180 }
181
182 body << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_methods[] = " << endl
183 << tab << tab << "{" << endl;
184
185 // generate the introspect methods
186 for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
187 {
188 Xml::Node &method = **mi;
189
190 body << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl;
191 }
192
193 body << tab << tab << tab << "{ 0, 0 }" << endl
194 << tab << tab << "};" << endl;
195
196 body << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_signals[] = " << endl
197 << tab << tab << "{" << endl;
198
199 for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
200 {
201 Xml::Node &method = **si;
202
203 body << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl;
204 }
205
206 body << tab << tab << tab << "{ 0, 0 }" << endl
207 << tab << tab << "};" << endl;
208
209 body << tab << tab << "static ::DBus::IntrospectedProperty " << ifaceclass << "_properties[] = " << endl
210 << tab << tab << "{" << endl;
211
212 for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
213 {
214 Xml::Node &property = **pi;
215
216 body << tab << tab << tab << "{ "
217 << "\"" << property.get("name") << "\", "
218 << "\"" << property.get("type") << "\", "
219 << (property.get("access").find("read") != string::npos
220 ? "true"
221 : "false")
222 << ", "
223 << (property.get("access").find("write") != string::npos
224 ? "true"
225 : "false")
226 << " }," << endl;
227 }
228
229
230 body << tab << tab << tab << "{ 0, 0, 0, 0 }" << endl
231 << tab << tab << "};" << endl;
232
233 // generate the Introspected interface
234 body << tab << tab << "static ::DBus::IntrospectedInterface " << ifaceclass << "_interface = " << endl
235 << tab << tab << "{" << endl
236 << tab << tab << tab << "\"" << ifacename << "\"," << endl
237 << tab << tab << tab << ifaceclass << "_methods," << endl
238 << tab << tab << tab << ifaceclass << "_signals," << endl
239 << tab << tab << tab << ifaceclass << "_properties" << endl
240 << tab << tab << "};" << endl
241 << tab << tab << "return &" << ifaceclass << "_interface;" << endl
242 << tab << "}" << endl
243 << endl;
244
245 body << "public:" << endl
246 << endl
247 << tab << "/* properties exposed by this interface, use" << endl
248 << tab << " * property() and property(value) to get and set a particular property" << endl
249 << tab << " */" << endl;
250
251 // generate the properties code
252 for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
253 {
254 Xml::Node &property = **pi;
255 string name = property.get("name");
256 string type = property.get("type");
257 string type_name = signature_to_type(type);
258
259 body << tab << "::DBus::PropertyAdaptor< " << type_name << " > " << name << ";" << endl;
260 }
261
262 body << endl;
263
264 body << "public:" << endl
265 << endl
266 << tab << "/* methods exported by this interface," << endl
267 << tab << " * you will have to implement them in your ObjectAdaptor" << endl
268 << tab << " */" << endl;
269
270 // generate the methods code
271 for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
272 {
273 Xml::Node &method = **mi;
274 Xml::Nodes args = method["arg"];
275 Xml::Nodes args_in = args.select("direction", "in");
276 Xml::Nodes args_out = args.select("direction", "out");
277 Xml::Nodes annotations = args["annotation"];
278 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
279 string arg_object;
280
281 if (!annotations_object.empty())
282 {
283 arg_object = annotations_object.front()->get("value");
284 }
285
286 body << tab << "virtual ";
287
288 // return type is 'void' if none or multible return values
289 if (args_out.size() == 0 || args_out.size() > 1)
290 {
291 body << "void ";
292 }
293 else if (args_out.size() == 1)
294 {
295 // generate basic or object return type
296 if (arg_object.length())
297 {
298 body << arg_object << " ";
299 }
300 else
301 {
302 body << signature_to_type(args_out.front()->get("type")) << " ";
303 }
304 }
305
306 // generate the method name
307 body << method.get("name") << "(";
308
309 // generate the methods 'in' variables
310 unsigned int i = 0;
311 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
312 {
313 Xml::Node &arg = **ai;
314 Xml::Nodes annotations = arg["annotation"];
315 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
316 string arg_name = arg.get("name");
317 string arg_object;
318
319 if (!annotations_object.empty())
320 {
321 arg_object = annotations_object.front()->get("value");
322 }
323
324 // generate basic signature only if no object name available...
325 if (!arg_object.length())
326 {
327 body << "const " << signature_to_type(arg.get("type")) << "& ";
328 }
329 // ...or generate object style if available
330 else
331 {
332 body << "const " << arg_object << "& ";
333
334 // store a object name to later generate header includes
335 include_vector.push_back(arg_object);
336 }
337
338 if (arg_name.length())
339 body << arg_name;
340
341 if ((i + 1 != args_in.size() || args_out.size() > 1))
342 body << ", ";
343 }
344
345 // generate the method 'out' variables if multibe 'out' values exist
346 if (args_out.size() > 1)
347 {
348 unsigned int i = 0;
349 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
350 {
351 Xml::Node &arg = **ao;
352 Xml::Nodes annotations = arg["annotation"];
353 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
354 string arg_name = arg.get("name");
355 string arg_object;
356
357 if (!annotations_object.empty())
358 {
359 arg_object = annotations_object.front()->get("value");
360 }
361
362 // generate basic signature only if no object name available...
363 if (!arg_object.length())
364 {
365 body << signature_to_type(arg.get("type")) << "& ";
366 }
367 // ...or generate object style if available
368 else
369 {
370 body << arg_object << "& ";
371
372 // store a object name to later generate header includes
373 include_vector.push_back(arg_object);
374 }
375
376 if (arg_name.length())
377 body << arg_name;
378
379 if (i + 1 != args_out.size())
380 body << ", ";
381 }
382 }
383 body << ") = 0;" << endl;
384 }
385
386 body << endl
387 << "public:" << endl
388 << endl
389 << tab << "/* signal emitters for this interface" << endl
390 << tab << " */" << endl;
391
392 // generate the signals code
393 for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
394 {
395 Xml::Node &signal = **si;
396 Xml::Nodes args = signal["arg"];
397
398 body << tab << "void " << signal.get("name") << "(";
399
400 // generate the signal arguments
401 unsigned int i = 0;
402 for (Xml::Nodes::iterator a = args.begin(); a != args.end(); ++a, ++i)
403 {
404 Xml::Node &arg = **a;
405 Xml::Nodes annotations = arg["annotation"];
406 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
407 string arg_object;
408
409 if (!annotations_object.empty())
410 {
411 arg_object = annotations_object.front()->get("value");
412 }
413
414 // generate basic signature only if no object name available...
415 if (!arg_object.length())
416 {
417 body << "const " << signature_to_type(arg.get("type")) << "& arg" << i + 1;
418 }
419 // ...or generate object style if available
420 else
421 {
422 body << "const " << arg_object << "& arg" << i + 1;
423
424 // store a object name to later generate header includes
425 include_vector.push_back(arg_object);
426 }
427
428 if (i + 1 != args.size())
429 body << ", ";
430 }
431
432 body << ")" << endl
433 << tab << "{" << endl
434 << tab << tab << "::DBus::SignalMessage sig(\"" << signal.get("name") << "\");" << endl;
435
436 // generate the signal body
437 if (!args.empty())
438 {
439 body << tab << tab << "::DBus::MessageIter wi = sig.writer();" << endl;
440
441 unsigned int i = 0;
442 for (Xml::Nodes::iterator a = args.begin(); a != args.end(); ++a, ++i)
443 {
444 Xml::Node &arg = **a;
445 Xml::Nodes annotations = arg["annotation"];
446 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
447 string arg_object;
448
449 if (!annotations_object.empty())
450 {
451 arg_object = annotations_object.front()->get("value");
452 }
453
454 if (arg_object.length())
455 {
456 body << tab << tab << signature_to_type(arg.get("type")) << " _arg" << i + 1 << ";" << endl;
457 body << tab << tab << "_arg" << i + 1 << " << " << "arg" << i + 1 << ";" << endl;
458
459 body << tab << tab << "wi << _arg" << i + 1 << ";" << endl;
460 }
461 else
462 {
463 body << tab << tab << "wi << arg" << i + 1 << ";" << endl;
464 }
465 }
466 }
467
468 // emit the signal in method body
469 body << tab << tab << "emit_signal(sig);" << endl
470 << tab << "}" << endl;
471 }
472
473 body << endl
474 << "private:" << endl
475 << endl
476 << tab << "/* unmarshalers (to unpack the DBus message before calling the actual interface method)" << endl
477 << tab << " */" << endl;
478
479 // generate the unmarshalers
480 for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
481 {
482 Xml::Node &method = **mi;
483 Xml::Nodes args = method["arg"];
484 Xml::Nodes args_in = args.select("direction", "in");
485 Xml::Nodes args_out = args.select("direction", "out");
486
487 body << tab << "::DBus::Message " << stub_name(method.get("name")) << "(const ::DBus::CallMessage &call)" << endl
488 << tab << "{" << endl
489 << tab << tab << "::DBus::MessageIter ri = call.reader();" << endl
490 << endl;
491
492 // generate the 'in' variables
493 unsigned int i = 1;
494 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
495 {
496 Xml::Node &arg = **ai;
497
498 body << tab << tab << signature_to_type(arg.get("type")) << " argin" << i << ";" << " ";
499 body << "ri >> argin" << i << ";" << endl;
500 }
501
502 // generate the 'in' object variables
503 i = 1;
504 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
505 {
506 Xml::Node &arg = **ai;
507 Xml::Nodes annotations = arg["annotation"];
508 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
509 string arg_object;
510
511 if (!annotations_object.empty())
512 {
513 arg_object = annotations_object.front()->get("value");
514 }
515
516 if (arg_object.length())
517 {
518 body << tab << tab << arg_object << " _argin" << i << ";";
519 body << " " << "_argin" << i << " << " << "argin" << i << ";" << endl;
520 }
521 }
522
523 // generate 'out' variables
524 if (!args_out.empty())
525 {
526 unsigned int i = 1;
527 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
528 {
529 Xml::Node &arg = **ao;
530
531 body << tab << tab << signature_to_type(arg.get("type")) << " argout" << i;
532
533 if (args_out.size() == 1) // a single 'out' parameter will be assigned
534 {
535 body << " = ";
536 }
537 else // multible 'out' parameters will be handled as parameters below
538 {
539 body << ";" << endl;
540 }
541 }
542 }
543
544 // generate 'out' object variables
545 if (!args_out.empty())
546 {
547 unsigned int i = 1;
548 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
549 {
550 Xml::Node &arg = **ao;
551 Xml::Nodes annotations = arg["annotation"];
552 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
553 string arg_object;
554
555 if (!annotations_object.empty())
556 {
557 arg_object = annotations_object.front()->get("value");
558 }
559
560 // generate object types
561 if (arg_object.length())
562 {
563 body << tab << tab << arg_object << " _argout" << i << ";" << endl;
564 }
565 }
566 }
567
568 // generate in '<<' operation
569 i = 0;
570 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
571 {
572 Xml::Node &arg = **ai;
573 Xml::Nodes annotations = arg["annotation"];
574 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
575 string arg_object;
576
577 if (!annotations_object.empty())
578 {
579 arg_object = annotations_object.front()->get("value");
580 }
581 }
582
583 // do correct indent
584 if (args_out.size() != 1)
585 {
586 body << tab << tab;
587 }
588
589 body << method.get("name") << "(";
590
591 // generate call stub parameters
592 i = 0;
593 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
594 {
595 Xml::Node &arg = **ai;
596 Xml::Nodes annotations = arg["annotation"];
597 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
598 string arg_object;
599
600 if (!annotations_object.empty())
601 {
602 arg_object = annotations_object.front()->get("value");
603 }
604
605 if (arg_object.length())
606 {
607 body << "_argin" << i + 1;
608 }
609 else
610 {
611 body << "argin" << i + 1;
612 }
613
614 if ((i + 1 != args_in.size() || args_out.size() > 1))
615 body << ", ";
616 }
617
618 if (args_out.size() > 1)
619 {
620 i = 0;
621 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
622 {
623 Xml::Node &arg = **ao;
624 Xml::Nodes annotations = arg["annotation"];
625 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
626 string arg_object;
627
628 if (!annotations_object.empty())
629 {
630 arg_object = annotations_object.front()->get("value");
631 }
632
633 if (arg_object.length())
634 {
635 body << "_argout" << i + 1;
636 }
637 else
638 {
639 body << "argout" << i + 1;
640 }
641
642 if (i + 1 != args_out.size())
643 body << ", ";
644 }
645 }
646
647 body << ");" << endl;
648
649 body << tab << tab << "::DBus::ReturnMessage reply(call);" << endl;
650
651 if (!args_out.empty())
652 {
653 body << tab << tab << "::DBus::MessageIter wi = reply.writer();" << endl;
654
655 // generate out '<<' operation
656 i = 0;
657 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
658 {
659 Xml::Node &arg = **ao;
660 Xml::Nodes annotations = arg["annotation"];
661 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
662 string arg_object;
663
664 if (!annotations_object.empty())
665 {
666 arg_object = annotations_object.front()->get("value");
667 }
668
669 if (arg_object.length())
670 {
671 body << tab << tab << "argout" << i + 1 << " << " << "_argout" << i + 1 << ";" << endl;
672 }
673 }
674
675 for (unsigned int i = 0; i < args_out.size(); ++i)
676 {
677 body << tab << tab << "wi << argout" << i + 1 << ";" << endl;
678 }
679 }
680
681 body << tab << tab << "return reply;" << endl;
682
683 body << tab << "}" << endl;
684 }
685
686 body << "};" << endl
687 << endl;
688
689 for (unsigned int i = 0; i < nspaces; ++i)
690 {
691 body << "} ";
692 }
693 body << endl;
694 }
695
696 body << "#endif //" << cond_comp << endl;
697
698 // remove all duplicates in the header include vector
699 vector<string>::const_iterator vec_end_it = unique(include_vector.begin(), include_vector.end());
700
701 for (vector<string>::const_iterator vec_it = include_vector.begin();
702 vec_it != vec_end_it;
703 ++vec_it)
704 {
705 const string &include = *vec_it;
706
707 head << "#include " << "\"" << include << ".h" << "\"" << endl;
708 }
709 head << endl;
710
711 ofstream file(filename);
712 if (file.bad())
713 {
714 cerr << "unable to write file " << filename << endl;
715 exit(-1);
716 }
717
718 file << head.str();
719 file << body.str();
720
721 file.close();
722}