Fix ellipsis parameter position in generated methods
[vala-lang.git] / codegen / valagirwriter.vala
blobe1899deec58061583eb6874cbe29095143410ab1
1 /* valagirwriter.vala
3 * Copyright (C) 2008-2010 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
25 /**
26 * Code visitor generating .gir file for the public interface.
28 public class Vala.GIRWriter : CodeVisitor {
29 private CodeContext context;
30 private string directory;
31 private string gir_namespace;
32 private string gir_version;
34 StringBuilder buffer = new StringBuilder();
35 FileStream stream;
36 Vala.HashSet<Namespace> unannotated_namespaces = new Vala.HashSet<Namespace>();
37 Vala.HashSet<Namespace> our_namespaces = new Vala.HashSet<Namespace>();
39 int indent;
41 private TypeSymbol gobject_type;
43 private struct GIRNamespace {
44 public GIRNamespace (string ns, string version) {
45 this.ns = ns; this.version = version;
47 public string ns;
48 public string version;
49 public bool equal (GIRNamespace g) {
50 return ((ns == g.ns) && (version == g.version));
54 private ArrayList<GIRNamespace?> externals = new ArrayList<GIRNamespace?> ((EqualFunc) GIRNamespace.equal);
56 public void write_includes() {
57 foreach (var i in externals) {
58 write_indent_stream ();
59 stream.printf ("<include name=\"%s\" version=\"%s\"/>\n", i.ns, i.version);
64 /**
65 * Writes the public interface of the specified code context into the
66 * specified file.
68 * @param context a code context
69 * @param filename a relative or absolute filename
71 public void write_file (CodeContext context, string directory, string gir_namespace, string gir_version, string package) {
72 this.context = context;
73 this.directory = directory;
74 this.gir_namespace = gir_namespace;
75 this.gir_version = gir_version;
77 var root_symbol = context.root;
78 var glib_ns = root_symbol.scope.lookup ("GLib");
79 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
81 write_package (package);
83 context.accept (this);
85 indent--;
86 buffer.append_printf ("</repository>\n");
88 string filename = "%s%c%s-%s.gir".printf (directory, Path.DIR_SEPARATOR, gir_namespace, gir_version);
89 stream = FileStream.open (filename, "w");
90 if (stream == null) {
91 Report.error (null, "unable to open `%s' for writing".printf (filename));
92 return;
95 stream.printf ("<?xml version=\"1.0\"?>\n");
97 stream.printf ("<repository version=\"1.1\"");
98 stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
99 stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
100 stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
101 stream.printf (">\n");
102 indent++;
104 write_includes();
105 indent--;
107 stream.puts (buffer.str);
108 stream = null;
110 foreach (var ns in unannotated_namespaces) {
111 if (!our_namespaces.contains(ns)) {
112 Report.warning (ns.source_reference, "Namespace %s does not have a GIR namespace and version annotation".printf (ns.name));
115 foreach (var ns in our_namespaces) {
116 ns.source_reference.file.gir_namespace = gir_namespace;
117 ns.source_reference.file.gir_version = gir_version;
121 private void write_package (string package) {
122 write_indent ();
123 buffer.append_printf ("<package name=\"%s\"/>\n", package);
126 private void write_c_includes (Namespace ns) {
127 // Collect C header filenames
128 Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
129 foreach (string c_header_filename in ns.get_cheader_filenames ()) {
130 header_filenames.add (c_header_filename);
132 foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
133 foreach (string c_header_filename in symbol.get_cheader_filenames ()) {
134 header_filenames.add (c_header_filename);
138 // Generate c:include tags
139 foreach (string c_header_filename in header_filenames) {
140 write_c_include (c_header_filename);
144 private void write_c_include (string name) {
145 write_indent ();
146 buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
149 public override void visit_namespace (Namespace ns) {
150 if (ns.external_package) {
151 return;
154 if (ns.name == null) {
155 // global namespace
156 ns.accept_children (this);
157 return;
160 if (ns.parent_symbol.name != null) {
161 // nested namespace
162 // not supported in GIR at the moment
163 return;
166 write_c_includes (ns);
168 write_indent ();
169 buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
170 string? cprefix = ns.get_cprefix ();
171 if (cprefix != null) {
172 buffer.append_printf (" c:prefix=\"%s\"", cprefix);
174 buffer.append_printf (">\n");
175 indent++;
177 write_annotations (ns);
179 ns.accept_children (this);
181 indent--;
182 write_indent ();
183 buffer.append_printf ("</namespace>\n");
184 our_namespaces.add(ns);
187 public override void visit_class (Class cl) {
188 if (cl.external_package) {
189 return;
192 if (!check_accessibility (cl)) {
193 return;
196 if (cl.is_subtype_of (gobject_type)) {
197 string gtype_struct_name = cl.name + "Class";
199 write_indent ();
200 buffer.append_printf ("<class name=\"%s\"", cl.name);
201 write_gtype_attributes (cl);
202 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
203 buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
204 if (cl.is_abstract) {
205 buffer.append_printf (" abstract=\"1\"");
207 buffer.append_printf (">\n");
208 indent++;
210 // write implemented interfaces
211 foreach (DataType base_type in cl.get_base_types ()) {
212 var object_type = (ObjectType) base_type;
213 if (object_type.type_symbol is Interface) {
214 write_indent ();
215 buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
219 write_annotations (cl);
221 write_indent ();
222 buffer.append_printf ("<field name=\"parent_instance\">\n");
223 indent++;
224 write_indent ();
225 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), cl.base_class.get_cname ());
226 indent--;
227 write_indent ();
228 buffer.append_printf("</field>\n");
230 write_indent ();
231 buffer.append_printf ("<field name=\"priv\">\n");
232 indent++;
233 write_indent ();
234 buffer.append_printf ("<type name=\"any\" c:type=\"%sPrivate*\"/>\n", cl.get_cname ());
235 indent--;
236 write_indent ();
237 buffer.append_printf("</field>\n");
239 cl.accept_children (this);
241 indent--;
242 write_indent ();
243 buffer.append_printf ("</class>\n");
245 write_indent ();
246 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
247 write_ctype_attributes (cl, "Class");
248 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", cl.name);
249 buffer.append_printf (">\n");
250 indent++;
252 write_indent ();
253 buffer.append_printf ("<field name=\"parent_class\">\n");
254 indent++;
255 write_indent ();
256 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), cl.base_class.get_cname ());
257 indent--;
258 write_indent ();
259 buffer.append_printf ("</field>\n");
261 foreach (Method m in cl.get_methods ()) {
262 if (m.is_abstract || m.is_virtual) {
263 write_indent ();
264 buffer.append_printf("<field name=\"%s\">\n", m.name);
265 indent++;
266 write_signature(m, "callback", true);
267 indent--;
268 write_indent ();
269 buffer.append_printf ("</field>\n");
273 foreach (Signal sig in cl.get_signals ()) {
274 if (sig.default_handler != null) {
275 write_indent ();
276 buffer.append_printf ("<field name=\"%s\">\n", sig.name);
277 indent++;
278 write_signature (sig.default_handler, "callback", true);
279 indent--;
280 write_indent ();
281 buffer.append_printf ("</field>\n");
286 indent--;
287 write_indent ();
288 buffer.append_printf ("</record>\n");
289 } else {
290 write_indent ();
291 buffer.append_printf ("<record name=\"%s\"", cl.name);
292 buffer.append_printf (">\n");
293 indent++;
295 write_annotations (cl);
297 cl.accept_children (this);
299 indent--;
300 write_indent ();
301 buffer.append_printf ("</record>\n");
305 public override void visit_struct (Struct st) {
306 if (st.external_package) {
307 return;
310 if (!check_accessibility (st)) {
311 return;
314 write_indent ();
315 buffer.append_printf ("<record name=\"%s\"", st.name);
316 buffer.append_printf (">\n");
317 indent++;
319 write_annotations (st);
321 st.accept_children (this);
323 indent--;
324 write_indent ();
325 buffer.append_printf ("</record>\n");
328 public override void visit_interface (Interface iface) {
329 if (iface.external_package) {
330 return;
333 if (!check_accessibility (iface)) {
334 return;
337 string gtype_struct_name = iface.name + "Iface";
339 write_indent ();
340 buffer.append_printf ("<interface name=\"%s\"", iface.name);
341 write_gtype_attributes (iface);
342 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
343 buffer.append_printf (">\n");
344 indent++;
346 // write prerequisites
347 if (iface.get_prerequisites ().size > 0) {
348 write_indent ();
349 buffer.append_printf ("<requires>\n");
350 indent++;
352 foreach (DataType base_type in iface.get_prerequisites ()) {
353 var object_type = (ObjectType) base_type;
354 if (object_type.type_symbol is Class) {
355 write_indent ();
356 buffer.append_printf ("<object name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
357 } else if (object_type.type_symbol is Interface) {
358 write_indent ();
359 buffer.append_printf ("<interface name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
360 } else {
361 assert_not_reached ();
365 indent--;
366 write_indent ();
367 buffer.append_printf ("</requires>\n");
370 write_annotations (iface);
372 iface.accept_children (this);
374 indent--;
375 write_indent ();
376 buffer.append_printf ("</interface>\n");
378 write_indent ();
379 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
380 write_ctype_attributes (iface, "Iface");
381 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", iface.name);
382 buffer.append_printf (">\n");
383 indent++;
385 foreach (Method m in iface.get_methods ()) {
386 if (m.is_abstract || m.is_virtual) {
387 write_signature(m, "callback", true);
391 indent--;
392 write_indent ();
393 buffer.append_printf ("</record>\n");
396 public override void visit_enum (Enum en) {
397 if (en.external_package) {
398 return;
401 if (!check_accessibility (en)) {
402 return;
405 write_indent ();
406 buffer.append_printf ("<enumeration name=\"%s\"", en.name);
407 write_gtype_attributes (en);
408 buffer.append_printf (">\n");
409 indent++;
411 write_annotations (en);
413 enum_value = 0;
414 en.accept_children (this);
416 indent--;
417 write_indent ();
418 buffer.append_printf ("</enumeration>\n");
421 private int enum_value;
423 public override void visit_enum_value (EnumValue ev) {
424 write_indent ();
425 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.down (), ev.get_cname ());
426 if (ev.value != null) {
427 string value = literal_expression_to_value_string (ev.value);
428 buffer.append_printf (" value=\"%s\"", value);
429 } else {
430 buffer.append_printf (" value=\"%d\"", enum_value++);
432 buffer.append_printf ("/>\n");
435 public override void visit_error_domain (ErrorDomain edomain) {
436 if (edomain.external_package) {
437 return;
440 if (!check_accessibility (edomain)) {
441 return;
444 write_indent ();
445 buffer.append_printf ("<errordomain name=\"%s\"", edomain.name);
446 buffer.append_printf (" get-quark=\"%squark\"", edomain.get_lower_case_cprefix ());
447 buffer.append_printf (" codes=\"%s\"", edomain.name);
448 buffer.append_printf (">\n");
450 write_annotations (edomain);
452 buffer.append_printf ("</errordomain>\n");
454 write_indent ();
455 buffer.append_printf ("<enumeration name=\"%s\"", edomain.name);
456 write_ctype_attributes (edomain);
457 buffer.append_printf (">\n");
458 indent++;
460 enum_value = 0;
461 edomain.accept_children (this);
463 indent--;
464 write_indent ();
465 buffer.append_printf ("</enumeration>\n");
468 public override void visit_error_code (ErrorCode ecode) {
469 write_indent ();
470 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.down (), ecode.get_cname ());
471 if (ecode.value != null) {
472 string value = literal_expression_to_value_string (ecode.value);
473 buffer.append_printf (" value=\"%s\"", value);
474 } else {
475 buffer.append_printf (" value=\"%d\"", enum_value++);
477 buffer.append_printf ("/>\n");
480 public override void visit_constant (Constant c) {
481 if (c.external_package) {
482 return;
485 if (!check_accessibility (c)) {
486 return;
489 //TODO Add better constant evaluation
490 var initializer = c.value;
491 string value = literal_expression_to_value_string (initializer);
493 write_indent ();
494 buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c.name, c.get_cname ());
495 buffer.append_printf (" value=\"%s\"", value);
496 buffer.append_printf (">\n");
497 indent++;
499 write_type (initializer.value_type);
501 indent--;
502 write_indent ();
503 buffer.append_printf ("</constant>\n");
506 public override void visit_field (Field f) {
507 if (f.external_package) {
508 return;
511 if (!check_accessibility (f)) {
512 return;
515 write_indent ();
516 buffer.append_printf ("<field name=\"%s\"", f.get_cname ());
517 if (f.variable_type.nullable) {
518 buffer.append_printf (" allow-none=\"1\"");
520 buffer.append_printf (">\n");
521 indent++;
523 write_annotations (f);
525 write_type (f.variable_type);
527 indent--;
528 write_indent ();
529 buffer.append_printf ("</field>\n");
532 private void write_implicit_params (DataType type, ref int index, bool has_array_length, string name, ParameterDirection direction) {
533 if (type is ArrayType && has_array_length) {
534 var int_type = new IntegerType (CodeContext.get ().root.scope.lookup ("int") as Struct);
535 write_param_or_return (int_type, "parameter", ref index, has_array_length, "%s_length1".printf (name), direction);
536 } else if (type is DelegateType) {
537 var data_type = new PointerType (new VoidType ());
538 write_param_or_return (data_type, "parameter", ref index, false, "%s_target".printf (name), direction);
539 if (type.value_owned) {
540 var notify_type = new DelegateType (CodeContext.get ().root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
541 write_param_or_return (notify_type, "parameter", ref index, false, "%s_target_destroy_notify".printf (name), direction);
546 private void write_params_and_return (List<FormalParameter> params, DataType? return_type, bool return_array_length, bool constructor = false, DataType? instance_type = null, bool user_data = false) {
547 int last_index = 0;
548 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType)) {
549 write_indent ();
550 buffer.append_printf ("<parameters>\n");
551 indent++;
552 int index = 1;
554 if (instance_type != null) {
555 write_param_or_return (instance_type, "parameter", ref index, false, "self");
558 foreach (FormalParameter param in params) {
559 write_param_or_return (param.variable_type, "parameter", ref index, !param.no_array_length, param.name, param.direction);
561 write_implicit_params (param.variable_type, ref index, !param.no_array_length, param.name, param.direction);
564 last_index = index - 1;
565 write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
567 if (user_data) {
568 write_indent ();
569 buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
570 indent++;
571 write_indent ();
572 buffer.append_printf ("<type name=\"any\" c:type=\"void*\"/>\n");
573 indent--;
574 write_indent ();
575 buffer.append_printf ("</parameter>\n");
578 indent--;
579 write_indent ();
580 buffer.append_printf ("</parameters>\n");
583 if (return_type != null) {
584 write_param_or_return (return_type, "return-value", ref last_index, return_array_length, null, ParameterDirection.IN, constructor);
588 public override void visit_delegate (Delegate cb) {
589 if (cb.external_package) {
590 return;
593 if (!check_accessibility (cb)) {
594 return;
597 write_indent ();
598 buffer.append_printf ("<callback name=\"%s\"", cb.name);
599 buffer.append_printf (" c:type=\"%s\"", cb.get_cname ());
600 if (cb.tree_can_fail) {
601 buffer.append_printf (" throws=\"1\"");
603 buffer.append_printf (">\n");
604 indent++;
606 write_annotations (cb);
608 write_params_and_return (cb.get_parameters (), cb.return_type, !cb.no_array_length, false, null, cb.has_target);
610 indent--;
611 write_indent ();
612 buffer.append_printf ("</callback>\n");
615 public override void visit_method (Method m) {
616 if (m.external_package) {
617 return;
620 // don't write interface implementation unless it's an abstract or virtual method
621 if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
622 return;
625 string tag_name = "method";
626 var parent = m.parent_symbol;
627 if (parent is Namespace || m.binding == MemberBinding.STATIC) {
628 tag_name = "function";
631 write_signature (m, tag_name);
633 if (m.is_abstract || m.is_virtual) {
634 write_signature (m, "virtual-method", false);
638 private void write_signature (Method m, string tag_name, bool instance = false) {
639 if (m.coroutine) {
640 string finish_name = m.name;
641 if (finish_name.has_suffix ("_async")) {
642 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
644 finish_name += "_finish";
645 do_write_signature (m, tag_name, instance, m.name, m.get_cname (), m.get_async_begin_parameters (), new VoidType (), false);
646 do_write_signature (m, tag_name, instance, finish_name, m.get_finish_cname (), m.get_async_end_parameters (), m.return_type, m.tree_can_fail);
647 } else {
648 do_write_signature (m, tag_name, instance, m.name, m.get_cname (), m.get_parameters (), m.return_type, m.tree_can_fail);
652 private void do_write_signature (Method m, string tag_name, bool instance, string name, string cname, List<Vala.FormalParameter> params, DataType return_type, bool can_fail) {
653 write_indent ();
654 buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
655 if (tag_name == "virtual-method") {
656 buffer.append_printf (" invoker=\"%s\"", name);
657 } else if (tag_name == "callback") {
658 /* this is only used for vfuncs */
659 buffer.append_printf (" c:type=\"%s\"", name);
660 } else {
661 buffer.append_printf (" c:identifier=\"%s\"", cname);
663 if (can_fail) {
664 buffer.append_printf (" throws=\"1\"");
666 buffer.append_printf (">\n");
667 indent++;
669 write_annotations (m);
671 DataType instance_type = null;
672 if (instance) {
673 instance_type = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
676 write_params_and_return (params, return_type, !m.no_array_length, false, instance_type);
678 indent--;
679 write_indent ();
680 buffer.append_printf ("</%s>\n", tag_name);
683 public override void visit_creation_method (CreationMethod m) {
684 if (m.external_package) {
685 return;
688 if (!check_accessibility (m)) {
689 return;
692 write_indent ();
694 if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
695 m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
696 buffer.append_printf ("<constructor name=\"new\" c:identifier=\"%s\"", m.get_cname ());
697 } else {
698 buffer.append_printf ("<constructor name=\"%s\" c:identifier=\"%s\"", m.name, m.get_cname ());
701 if (m.tree_can_fail) {
702 buffer.append_printf (" throws=\"1\"");
704 buffer.append_printf (">\n");
705 indent++;
707 write_annotations (m);
710 var datatype = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
711 write_params_and_return (m.get_parameters (), datatype, false, true);
713 indent--;
714 write_indent ();
715 buffer.append_printf ("</constructor>\n");
718 public override void visit_property (Property prop) {
719 if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
720 return;
723 write_indent ();
724 buffer.append_printf ("<property name=\"%s\"", prop.get_canonical_name ());
725 if (prop.get_accessor == null) {
726 buffer.append_printf (" readable=\"0\"");
728 if (prop.set_accessor != null) {
729 buffer.append_printf (" writable=\"1\"");
730 if (prop.set_accessor.construction) {
731 if (!prop.set_accessor.writable) {
732 buffer.append_printf (" construct-only=\"1\"");
733 } else {
734 buffer.append_printf (" construct=\"1\"");
738 buffer.append_printf (">\n");
739 indent++;
741 write_annotations (prop);
743 write_type (prop.property_type);
745 indent--;
746 write_indent ();
747 buffer.append_printf ("</property>\n");
750 public override void visit_signal (Signal sig) {
751 if (!check_accessibility (sig)) {
752 return;
755 write_indent ();
756 buffer.append_printf ("<glib:signal name=\"%s\"", sig.get_cname ());
757 buffer.append_printf (">\n");
758 indent++;
760 write_annotations (sig);
762 write_params_and_return (sig.get_parameters (), sig.return_type, false);
764 indent--;
765 write_indent ();
766 buffer.append_printf ("</glib:signal>\n");
769 private void write_indent () {
770 int i;
772 for (i = 0; i < indent; i++) {
773 buffer.append_c ('\t');
777 private void write_indent_stream () {
778 int i;
780 for (i = 0; i < indent; i++) {
781 stream.putc ('\t');
786 private void write_param_or_return (DataType type, string tag, ref int index, bool has_array_length, string? name = null, ParameterDirection direction = ParameterDirection.IN, bool constructor = false) {
787 write_indent ();
788 buffer.append_printf ("<%s", tag);
789 if (name != null) {
790 buffer.append_printf (" name=\"%s\"", name);
792 if (direction == ParameterDirection.REF) {
793 buffer.append_printf (" direction=\"inout\"");
794 } else if (direction == ParameterDirection.OUT) {
795 buffer.append_printf (" direction=\"out\"");
798 DelegateType delegate_type = type as DelegateType;
800 if ((type.value_owned && delegate_type == null) || constructor) {
801 buffer.append_printf (" transfer-ownership=\"full\"");
802 } else {
803 buffer.append_printf (" transfer-ownership=\"none\"");
805 if (type.nullable) {
806 buffer.append_printf (" allow-none=\"1\"");
809 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
810 buffer.append_printf (" closure=\"%i\"", index + 1);
811 if (type.value_owned) {
812 buffer.append_printf (" destroy=\"%i\"", index + 2);
816 buffer.append_printf (">\n");
817 indent++;
819 write_type (type, has_array_length ? index : -1);
821 indent--;
822 write_indent ();
823 buffer.append_printf ("</%s>\n", tag);
824 index++;
827 private void write_ctype_attributes (TypeSymbol symbol, string suffix = "") {
828 buffer.append_printf (" c:type=\"%s%s\"", symbol.get_cname (), suffix);
831 private void write_gtype_attributes (TypeSymbol symbol) {
832 write_ctype_attributes(symbol);
833 buffer.append_printf (" glib:type-name=\"%s\"", symbol.get_cname ());
834 buffer.append_printf (" glib:get-type=\"%sget_type\"", symbol.get_lower_case_cprefix ());
837 private void write_type (DataType type, int index = -1) {
838 if (type is ArrayType) {
839 var array_type = (ArrayType) type;
841 write_indent ();
842 buffer.append_printf ("<array");
843 if (array_type.fixed_length) {
844 buffer.append_printf (" fixed-length\"%i\"", array_type.length);
845 } else if (index != -1) {
846 buffer.append_printf (" length=\"%i\"", index + 1);
848 buffer.append_printf (">\n");
849 indent++;
851 write_type (array_type.element_type);
853 indent--;
854 write_indent ();
855 buffer.append_printf ("</array>\n");
856 } else if (type is VoidType) {
857 write_indent ();
858 buffer.append_printf ("<type name=\"none\"/>\n");
859 } else if (type is PointerType) {
860 write_indent ();
861 buffer.append_printf ("<type name=\"any\" c:type=\"%s\"/>\n", type.get_cname ());
862 } else if (type.data_type != null) {
863 write_indent ();
864 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"", gi_type_name (type.data_type), type.get_cname ());
866 List<DataType> type_arguments = type.get_type_arguments ();
867 if (type_arguments.size == 0) {
868 buffer.append_printf ("/>\n");
869 } else {
870 buffer.append_printf (">\n");
871 indent++;
873 foreach (DataType type_argument in type_arguments) {
874 write_type (type_argument);
877 indent--;
878 write_indent ();
879 buffer.append_printf ("</type>\n");
881 } else if (type is DelegateType) {
882 var deleg_type = (DelegateType) type;
883 write_indent ();
884 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (deleg_type.delegate_symbol), type.get_cname ());
885 } else if (type is GenericType) {
886 // generic type parameters not supported in GIR
887 write_indent ();
888 buffer.append ("<type name=\"any\" c:type=\"gpointer\"/>\n");
889 } else {
890 write_indent ();
891 buffer.append_printf ("<type name=\"%s\"/>\n", type.to_string ());
895 private void write_annotations (CodeNode node) {
896 foreach (Attribute attr in node.attributes) {
897 string name = camel_case_to_canonical (attr.name);
898 foreach (string arg_name in attr.args.get_keys ()) {
899 var arg = attr.args.get (arg_name);
901 string value = literal_expression_to_value_string ((Literal) arg);
903 if (value != null) {
904 write_indent ();
905 buffer.append_printf ("<annotation key=\"%s.%s\" value=\"%s\"/>\n",
906 name, camel_case_to_canonical (arg_name), value);
912 private string gi_type_name (TypeSymbol type_symbol) {
913 Symbol parent = type_symbol.parent_symbol;
914 if (parent is Namespace) {
915 Namespace ns = parent as Namespace;
916 if (ns.name != null) {
917 if (type_symbol.source_reference.file.gir_namespace != null) {
918 GIRNamespace external = GIRNamespace (type_symbol.source_reference.file.gir_namespace, type_symbol.source_reference.file.gir_version);
919 if (!externals.contains (external)) {
920 externals.add (external);
922 return "%s.%s".printf (type_symbol.source_reference.file.gir_namespace, type_symbol.name);
923 } else {
924 unannotated_namespaces.add(ns);
929 return vala_to_gi_type_name (type_symbol.get_full_name());
932 private string vala_to_gi_type_name (string name) {
933 if (name == "bool") {
934 return "boolean";
935 } else if (name == "string") {
936 return "utf8";
937 } else if (!name.contains (".")) {
938 return name;
939 } else {
940 string[] split_name = name.split (".");
942 StringBuilder type_name = new StringBuilder ();
943 type_name.append (split_name[0]);
944 type_name.append_unichar ('.');
945 for (int i = 1; i < split_name.length; i++) {
946 type_name.append (split_name[i]);
948 return type_name.str;
952 private string? literal_expression_to_value_string (Expression literal) {
953 if (literal is StringLiteral) {
954 var lit = literal as StringLiteral;
955 if (lit != null) {
956 return Markup.escape_text (lit.eval ());
958 } else if (literal is CharacterLiteral) {
959 return "%c".printf ((char) ((CharacterLiteral) literal).get_char ());
960 } else if (literal is BooleanLiteral) {
961 return ((BooleanLiteral) literal).value ? "true" : "false";
962 } else if (literal is RealLiteral) {
963 return ((RealLiteral) literal).value;
964 } else if (literal is IntegerLiteral) {
965 return ((IntegerLiteral) literal).value;
966 } else if (literal is UnaryExpression) {
967 var unary = (UnaryExpression) literal;
968 if (unary.operator == UnaryOperator.MINUS) {
969 if (unary.inner is RealLiteral) {
970 return "-" + ((RealLiteral) unary.inner).value;
971 } else if (unary.inner is IntegerLiteral) {
972 return "-" + ((IntegerLiteral) unary.inner).value;
976 return null;
979 private string camel_case_to_canonical (string name) {
980 string[] parts = Symbol.camel_case_to_lower_case (name).split ("_");
981 return string.joinv ("-", parts);
984 private bool check_accessibility (Symbol sym) {
985 if (sym.access == SymbolAccessibility.PUBLIC ||
986 sym.access == SymbolAccessibility.PROTECTED) {
987 return true;
990 return false;