blob: bf1094ac736598954c84c773b0ef60014f86dfa8 [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_proxy.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 proxy code for a XML introspection
40 */
41void generate_proxy(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 + "__PROXY_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 "_proxy" is added to class name to distinguish between proxy and adaptor
103 ifaceclass += "_proxy";
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::InterfaceProxy" << endl
110 << "{" << endl
111 << "public:" << endl
112 << endl
113 << tab << ifaceclass << "()" << endl
114 << tab << ": ::DBus::InterfaceProxy(\"" << ifacename << "\")" << endl
115 << tab << "{" << endl;
116
117 // generates code to connect all the signal stubs; this is still inside the constructor
118 for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
119 {
120 Xml::Node &signal = **si;
121
122 string marshname = "_" + signal.get("name") + "_stub";
123
124 body << tab << tab << "connect_signal("
125 << ifaceclass << ", " << signal.get("name") << ", " << stub_name(signal.get("name"))
126 << ");" << endl;
127 }
128
129 // the constructor ends here
130 body << tab << "}" << endl
131 << endl;
132
133 // write public block header for properties
134 body << "public:" << endl << endl
135 << tab << "/* properties exported by this interface */" << endl;
136
137 // this loop generates all properties
138 for (Xml::Nodes::iterator pi = properties.begin();
139 pi != properties.end(); ++pi)
140 {
141 Xml::Node &property = **pi;
142 string prop_name = property.get("name");
143 string property_access = property.get("access");
144 if (property_access == "read" || property_access == "readwrite")
145 {
146 body << tab << tab << "const " << signature_to_type(property.get("type"))
147 << " " << prop_name << "() {" << endl;
148 body << tab << tab << tab << "::DBus::CallMessage call ;\n ";
149 body << tab << tab << tab
150 << "call.member(\"Get\"); call.interface(\"org.freedesktop.DBus.Properties\");"
151 << endl;
152 body << tab << tab << tab
153 << "::DBus::MessageIter wi = call.writer(); " << endl;
154 body << tab << tab << tab
155 << "const std::string interface_name = \"" << ifacename << "\";"
156 << endl;
157 body << tab << tab << tab
158 << "const std::string property_name = \"" << prop_name << "\";"
159 << endl;
160 body << tab << tab << tab << "wi << interface_name;" << endl;
161 body << tab << tab << tab << "wi << property_name;" << endl;
162 body << tab << tab << tab
163 << "::DBus::Message ret = this->invoke_method (call);" << endl;
164 // TODO: support invoke_method_NoReply for properties
165 body << tab << tab << tab
166 << "::DBus::MessageIter ri = ret.reader ();" << endl;
167 body << tab << tab << tab << "::DBus::Variant argout; " << endl;
168 body << tab << tab << tab << "ri >> argout;" << endl;
169 body << tab << tab << tab << "return argout;" << endl;
170 body << tab << tab << "};" << endl;
171 }
172
173 if (property_access == "write" || property_access == "readwrite")
174 {
175 body << tab << tab << "void " << prop_name << "( const " << signature_to_type(property.get("type")) << " & input" << ") {" << endl;
176 body << tab << tab << tab << "::DBus::CallMessage call ;\n ";
177 body << tab << tab << tab << "call.member(\"Set\"); call.interface( \"org.freedesktop.DBus.Properties\");" << endl;
178 body << tab << tab << tab << "::DBus::MessageIter wi = call.writer(); " << endl;
179 body << tab << tab << tab << "::DBus::Variant value;" << endl;
180 body << tab << tab << tab << "::DBus::MessageIter vi = value.writer ();" << endl;
181 body << tab << tab << tab << "vi << input;" << endl;
182 body << tab << tab << tab << "const std::string interface_name = \"" << ifacename << "\";" << endl;
183 body << tab << tab << tab << "const std::string property_name = \"" << prop_name << "\";" << endl;
184 body << tab << tab << tab << "wi << interface_name;" << endl;
185 body << tab << tab << tab << "wi << property_name;" << endl;
186 body << tab << tab << tab << "wi << value;" << endl;
187 body << tab << tab << tab << "::DBus::Message ret = this->invoke_method (call);" << endl;
188 // TODO: support invoke_method_noreply for properties
189 body << tab << tab << "};" << endl;
190 }
191 }
192
193 // write public block header for methods
194 body << "public:" << endl
195 << endl
196 << tab << "/* methods exported by this interface," << endl
197 << tab << " * this functions will invoke the corresponding methods on the remote objects" << endl
198 << tab << " */" << endl;
199
200 // this loop generates all methods
201 for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
202 {
203 Xml::Node &method = **mi;
204 Xml::Nodes args = method["arg"];
205 Xml::Nodes args_in = args.select("direction", "in");
206 Xml::Nodes args_out = args.select("direction", "out");
207 Xml::Nodes annotations = args["annotation"];
208 Xml::Nodes method_annotations = method["annotation"];
209 Xml::Nodes annotations_noreply = method_annotations.select("name", "org.freedesktop.DBus.Method.NoReply");
210 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
211 string arg_object;
212 bool annotation_noreply_value = false;
213
214 // parse method level noreply annotations
215 if (!annotations_noreply.empty())
216 {
217 string annotation_noreply_value_str = annotations_noreply.front()->get("value");
218
219 if (annotation_noreply_value_str == "true")
220 {
221 annotation_noreply_value = true;
222 }
223 }
224
225 if (!annotations_object.empty())
226 {
227 arg_object = annotations_object.front()->get("value");
228 }
229
230 if (args_out.size() == 0 || args_out.size() > 1)
231 {
232 body << tab << "void ";
233 }
234 else if (args_out.size() == 1)
235 {
236 if (arg_object.length())
237 {
238 body << tab << arg_object << " ";
239 }
240 else
241 {
242 body << tab << signature_to_type(args_out.front()->get("type")) << " ";
243 }
244 }
245
246 body << method.get("name") << "(";
247
248 // generate all 'in' arguments for a method signature
249 unsigned int i = 0;
250 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
251 {
252 Xml::Node &arg = **ai;
253 Xml::Nodes annotations = arg["annotation"];
254 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
255 string arg_object;
256
257 if (!annotations_object.empty())
258 {
259 arg_object = annotations_object.front()->get("value");
260 }
261
262 // generate basic signature only if no object name available...
263 if (!arg_object.length())
264 {
265 body << "const " << signature_to_type(arg.get("type")) << "& ";
266 }
267 // ...or generate object style if available
268 else
269 {
270 body << "const " << arg_object << "& ";
271
272 // store a object name to later generate header includes
273 include_vector.push_back(arg_object);
274 }
275
276 string arg_name = arg.get("name");
277 if (arg_name.length())
278 body << arg_name;
279 else
280 body << "argin" << i;
281
282 if ((i + 1 != args_in.size() || args_out.size() > 1))
283 body << ", ";
284 }
285
286 if (args_out.size() > 1)
287 {
288 // generate all 'out' arguments for a method signature
289 unsigned int j = 0;
290 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++j)
291 {
292 Xml::Node &arg = **ao;
293 Xml::Nodes annotations = arg["annotation"];
294 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
295 string arg_object;
296
297 if (!annotations_object.empty())
298 {
299 arg_object = annotations_object.front()->get("value");
300 }
301
302 // generate basic signature only if no object name available...
303 if (!arg_object.length())
304 {
305 body << signature_to_type(arg.get("type")) << "&";
306 }
307 // ...or generate object style if available
308 else
309 {
310 body << arg_object << "& ";
311
312 // store a object name to later generate header includes
313 include_vector.push_back(arg_object);
314 }
315
316 string arg_name = arg.get("name");
317 if (arg_name.length())
318 body << " " << arg_name;
319 else
320 body << " argout" << j;
321
322 if (j + 1 != args_out.size())
323 body << ", ";
324 }
325 }
326 body << ")" << endl;
327
328 body << tab << "{" << endl
329 << tab << tab << "::DBus::CallMessage call;" << endl;
330
331 if (!args_in.empty())
332 {
333 body << tab << tab << "::DBus::MessageIter wi = call.writer();" << endl
334 << endl;
335 }
336
337 // generate all 'in' arguments for a method body
338 i = 0;
339 for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
340 {
341 Xml::Node &arg = **ai;
342 string arg_name = arg.get("name");
343 Xml::Nodes annotations = arg["annotation"];
344 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
345 string arg_object;
346
347 if (!annotations_object.empty())
348 {
349 arg_object = annotations_object.front()->get("value");
350 }
351
352 if (!arg_name.length())
353 {
354 arg_name = "argin";
355 arg_name += toString <uint> (i);
356 }
357
358 // generate extra code to wrap object
359 if (arg_object.length())
360 {
361 body << tab << tab << signature_to_type(arg.get("type")) << "_" << arg_name << ";" << endl;
362 body << tab << tab << "_" << arg_name << " << " << arg_name << ";" << endl;
363
364 arg_name = string("_") + arg_name;
365 }
366
367 body << tab << tab << "wi << " << arg_name << ";" << endl;
368 }
369
370 body << tab << tab << "call.member(\"" << method.get("name") << "\");" << endl;
371
372 // generate noreply/reply method calls
373 if (annotation_noreply_value)
374 {
375 if (args_out.size())
376 {
377 cerr << "Function: " << method.get("name") << ":" << endl;
378 cerr << "Option 'org.freedesktop.DBus.Method.NoReply' not allowed for methods with 'out' variables!" << endl << "-> Option ignored!" << endl;
379
380 body << tab << tab << "::DBus::Message ret = invoke_method (call);" << endl;
381 }
382 else
383 {
384 body << tab << tab << "assert (invoke_method_noreply (call));" << endl; // will only assert in case of no memory
385 }
386 }
387 else
388 {
389 body << tab << tab << "::DBus::Message ret = invoke_method (call);" << endl;
390 }
391
392 if (!args_out.empty())
393 {
394 body << tab << tab << "::DBus::MessageIter ri = ret.reader();" << endl
395 << endl;
396 }
397
398 // generate 'out' values as return if only one existing
399 if (args_out.size() == 1)
400 {
401 Xml::Nodes annotations = args_out["annotation"];
402 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
403 string arg_object;
404
405 if (!annotations_object.empty())
406 {
407 arg_object = annotations_object.front()->get("value");
408 }
409
410 if (arg_object.length())
411 {
412 body << tab << tab << arg_object << " _argout;" << endl;
413 }
414
415 body << tab << tab << signature_to_type(args_out.front()->get("type")) << " argout;" << endl;
416
417 body << tab << tab << "ri >> argout;" << endl;
418
419 if (arg_object.length())
420 {
421 body << tab << tab << "_argout << argout;" << endl;
422 body << tab << tab << "return _argout;" << endl;
423 }
424 else
425 {
426 body << tab << tab << "return argout;" << endl;
427 }
428 }
429 else if (args_out.size() > 1)
430 {
431 // generate multible 'out' value
432 unsigned int i = 0;
433 for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
434 {
435 Xml::Node &arg = **ao;
436 string arg_name = arg.get("name");
437 Xml::Nodes annotations = arg["annotation"];
438 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
439 string arg_object;
440
441 if (!annotations_object.empty())
442 {
443 arg_object = annotations_object.front()->get("value");
444 }
445
446 if (!arg_name.length())
447 {
448 arg_name = "argout" + toString <uint> (i);
449 }
450
451 if (arg_object.length())
452 {
453 body << tab << tab << signature_to_type(arg.get("type")) << "_" << arg_name << ";" << endl;
454 }
455
456 if (arg_object.length())
457 {
458 body << tab << tab << "ri >> " << "_" << arg_name << ";" << endl;
459 }
460 else
461 {
462 body << tab << tab << "ri >> " << arg_name << ";" << endl;
463 }
464
465 if (arg_object.length())
466 {
467 body << tab << tab << arg_name << " << " << "_" << arg_name << ";" << endl;
468 }
469 }
470 }
471
472 body << tab << "}" << endl
473 << endl;
474 }
475
476 // write public block header for signals
477 body << endl
478 << "public:" << endl
479 << endl
480 << tab << "/* signal handlers for this interface" << endl
481 << tab << " */" << endl;
482
483 // this loop generates all signals
484 for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
485 {
486 Xml::Node &signal = **si;
487 Xml::Nodes args = signal["arg"];
488
489 body << tab << "virtual void " << signal.get("name") << "(";
490
491 // this loop generates all argument for a signal
492 unsigned int i = 0;
493 for (Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++i)
494 {
495 Xml::Node &arg = **ai;
496 string arg_name = arg.get("name");
497 Xml::Nodes annotations = arg["annotation"];
498 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
499 string arg_object;
500
501 if (!annotations_object.empty())
502 {
503 arg_object = annotations_object.front()->get("value");
504 }
505
506 // generate basic signature only if no object name available...
507 if (!arg_object.length())
508 {
509 body << "const " << signature_to_type(arg.get("type")) << "& ";
510 }
511 // ...or generate object style if available
512 else
513 {
514 body << "const " << arg_object << "& ";
515
516 // store a object name to later generate header includes
517 include_vector.push_back(arg_object);
518 }
519
520 if (arg_name.length())
521 body << arg_name;
522 else
523 body << "argin" << i;
524
525 if ((ai + 1 != args.end()))
526 body << ", ";
527 }
528 body << ") = 0;" << endl;
529 }
530
531 // write private block header for unmarshalers
532 body << endl
533 << "private:" << endl
534 << endl
535 << tab << "/* unmarshalers (to unpack the DBus message before calling the actual signal handler)" << endl
536 << tab << " */" << endl;
537
538 // generate all the unmarshalers
539 for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
540 {
541 Xml::Node &signal = **si;
542 Xml::Nodes args = signal["arg"];
543
544 body << tab << "void " << stub_name(signal.get("name")) << "(const ::DBus::SignalMessage &sig)" << endl
545 << tab << "{" << endl;
546
547 if (!args.empty())
548 {
549 body << tab << tab << "::DBus::MessageIter ri = sig.reader();" << endl
550 << endl;
551 }
552
553 unsigned int i = 0;
554 for (Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++i)
555 {
556 Xml::Node &arg = **ai;
557 string arg_name = arg.get("name");
558 Xml::Nodes annotations = arg["annotation"];
559 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
560 string arg_object;
561
562 if (!annotations_object.empty())
563 {
564 arg_object = annotations_object.front()->get("value");
565 }
566
567 body << tab << tab << signature_to_type(arg.get("type")) << " " ;
568
569 // use a default if no arg name given
570 if (!arg_name.length())
571 {
572 arg_name = "arg" + toString <uint> (i);
573 }
574
575 body << arg_name << ";" << endl;
576 body << tab << tab << "ri >> " << arg_name << ";" << endl;
577
578 // if a object type is used create a local variable and insert values with '<<' operation
579 if (arg_object.length())
580 {
581 body << tab << tab << arg_object << " _" << arg_name << ";" << endl;
582 body << tab << tab << "_" << arg_name << " << " << arg_name << ";" << endl;
583
584 // store a object name to later generate header includes
585 include_vector.push_back(arg_object);
586 }
587 }
588
589 body << tab << tab << signal.get("name") << "(";
590
591 // generate all arguments for the call to the virtual function
592 unsigned int j = 0;
593 for (Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++j)
594 {
595 Xml::Node &arg = **ai;
596 string arg_name = arg.get("name");
597 Xml::Nodes annotations = arg["annotation"];
598 Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
599 string arg_object;
600
601 if (!annotations_object.empty())
602 {
603 arg_object = annotations_object.front()->get("value");
604 }
605
606 if (!arg_name.length())
607 {
608 arg_name = "arg" + toString <uint> (j);
609 }
610
611 if (arg_object.length())
612 {
613 body << "_" << arg_name;
614 }
615 else
616 {
617 body << arg_name;
618 }
619
620 if (ai + 1 != args.end())
621 body << ", ";
622 }
623
624 body << ");" << endl;
625
626 body << tab << "}" << endl;
627 }
628
629 body << "};" << endl
630 << endl;
631
632 for (unsigned int i = 0; i < nspaces; ++i)
633 {
634 body << "} ";
635 }
636 body << endl;
637 }
638
639 body << "#endif //" << cond_comp << endl;
640
641 // remove all duplicates in the header include vector
642 vector<string>::const_iterator vec_end_it = unique(include_vector.begin(), include_vector.end());
643
644 for (vector<string>::const_iterator vec_it = include_vector.begin();
645 vec_it != vec_end_it;
646 ++vec_it)
647 {
648 const string &include = *vec_it;
649
650 head << "#include " << "\"" << include << ".h" << "\"" << endl;
651 }
652 head << endl;
653
654 ofstream file(filename);
655 if (file.bad())
656 {
657 cerr << "unable to write file " << filename << endl;
658 exit(-1);
659 }
660
661 file << head.str();
662 file << body.str();
663
664 file.close();
665}