2 * vala-dbus-binding-tool.vala
4 * (C) 2009 by Didier "Ptitjes" <ptitjes@free.fr>
5 * (C) 2009-2011 the freesmartphone.org team <smartphones-standards@linuxtogo.org>
13 public errordomain GeneratorError
{
19 public enum Synchrony
{
25 internal class GeneratedNamespace
{
26 public GeneratedNamespace parent
;
28 public Gee
.Map
<string, Xml
.Node
*> members
29 = new Gee
.HashMap
<string, Xml
.Node
*>(str_hash
, str_equal
, direct_equal
);
30 public Gee
.Map
<string, GeneratedNamespace
> namespaces
31 = new Gee
.HashMap
<string, GeneratedNamespace
>(str_hash
, str_equal
, direct_equal
);
34 public class BindingGenerator
: Object
{
36 private static Set
<string> registered_names
= new HashSet
<string>(str_hash
, str_equal
);
37 private static int verbosity
;
38 private static int errors
;
39 private static bool synced
;
40 private static bool gdbus
;
43 registered_names
.add("using");
44 registered_names
.add("namespace");
45 registered_names
.add("public");
46 registered_names
.add("private");
47 registered_names
.add("internal");
48 registered_names
.add("errordomain");
49 registered_names
.add("class");
50 registered_names
.add("struct");
51 registered_names
.add("new");
52 registered_names
.add("for");
53 registered_names
.add("while");
54 registered_names
.add("foreach");
55 registered_names
.add("switch");
56 registered_names
.add("case");
57 registered_names
.add("static");
58 registered_names
.add("unowned");
59 registered_names
.add("weak");
60 registered_names
.add("register");
61 registered_names
.add("message");
62 registered_names
.add("get_type");
63 registered_names
.add("dispose");
64 registered_names
.add("result");
67 public static void INFO(string msg
) {
69 stdout
.printf(@
"[INFO] $msg\n");
72 public static void DEBUG(string msg
) {
74 stdout
.printf(@
"[DEBUG] $msg\n");
77 public static void WARN(string msg
) {
78 stderr
.printf(@
"[WARN] $msg\n");
81 public static void ERROR(string msg
) {
82 stderr
.printf(@
"[ERROR] $msg\n");
86 public static int main(string[] args
) {
87 //FIXME: Convert to OptionEntry
88 string[] split_name
= args
[0].split("/");
89 string program_name
= split_name
[split_name
.length
- 1];
90 string command
= string.joinv(" ", args
);
92 string api_path
= null;
93 string output_directory
= null;
94 uint dbus_timeout
= 120000;
97 Map
<string,string> namespace_renaming
= new HashMap
<string,string>(str_hash
, str_equal
, str_equal
);
99 for (int i
= 1; i
< args
.length
; i
++) {
100 string arg
= args
[i
];
102 string[] split_arg
= arg
.split("=");
103 switch (split_arg
[0]) {
106 show_usage(program_name
);
115 api_path
= split_arg
[1];
119 output_directory
= split_arg
[1];
121 case "--strip-namespace":
122 namespace_renaming
.set(split_arg
[1], "");
124 case "--rename-namespace":
125 string[] ns_split
= split_arg
[1].split(":");
126 namespace_renaming
.set(ns_split
[0], ns_split
[1]);
128 case "--dbus-timeout":
129 dbus_timeout
= int.parse( split_arg
[1] );
138 stdout
.printf("%s: Unknown option %s\n", program_name
, arg
);
139 show_usage(program_name
);
144 if (api_path
== null)
146 if (output_directory
== null)
147 output_directory
= ".";
150 generate(api_path
, output_directory
, namespace_renaming
, command
, dbus_timeout
);
151 } catch (GLib
.FileError ex
) {
154 } catch (GeneratorError ex
) {
160 stdout
.printf( @
"\n$errors errors detected in API files. The generated files will not be usable.\n" );
166 private static void show_version() {
167 stdout
.printf(@
"Vala D-Bus Binding Tool $(Config.PACKAGE_VERSION)\n");
168 stdout
.printf("Written by Didier \"Ptitjes\" and the freesmartphone.org team\n");
169 stdout
.printf("This is free software; see the source for copying conditions.\n");
170 stdout
.printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
173 private static void show_usage(string program_name
) {
174 stdout
.printf("Usage:\n");
175 stdout
.printf(" %s [-v] [--version] [--help]\n", program_name
);
176 stdout
.printf(" %s [--gdbus] [--api-path=PATH] [--no-synced] [--dbus-timeout=TIMEOUT] [--directory=DIR] [--strip-namespace=NS]* [--rename-namespace=OLD_NS:NEW_NS]*\n", program_name
);
179 public static void generate(string api_path
, string output_directory
,
180 Map
<string,string> namespace_renaming
, string command
, uint dbus_timeout
)
181 throws GeneratorError
, GLib
.FileError
{
185 BindingGenerator generator
= new
BindingGenerator(output_directory
, namespace_renaming
, command
, dbus_timeout
);
186 generator
.generate_bindings(api_path
);
191 private BindingGenerator(string output_directory
, Map
<string,string> namespace_renaming
, string command
, uint dbus_timeout
) {
192 this
.output_directory
= output_directory
;
193 this
.namespace_renaming
= namespace_renaming
;
194 this
.command
= command
;
195 this
.dbus_timeout
= dbus_timeout
;
198 private string output_directory
;
199 private Map
<string,string> namespace_renaming
;
200 private string command
;
201 private uint dbus_timeout
;
202 private bool inner_interface_strategy_concat
= true;
204 private static const string FSO_NAMESPACE
= "http://www.freesmartphone.org/schemas/DBusSpecExtension";
206 private static const string INTERFACE_ELTNAME
= "interface";
207 private static const string METHOD_ELTNAME
= "method";
208 private static const string SIGNAL_ELTNAME
= "signal";
209 private static const string PROPERTY_ELTNAME
= "property";
210 private static const string ARG_ELTNAME
= "arg";
211 private static const string NAME_ATTRNAME
= "name";
212 private static const string TYPE_ATTRNAME
= "type";
213 private static const string DIRECTION_ATTRNAME
= "direction";
214 private static const string REPLACED_BY_ATTRNAME
= "replaced-by";
215 private static const string IN_ATTRVALUE
= "in";
216 private static const string OUT_ATTRVALUE
= "out";
217 private static const string ENUMERATION_ELTNAME
= "enumeration";
218 private static const string MEMBER_ELTNAME
= "member";
219 private static const string VALUE_ATTRNAME
= "value";
220 private static const string ERRORDOMAIN_ELTNAME
= "errordomain";
221 private static const string ERROR_ELTNAME
= "error";
222 private static const string NO_CONTAINER_ATTRNAME
= "no-container";
223 private static const string THROWS_ELTNAME
= "throws";
224 private static const string STRUCT_ELTNAME
= "struct";
225 private static const string FIELD_ELTNAME
= "field";
226 private static const string ANNOTATION_ELTNAME
= "annotation";
227 private static const string DEPRECATED_ELTNAME
= "deprecated";
229 private void generate_bindings(string api_path
)
230 throws GeneratorError
, GLib
.FileError
{
232 if (api_path
.has_suffix(".xml")) {
233 add_api_file(api_path
);
235 GLib
.Dir dir
= GLib
.Dir
.open(api_path
);
237 while ((name
= dir
.read_name()) != null) {
238 if (name
.has_suffix(".xml")) {
239 add_api_file(Path
.build_filename(api_path
, name
));
244 index_names(root_namespace
);
245 generate_namespace(root_namespace
);
248 private void add_api_file(string api_file
) throws GeneratorError
{
249 INFO(@
"Adding API file $api_file");
250 // Parse the API document from path
251 Xml
.Doc
* api_doc
= Parser
.parse_file(api_file
);
252 if (api_doc
== null) {
253 throw new GeneratorError
.FILE_NOT_FOUND(api_file
);
256 api_docs
.add(api_doc
);
258 preprocess_binding_names(api_doc
);
261 private FileStream output
;
263 private void create_binding_file(string name
) throws GeneratorError
{
264 output
= FileStream
.open(name
, "w");
265 if (output
== null) {
266 throw new GeneratorError
.CANT_CREATE_FILE(name
);
269 output
.printf(@
"/* Generated by vala-dbus-binding-tool $(Config.PACKAGE_VERSION). Do not modify! */\n");
270 output
.printf(@
"/* Generated with: $command */\n");
272 output
.printf("using DBus;\n");
273 output
.printf("using GLib;\n");
276 private Gee
.List
<Xml
.Doc
*> api_docs
= new Gee
.ArrayList
<Xml
.Doc
*>();
278 private void preprocess_binding_names(Xml
.Doc
* api_doc
) {
279 for (Xml
.Node
* iter
= api_doc
->get_root_element()->children
; iter
!= null; iter
= iter
->next
) {
280 //FIXME: Use $(iter->type) when enum to_string works
281 DEBUG(@
" Processing $(iter->name) as type %d".printf(iter
->type
));
282 if (iter
->type
!= ElementType
.ELEMENT_NODE
) {
283 DEBUG(@
" not a node; continuing");
287 if (iter
->name
!= INTERFACE_ELTNAME
288 && iter
->name
!= ENUMERATION_ELTNAME
289 && iter
->name
!= ERRORDOMAIN_ELTNAME
290 && iter
->name
!= STRUCT_ELTNAME
) {
291 DEBUG(@
" not interface or enumeration or errordomain or struct; continuing");
295 string no_error_container_string
= iter
->get_ns_prop(NO_CONTAINER_ATTRNAME
, FSO_NAMESPACE
);
296 bool no_error_container
= (no_error_container_string
!= null && no_error_container_string
== "true");
298 string dbus_interface_name
= iter
->get_prop(NAME_ATTRNAME
);
299 string[] split_name
= dbus_interface_name
.split(".");
302 if (iter
->name
== ERRORDOMAIN_ELTNAME
&& no_error_container
) {
303 short_name
= "Error";
304 last_part
= split_name
.length
;
306 short_name
= split_name
[split_name
.length
- 1];
307 last_part
= split_name
.length
- 1;
310 // Removing stripped root namespaces
312 for (; i
< last_part
; i
++) {
313 string part
= split_name
[i
];
314 if (namespace_renaming
.get(part
) != "") break;
317 // Traversing inner namespaces
318 GeneratedNamespace ns
= root_namespace
;
319 for (; i
< last_part
; i
++) {
320 string part
= split_name
[i
];
322 if (namespace_renaming
.has_key(part
) && namespace_renaming
.get(part
) != "") {
323 part
= namespace_renaming
.get(part
);
326 if (ns
.members
.has_key(part
) && inner_interface_strategy_concat
) {
327 if (ns
.namespaces
.has_key(part
)) {
328 GeneratedNamespace child
= ns
.namespaces
.get(part
);
329 foreach (string interf_name
in child
.members
.keys
) {
330 Xml
.Node
* interf
= child
.members
.get(interf_name
);
331 ns
.members
.set(part
+ interf_name
, interf
);
333 ns
.namespaces
.unset(part
);
340 GeneratedNamespace child
= null;
341 if (ns
.namespaces
.has_key(part
)) {
342 child
= ns
.namespaces
.get(part
);
344 child
= new
GeneratedNamespace();
347 ns
.namespaces
.set(part
, child
);
350 if (ns
.members
.has_key(part
)) {
351 child
.members
.set(part
, ns
.members
.get(part
));
352 ns
.members
.unset(part
);
358 string interface_name
= null;
359 if (inner_interface_strategy_concat
) {
360 StringBuilder name_builder
= new
StringBuilder();
361 // Concatenating last inner namespaces
362 for (; i
< last_part
; i
++) {
363 name_builder
.append(split_name
[i
]);
365 name_builder
.append(short_name
);
366 interface_name
= name_builder
.str
;
368 if (ns
.namespaces
.has_key(short_name
)) {
369 GeneratedNamespace child
= ns
.namespaces
.get(short_name
);
370 foreach (string interf_name
in child
.members
.keys
) {
371 Xml
.Node
* interf
= child
.members
.get(interf_name
);
372 ns
.members
.set(short_name
+ interf_name
, interf
);
374 ns
.namespaces
.unset(short_name
);
378 if (ns
.namespaces
.has_key(short_name
)) {
379 ns
= ns
.namespaces
.get(short_name
);
381 interface_name
= short_name
;
384 if (!ns
.members
.has_key(interface_name
)) {
385 ns
.members
.set(interface_name
, iter
);
387 Xml
.Node
* iter2
= ns
.members
.get(interface_name
);
388 var name
= iter2
->get_prop(NAME_ATTRNAME
);
389 ERROR(@
"$interface_name has been added already as namespace $name");
394 private void index_names(GeneratedNamespace ns
) {
395 if (ns
.members
.size
> 0) {
396 string namespace_name
= string.joinv(".", get_namespace_path(ns
));
398 foreach (string name
in ns
.members
.keys
) {
399 Xml
.Node
* api
= ns
.members
.get(name
);
400 string dbus_name
= api
->get_prop(NAME_ATTRNAME
);
401 if (api
->name
== ERRORDOMAIN_ELTNAME
) {
402 INFO(@
"Registering new errordomain $dbus_name");
403 error_name_index
.set(dbus_name
, namespace_name
+ "." + name
);
405 name_index
.set(dbus_name
, namespace_name
+ "." + name
);
410 foreach (string name
in ns
.namespaces
.keys
) {
411 GeneratedNamespace child
= ns
.namespaces
.get(name
);
417 private string[] get_namespace_path(GeneratedNamespace ns
) {
418 string[] reversed_namespace_names
= new
string[0];
419 GeneratedNamespace a_namespace
= ns
;
420 while (a_namespace
.name
!= null) {
421 reversed_namespace_names
+= a_namespace
.name
;
422 a_namespace
= a_namespace
.parent
;
425 string[] namespace_names
= new
string[0];
426 for (int i
= reversed_namespace_names
.length
- 1; i
>= 0; i
--) {
427 namespace_names
+= reversed_namespace_names
[i
];
430 return namespace_names
;
433 private GeneratedNamespace root_namespace
= new
GeneratedNamespace();
435 private Map
<string, string> name_index
= new HashMap
<string, string>(str_hash
, str_equal
, str_equal
);
436 private Map
<string, string> error_name_index
= new HashMap
<string, string>(str_hash
, str_equal
, str_equal
);
438 private void generate_namespace(GeneratedNamespace ns
)
439 throws GeneratorError
{
440 if (ns
.members
.size
> 0) {
441 string[] namespace_names
= get_namespace_path(ns
);
443 create_binding_file(output_directory
+ "/" + string.joinv("-", namespace_names
).down() + ".vala");
445 foreach (string name
in namespace_names
) {
447 output
.printf("%snamespace %s {\n", get_indent(), name
);
451 foreach (string name
in ns
.members
.keys
) {
452 Xml
.Node
* api
= ns
.members
.get(name
);
455 case INTERFACE_ELTNAME
:
456 generate_interface(name
, api
, Synchrony
.AUTO
);
457 generate_proxy_getter(api
, name
);
459 generate_interface(name
, api
, Synchrony
.FORCE_SYNC
);
460 generate_proxy_getter(api
, name
, Synchrony
.FORCE_SYNC
);
462 case ENUMERATION_ELTNAME
:
463 generate_enumeration(name
, api
);
465 case ERRORDOMAIN_ELTNAME
:
466 generate_errordomain(name
, api
);
469 generate_explicit_struct(name
, api
);
474 foreach (string name
in namespace_names
) {
476 output
.printf("%s}\n", get_indent());
482 foreach (string name
in ns
.namespaces
.keys
) {
483 GeneratedNamespace child
= ns
.namespaces
.get(name
);
485 generate_namespace(child
);
489 private Gee
.Map
<string, string> structs_to_generate
490 = new Gee
.HashMap
<string, string>(str_hash
, str_equal
, str_equal
);
492 private void generate_interface(string interface_name
, Xml
.Node
* node
, Synchrony synchrony
= Synchrony
.AUTO
)
493 throws GeneratorError
{
494 string dbus_name
= node
->get_prop(NAME_ATTRNAME
);
495 string namespace_name
= get_namespace_name(interface_name
);
497 assert( synchrony
!= Synchrony
.FORCE_ASYNC
); // not supported yet, maybe never
499 var iface_name
= ( synchrony
== Synchrony
.FORCE_SYNC
) ? interface_name
+ "Sync" : interface_name
;
501 INFO(@
"Generating interface $dbus_name");
504 output
.printf("%s[DBus (name = \"%s\", timeout = %u)]\n", get_indent(), dbus_name
, dbus_timeout
);
505 output
.printf("%spublic interface %s : GLib.Object {\n", get_indent(), iface_name
);
508 generate_members(node
, iface_name
, get_namespace_name(dbus_name
), synchrony
);
511 output
.printf("%s}\n", get_indent());
513 if (structs_to_generate
.size
!= 0) {
514 foreach (string name
in structs_to_generate
.keys
) {
515 generate_struct(name
, structs_to_generate
.get(name
), namespace_name
);
517 structs_to_generate
.clear();
521 private void generate_enumeration(string enumeration_name
, Xml
.Node
* node
) throws GeneratorError
{
522 string dbus_name
= node
->get_prop(NAME_ATTRNAME
);
523 string type
= node
->get_prop(TYPE_ATTRNAME
);
524 bool string_enum
= type
== "s";
526 INFO(@
"Generating enumeration $type for $dbus_name");
529 output
.printf("%s[DBus%s]\n", get_indent(), string_enum ?
" (use_string_marshalling = true)" : "");
530 output
.printf("%spublic enum %s {\n", get_indent(), enumeration_name
);
533 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
534 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
537 switch (iter
->name
) {
539 string member_name
= normalized_to_upper_case(iter
->get_prop(NAME_ATTRNAME
));
540 string member_value
= iter
->get_prop(VALUE_ATTRNAME
);
542 output
.printf("%s[DBus (value=\"%s\")]\n", get_indent(), member_value
);
544 output
.printf("%s%s%s%s\n", get_indent(), member_name
, string_enum ?
"" : " = %s".printf(member_value
), iter
->next
== null ?
"" : ",");
550 output
.printf("%s}\n", get_indent());
553 private void generate_errordomain(string errordomain_name
, Xml
.Node
* node
)
554 throws GeneratorError
{
555 string dbus_name
= node
->get_prop(NAME_ATTRNAME
);
557 INFO(@
"Generating errordomain $errordomain_name for $dbus_name");
560 output
.printf("%s[DBus (name = \"%s\")]\n", get_indent(), dbus_name
);
561 output
.printf("%spublic errordomain %s {\n", get_indent(), errordomain_name
);
564 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
565 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
568 switch (iter
->name
) {
570 string dbus_error_name
= iter
->get_prop(NAME_ATTRNAME
);
571 string error_name
= camel_case_to_upper_case(dbus_error_name
);
573 output
.printf("%s[DBus (name = \"%s\")]\n", get_indent(), dbus_error_name
);
574 output
.printf("%s%s%s\n", get_indent(), error_name
, iter
->next
== null ?
"" : ",");
580 output
.printf("%s}\n", get_indent());
583 private void generate_explicit_struct(string struct_name
, Xml
.Node
* node
)
584 throws GeneratorError
{
585 string dbus_name
= node
->get_prop(NAME_ATTRNAME
);
587 INFO(@
"Generating explicit struct $struct_name for $dbus_name");
590 output
.printf("%spublic struct %s {\n", get_indent(), struct_name
);
593 string ctor_signature
= "%spublic %s (".printf(get_indent(), struct_name
);
594 string ctor_body
= "";
596 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
597 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
600 switch (iter
->name
) {
602 string field_name
= transform_registered_name(iter
->get_prop(NAME_ATTRNAME
));
603 string field_type
= "unknown";
605 field_type
= translate_type(iter
->get_prop(TYPE_ATTRNAME
),
606 iter
->get_ns_prop(TYPE_ATTRNAME
, FSO_NAMESPACE
),
607 struct_name
, get_namespace_name(dbus_name
));
608 } catch (GeneratorError
.UNKNOWN_DBUS_TYPE ex
) {
609 ERROR(@
"In struct $struct_name field $field_name : Unknown dbus type $(ex.message)");
612 output
.printf("%spublic %s %s;\n", get_indent(), field_type
, field_name
);
613 ctor_signature
+= "%s %s, ".printf(field_type
, field_name
);
614 ctor_body
+= "%sthis.%s = %s;\n".printf(get_indent(+1), field_name
, field_name
);
618 string constructor
= "%s ) {\n%s%s}".printf( ctor_signature
.substring( 0, ctor_signature
.length
-2 ), ctor_body
, get_indent() );
620 output
.printf("\n%s\n", constructor
);
623 INFO(@
"Generating from_variant method for $struct_name");
624 output
.printf("\n%spublic static %s from_variant (Variant v) {\n", get_indent(), struct_name
);
626 output
.printf("%sreturn v as %s;\n", get_indent(), struct_name
);
628 output
.printf("%s}\n", get_indent());
631 output
.printf("%s}", get_indent());
634 private void generate_struct(string name
, string content_signature
, string dbus_namespace
)
635 throws GeneratorError
{
636 INFO(@
"Generating struct $name w/ signature $content_signature in dbus namespace $dbus_namespace");
639 output
.printf("%spublic struct %s {\n", get_indent(), name
);
642 int attribute_number
= 1;
643 string signature
= content_signature
;
645 while (signature
!= "") {
646 string type
= parse_type(signature
, out tail
, "", dbus_namespace
);
647 output
.printf("%spublic %s attr%d;\n", get_indent(), type
, attribute_number
);
653 output
.printf("%s}\n", get_indent());
656 private void generate_members(Xml
.Node
* node
, string interface_name
, string dbus_namespace
, Synchrony synchrony
)
657 throws GeneratorError
{
658 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
659 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
662 switch (iter
->name
) {
664 generate_method(iter
, interface_name
, dbus_namespace
, synchrony
);
667 generate_signal(iter
, interface_name
, dbus_namespace
);
669 case PROPERTY_ELTNAME
:
670 generate_property(iter
, interface_name
, dbus_namespace
);
673 generate_error(iter
, interface_name
);
679 private void generate_method(Xml
.Node
* node
, string interface_name
, string dbus_namespace
, Synchrony synchrony
)
680 throws GeneratorError
{
682 string realname
= node
->get_prop(NAME_ATTRNAME
);
683 string name
= transform_registered_name(uncapitalize(node
->get_prop(NAME_ATTRNAME
)));
685 INFO(@
" Generating method $name (originally $realname) for $interface_name");
687 int unknown_param_count
= 0;
689 int out_param_count
= get_out_parameter_count(node
);
691 bool first_param
= true;
692 bool first_error
= true;
693 StringBuilder args_builder
= new
StringBuilder();
694 StringBuilder throws_builder
= new
StringBuilder();
695 string return_value_type
= "void";
696 bool async_method
= false;
697 bool noreply_method
= false;
698 bool deprecated_method
= false;
699 string deprecated_method_replaced_by
= "";
701 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
702 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
705 switch (iter
->name
) {
707 string? param_name
= transform_registered_name(iter
->get_prop(NAME_ATTRNAME
));
708 if(param_name
== null || param_name
== "") {
709 param_name
= "param%i".printf(unknown_param_count
);
710 unknown_param_count
++;
712 string param_type
= "unknown";
714 param_type
= translate_type(iter
->get_prop(TYPE_ATTRNAME
),
715 iter
->get_ns_prop(TYPE_ATTRNAME
, FSO_NAMESPACE
),
716 get_struct_name(interface_name
, param_name
),
718 } catch (GeneratorError
.UNKNOWN_DBUS_TYPE ex
) {
719 ERROR(@
"In interface $interface_name method $name : Unknown dbus type $(ex.message)");
721 string? param_dir
= iter
->get_prop(DIRECTION_ATTRNAME
);
725 if (param_type
== null) {
728 if (out_param_count
!= 1) {
730 args_builder
.append(", ");
733 args_builder
.append("out ");
734 args_builder
.append(param_type
);
735 args_builder
.append(" ");
736 args_builder
.append(param_name
);
739 return_value_type
= param_type
;
745 args_builder
.append(", ");
748 args_builder
.append(param_type
);
749 args_builder
.append(" ");
750 args_builder
.append(param_name
);
756 string errordomain_name
= null;
757 string fso_type
= iter
->get_prop(TYPE_ATTRNAME
);
758 if (fso_type
!= null) {
759 errordomain_name
= error_name_index
.get(fso_type
);
761 if (errordomain_name
== null) {
762 ERROR(@
"In interface $interface_name method $name : Unknown dbus error $(fso_type)");
763 errordomain_name
= "<unknown>";
767 throws_builder
.append(", ");
769 throws_builder
.append(errordomain_name
);
772 case ANNOTATION_ELTNAME
:
773 string annotation_name
= iter
->get_prop(NAME_ATTRNAME
);
774 if (annotation_name
== "org.freedesktop.DBus.GLib.Async") {
777 if (annotation_name
== "org.freedesktop.DBus.GLib.NoReply") {
778 noreply_method
= true;
781 case DEPRECATED_ELTNAME
:
782 deprecated_method
= true;
783 deprecated_method_replaced_by
= iter
->get_prop(REPLACED_BY_ATTRNAME
);
788 if (async_method
&& noreply_method
) {
789 WARN(@
"In interface $interface_name method $name : Requested both async and noreply; which is not supported by Vala. Will force sync.");
790 async_method
= false;
793 if (noreply_method
&& out_param_count
> 0) {
794 ERROR(@
"In interface $interface_name method $name : noreply methods are not allowed to have out parameters!");
798 throws_builder
.append(", ");
800 throws_builder
.append( gdbus ?
"DBusError, IOError" : "DBus.Error");
804 case Synchrony
.FORCE_SYNC
:
805 async_method
= false;
807 case Synchrony
.FORCE_ASYNC
:
811 /* AUTO, leave it like it is */
816 if (noreply_method
) {
817 output
.printf("%s[DBus (name = \"%s\", no_reply = true)]\n", get_indent(), realname
);
819 output
.printf("%s[DBus (name = \"%s\")]\n", get_indent(), realname
);
821 if (deprecated_method
) {
822 if (deprecated_method_replaced_by
.length
== 0)
823 output
.printf("[Deprecated]\n");
824 else output
.printf("[Deprecated (replacement = \"%s\")]".printf(deprecated_method_replaced_by
));
826 output
.printf("%spublic abstract%s %s %s(%s) throws %s;\n",
827 get_indent(), (async_method ?
" async" : ""), return_value_type
, name
, args_builder
.str
, throws_builder
.str
);
830 private int get_out_parameter_count(Xml
.Node
* node
) {
831 int out_param_count
= 0;
832 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
833 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
835 if (iter
->name
!= ARG_ELTNAME
)
837 if (iter
->get_prop(DIRECTION_ATTRNAME
) != OUT_ATTRVALUE
)
842 return out_param_count
;
845 private void generate_signal(Xml
.Node
* node
, string interface_name
, string dbus_namespace
)
846 throws GeneratorError
{
847 string realname
= node
->get_prop(NAME_ATTRNAME
);
848 string name
= transform_registered_name(uncapitalize(node
->get_prop(NAME_ATTRNAME
)));
850 INFO(@
" Generating signal $name (originally $realname) for $interface_name");
852 int unknown_param_count
= 0;
854 bool first_param
= true;
855 StringBuilder args_builder
= new
StringBuilder();
856 for (Xml
.Node
* iter
= node
->children
; iter
!= null; iter
= iter
->next
) {
857 if (iter
->type
!= ElementType
.ELEMENT_NODE
)
860 if (iter
->name
!= ARG_ELTNAME
)
863 string param_name
= transform_registered_name(iter
->get_prop(NAME_ATTRNAME
));
864 if(param_name
== null || param_name
== "") {
865 param_name
= "param%i".printf(unknown_param_count
);
866 unknown_param_count
++;
868 string param_type
= "unknown";
870 param_type
= translate_type(iter
->get_prop(TYPE_ATTRNAME
),
871 iter
->get_ns_prop(TYPE_ATTRNAME
, FSO_NAMESPACE
),
872 interface_name
+ capitalize(param_name
),
874 } catch (GeneratorError
.UNKNOWN_DBUS_TYPE ex
) {
875 ERROR(@
"In interface $interface_name signal $name : Unknown dbus type $(ex.message)");
879 args_builder
.append(", ");
882 args_builder
.append(param_type
);
883 args_builder
.append(" ");
884 args_builder
.append(param_name
);
889 output
.printf("%s[DBus (name = \"%s\")]\n", get_indent(), realname
);
890 output
.printf("%spublic signal void %s(%s);\n",
891 get_indent(), name
, args_builder
.str
);
894 private void generate_property(Xml
.Node
* node
, string interface_name
, string dbus_namespace
)
896 string realname
= node
->get_prop(NAME_ATTRNAME
);
897 string name
= transform_registered_name(uncapitalize(node
->get_prop(NAME_ATTRNAME
)));
899 string typename
= "unknown";
902 rawtype
= node
->get_prop(TYPE_ATTRNAME
);
903 typename
= translate_type(rawtype
,
904 node
->get_ns_prop(TYPE_ATTRNAME
, FSO_NAMESPACE
),
905 interface_name
+ capitalize(name
),
907 } catch (GeneratorError
.UNKNOWN_DBUS_TYPE ex
) {
908 ERROR(@
"In interface $interface_name property $name : Unknown dbus type $(ex.message)");
911 string accesstype
= "readwrite";
912 if (node
->has_prop("access") != null) {
913 accesstype
= node
->get_prop("access");
914 if (accesstype
!= "readwrite" && accesstype
!= "readonly") {
915 ERROR(@
"In interface $interface_name property $name : Unknown access type: $accesstype");
919 INFO(@
" Generating property $name (originally $realname) of type $typename for $interface_name");
921 string owned_specifier
= is_simple_type(rawtype
) ?
"" : "owned";
922 string accessimpl
= (accesstype
== "readonly") ? @
"$owned_specifier get;" : @
"$owned_specifier get; set;";
925 output
.printf("%s[DBus (name = \"%s\")]\n", get_indent(), realname
);
926 output
.printf("%spublic abstract %s %s { %s }\n", get_indent(), typename
, name
, accessimpl
);
929 private void generate_error(Xml
.Node
* node
, string interface_name
)
930 throws GeneratorError
{
933 private void generate_proxy_getter(Xml
.Node
* node
, owned
string interface_name
, Synchrony synchrony
= Synchrony
.AUTO
)
934 throws GeneratorError
{
935 bool async_method
= true;
938 case Synchrony
.FORCE_SYNC
:
939 async_method
= false;
941 case Synchrony
.FORCE_ASYNC
:
945 /* AUTO, leave it like it is */
948 if ( !async_method
) {
949 interface_name
= interface_name
+ "Sync";
953 output
.printf(@
"\n$(get_indent())public $(interface_name) get_$(uncapitalize(interface_name))_proxy(DBus.Connection con, string busname, DBus.ObjectPath path) {");
955 output
.printf(@
"\n$(get_indent())return con.get_object(busname, path) as $(interface_name);");
957 output
.printf(@
"\n$(get_indent())}");
961 private string translate_type(string type
, string? fso_type
, string type_name
, string dbus_namespace
)
962 throws GeneratorError
{
964 if (fso_type
!= null) {
965 var vala_type
= name_index
.get(fso_type
);
966 if (vala_type
== null) {
967 throw new GeneratorError
.UNKNOWN_DBUS_TYPE(fso_type
);
969 return vala_type
+ (type
.has_prefix("a") ?
"[]" : "");
971 return parse_type(type
, out tail
, type_name
, dbus_namespace
).replace("][", ",");
974 private bool is_simple_type(string type
)
990 private string parse_type(string type
, out string tail
, string type_name
, string dbus_namespace
)
991 throws GeneratorError
{
992 tail
= type
.substring(1);
993 if (type
.has_prefix("y")) {
994 return "uint8"; // uchar only works since post-vala 0.8.0 (see c4cf64b6590e5cce21febf98b1f3ff935d921fd5)
995 } else if (type
.has_prefix("b")) {
997 } else if (type
.has_prefix("n") || type
.has_prefix("i")) {
999 } else if (type
.has_prefix("q") || type
.has_prefix("u")) {
1001 } else if (type
.has_prefix("x")) {
1003 } else if (type
.has_prefix("t")) {
1005 } else if (type
.has_prefix("d")) {
1007 } else if (type
.has_prefix("s")) {
1009 } else if (type
.has_prefix("o")) {
1010 return gdbus ?
"GLib.ObjectPath" : "DBus.ObjectPath"; // needs to be prefixed post vala 0.9.2 (see 142ca8fe0e5b4b8058d4913e909ccc820b6f7768 and 9a650b7f3bb796c36e31a7c649c7f59e8292631e)
1011 } else if (type
.has_prefix("v")) {
1012 return gdbus ?
"GLib.Variant" : "GLib.Value";
1013 } else if (type
.has_prefix("a{") && type
.has_suffix("}")) {
1014 string tmp_type
= get_subsignature(type
, '{', '}', out tail
);
1015 string tail2
= null;
1016 string tail3
= null;
1018 StringBuilder vala_type
= new
StringBuilder();
1019 vala_type
.append("GLib.HashTable<");
1020 string foo
= parse_type(tmp_type
, out tail2
, plural_to_singular(type_name
) + "Key", dbus_namespace
);
1021 vala_type
.append(foo
);
1022 vala_type
.append(", ");
1024 string value_type
= parse_type(tail2
, out tail3
, plural_to_singular(type_name
), dbus_namespace
);
1025 if (value_type
== "GLib.Value") {
1028 vala_type
.append(value_type
);
1029 vala_type
.append(">");
1031 return vala_type
.str
;
1032 } else if (type
.has_prefix("a")) {
1033 string tail2
= null;
1034 return parse_type(tail
, out tail2
, plural_to_singular(type_name
), dbus_namespace
) + "[]";
1035 } else if (type
.has_prefix("(") && type
.has_suffix(")")) {
1036 string sub_type
= get_subsignature(type
, '(', ')', out tail
);
1038 string unique_type_name
= type_name
+"Struct";
1039 while (structs_to_generate
.has_key(unique_type_name
)) {
1040 unique_type_name
= "%s%d".printf(unique_type_name
, number
++);
1043 if (!name_index
.has_key(dbus_namespace
+ "." + unique_type_name
)) {
1044 structs_to_generate
.set(unique_type_name
, sub_type
);
1046 return unique_type_name
;
1048 throw new GeneratorError
.UNKNOWN_DBUS_TYPE(@
"dbustype: '$type' unknown");
1051 private string get_struct_name(string interface_name
, string param_name
) {
1052 string striped_interface_name
= strip_namespace(interface_name
);
1053 string name
= capitalize(param_name
);
1054 return name
.has_prefix(striped_interface_name
) ? name
: striped_interface_name
+ name
;
1057 private string get_namespace_name(string interface_name
) {
1058 long last_dot
= interface_name
.length
- 1;
1059 while (last_dot
>= 0 && interface_name
[last_dot
] != '.') {
1062 return interface_name
.substring(0, last_dot
);
1065 private string strip_namespace(string interface_name
) {
1066 long last_dot
= interface_name
.length
- 1;
1067 while (last_dot
>= 0 && interface_name
[last_dot
] != '.') {
1070 return interface_name
.substring(last_dot
+ 1, interface_name
.length
- last_dot
- 1);
1073 private string capitalize(string type_name
) {
1074 string[] parts
= type_name
.split("_");
1075 StringBuilder capitalized_name
= new
StringBuilder();
1076 foreach (string part
in parts
) {
1078 capitalized_name
.append(part
.substring(0, 1).up());
1079 capitalized_name
.append(part
.substring(1, part
.length
- 1));
1082 return capitalized_name
.str
;
1085 private string uncapitalize(string name
) {
1086 StringBuilder uncapitalized_name
= new
StringBuilder();
1087 for (int i
= 0; i
< name
.length
; i
++) {
1088 unichar c
= name
[i
];
1091 uncapitalized_name
.append_unichar('_');
1092 uncapitalized_name
.append_unichar(c
.tolower());
1094 uncapitalized_name
.append_unichar(c
);
1097 return transform_registered_name(uncapitalized_name
.str
);
1100 private string normalized_to_upper_case(string name
) {
1101 return name
.replace("-", "_").up();
1104 private string camel_case_to_upper_case(string name
) {
1105 return uncapitalize(name
).up();
1108 private string transform_registered_name(string? name
) {
1109 if (name
!= null && registered_names
.contains(name
)) {
1115 private string plural_to_singular(string type_name
) {
1116 if (type_name
.has_suffix("ies"))
1117 return type_name
.substring(0, type_name
.length
- 3) + "y";
1118 else if (type_name
.has_suffix("ses"))
1119 return type_name
.substring(0, type_name
.length
- 2);
1120 else if (type_name
.has_suffix("us"))
1122 else if (type_name
.has_suffix("i"))
1123 return type_name
.substring(0, type_name
.length
- 1) + "o";
1124 else if (type_name
.has_suffix("s"))
1125 return type_name
.substring(0, type_name
.length
- 1);
1126 else return type_name
;
1129 private int indentSize
= 0;
1131 private string get_indent(int offset
= 0) {
1132 return string.nfill(indentSize
+ offset
, '\t');
1135 private void update_indent(int increment
) {
1136 indentSize
+= increment
;
1139 private string get_subsignature( string s
, char start
, char end
, out string tail
) {
1140 unowned
char[] data
= (char[])s
;
1146 for(iter
= 0; iter
< s
.length
; iter
++) {
1149 if( counter
== 0 ) {
1161 tail
= s
.substring( iter
+ 1, -1 );
1162 var tmp
= s
.substring( begin
+ 1, iter
- begin
- 1);