78e34c9caddb2e8d05099ab548397bc18f17c5d9
[gnome.gobject-introspection] / giscanner / girwriter.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 # Copyright (C) 2008, 2009 Red Hat, Inc.
5 #
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2 of the License, or (at your option) any later version.
10 #
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the
18 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 # Boston, MA 02111-1307, USA.
20 #
21
22 from __future__ import with_statement
23
24 from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
25                   Function, Interface, List, Map, Member, Struct, Union,
26                   Varargs)
27 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
28                       GLibFlags, GLibObject, GLibInterface,
29                       GLibRecord)
30 from .xmlwriter import XMLWriter
31
32
33 class GIRWriter(XMLWriter):
34
35     def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
36         super(GIRWriter, self).__init__()
37         self.write_comment(
38 '''This file was automatically generated from C sources - DO NOT EDIT!
39 To affect the contents of this file, edit the original C definitions,
40 and/or use gtk-doc annotations. ''')
41         self._write_repository(namespace, shlibs, includes, pkgs,
42                                c_includes, cprefix)
43
44     def _write_repository(self, namespace, shlibs, includes=None,
45                           packages=None, c_includes=None, cprefix=None):
46         if includes is None:
47             includes = frozenset()
48         if packages is None:
49             packages = frozenset()
50         if c_includes is None:
51             c_includes = frozenset()
52         attrs = [
53             ('version', '1.0'),
54             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
55             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
56             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
57             ]
58         with self.tagcontext('repository', attrs):
59             for include in sorted(includes):
60                 self._write_include(include)
61             for pkg in sorted(set(packages)):
62                 self._write_pkgconfig_pkg(pkg)
63             for c_include in sorted(set(c_includes)):
64                 self._write_c_include(c_include)
65             self._write_namespace(namespace, shlibs, cprefix)
66
67     def _write_include(self, include):
68         attrs = [('name', include.name), ('version', include.version)]
69         self.write_tag('include', attrs)
70
71     def _write_pkgconfig_pkg(self, package):
72         attrs = [('name', package)]
73         self.write_tag('package', attrs)
74
75     def _write_c_include(self, c_include):
76         attrs = [('name', c_include)]
77         self.write_tag('c:include', attrs)
78
79     def _write_namespace(self, namespace, shlibs, cprefix):
80         attrs = [('name', namespace.name),
81                  ('version', namespace.version),
82                  ('shared-library', ','.join(shlibs)),
83                  ('c:prefix', cprefix)]
84         with self.tagcontext('namespace', attrs):
85             # We define a custom sorting function here because
86             # we want aliases to be first.  They're a bit
87             # special because the typelib compiler expands them.
88             def nscmp(a, b):
89                 if isinstance(a, Alias):
90                     if isinstance(b, Alias):
91                         return cmp(a.name, b.name)
92                     else:
93                         return -1
94                 elif isinstance(b, Alias):
95                     return 1
96                 else:
97                     return cmp(a, b)
98             for node in sorted(namespace.nodes, cmp=nscmp):
99                 if not node.skip:
100                     self._write_node(node)
101
102     def _write_node(self, node):
103         if isinstance(node, Function):
104             self._write_function(node)
105         elif isinstance(node, Enum):
106             self._write_enum(node)
107         elif isinstance(node, Bitfield):
108             self._write_bitfield(node)
109         elif isinstance(node, (Class, Interface)):
110             self._write_class(node)
111         elif isinstance(node, Callback):
112             self._write_callback(node)
113         elif isinstance(node, Struct):
114             self._write_record(node)
115         elif isinstance(node, Union):
116             self._write_union(node)
117         elif isinstance(node, GLibBoxed):
118             self._write_boxed(node)
119         elif isinstance(node, Member):
120             # FIXME: atk_misc_instance singleton
121             pass
122         elif isinstance(node, Alias):
123             self._write_alias(node)
124         elif isinstance(node, Constant):
125             self._write_constant(node)
126         else:
127             print 'WRITER: Unhandled node', node
128
129     def _append_version(self, node, attrs):
130         if node.version:
131             attrs.append(('version', node.version))
132
133     def _write_attributes(self, node):
134         for key, value in node.attributes:
135             self.write_tag('attribute', [('name', key), ('value', value)])
136
137     def _append_deprecated(self, node, attrs):
138         if node.deprecated:
139             attrs.append(('deprecated', node.deprecated))
140             if node.deprecated_version:
141                 attrs.append(('deprecated-version',
142                               node.deprecated_version))
143
144     def _append_throws(self, func, attrs):
145         if func.throws:
146             attrs.append(('throws', '1'))
147
148     def _write_alias(self, alias):
149         attrs = [('name', alias.name), ('target', alias.target)]
150         if alias.ctype is not None:
151             attrs.append(('c:type', alias.ctype))
152         self.write_tag('alias', attrs)
153
154     def _write_callable(self, callable, tag_name, extra_attrs):
155         if callable.skip:
156             return
157         attrs = [('name', callable.name)]
158         attrs.extend(extra_attrs)
159         if callable.doc:
160             attrs.append(('doc', callable.doc))
161         self._append_version(callable, attrs)
162         self._append_deprecated(callable, attrs)
163         self._append_throws(callable, attrs)
164         with self.tagcontext(tag_name, attrs):
165             self._write_attributes(callable)
166             self._write_return_type(callable.retval)
167             self._write_parameters(callable.parameters)
168
169     def _write_function(self, func, tag_name='function'):
170         attrs = [('c:identifier', func.symbol)]
171         self._write_callable(func, tag_name, attrs)
172
173     def _write_method(self, method):
174         self._write_function(method, tag_name='method')
175
176     def _write_static_method(self, method):
177         self._write_function(method, tag_name='function')
178
179     def _write_constructor(self, method):
180         self._write_function(method, tag_name='constructor')
181
182     def _write_return_type(self, return_):
183         if not return_:
184             return
185
186         assert return_.transfer is not None, return_
187
188         attrs = []
189         attrs.append(('transfer-ownership', return_.transfer))
190         if return_.doc:
191             attrs.append(('doc', return_.doc))
192         with self.tagcontext('return-value', attrs):
193             self._write_type(return_.type)
194
195     def _write_parameters(self, parameters):
196         if not parameters:
197             return
198         with self.tagcontext('parameters'):
199             for parameter in parameters:
200                 self._write_parameter(parameter)
201
202     def _write_parameter(self, parameter):
203         assert parameter.transfer is not None, parameter
204
205         attrs = []
206         if parameter.name is not None:
207             attrs.append(('name', parameter.name))
208         if parameter.direction != 'in':
209             attrs.append(('direction', parameter.direction))
210         attrs.append(('transfer-ownership',
211                      parameter.transfer))
212         if parameter.allow_none:
213             attrs.append(('allow-none', '1'))
214         if parameter.scope:
215             attrs.append(('scope', parameter.scope))
216         if parameter.closure_index >= 0:
217             attrs.append(('closure', '%d' % parameter.closure_index))
218         if parameter.destroy_index >= 0:
219             attrs.append(('destroy', '%d' % parameter.destroy_index))
220         if parameter.doc:
221             attrs.append(('doc', parameter.doc))
222         with self.tagcontext('parameter', attrs):
223             self._write_type(parameter.type)
224
225     def _type_to_string(self, ntype):
226         if isinstance(ntype, basestring):
227             return ntype
228         return ntype.name
229
230     def _write_type(self, ntype, relation=None):
231         if isinstance(ntype, basestring):
232             typename = ntype
233             type_cname = None
234         else:
235             typename = ntype.name
236             type_cname = ntype.ctype
237         if isinstance(ntype, Varargs):
238             with self.tagcontext('varargs', []):
239                 pass
240             return
241         if isinstance(ntype, Array):
242             attrs = []
243             if not ntype.zeroterminated:
244                 attrs.append(('zero-terminated', '0'))
245             if ntype.length_param_index >= 0:
246                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
247             attrs.append(('c:type', ntype.ctype))
248             if ntype.size is not None:
249                 attrs.append(('fixed-size', ntype.size))
250             with self.tagcontext('array', attrs):
251                 self._write_type(ntype.element_type)
252             return
253         attrs = [('name', self._type_to_string(ntype))]
254         # FIXME: figure out if type references a basic type
255         #        or a boxed/class/interface etc. and skip
256         #        writing the ctype if the latter.
257         if type_cname is not None:
258             attrs.append(('c:type', type_cname))
259         if isinstance(ntype, List) and ntype.element_type:
260             with self.tagcontext('type', attrs):
261                 self._write_type(ntype.element_type)
262             return
263         if isinstance(ntype, Map) and ntype.key_type:
264             with self.tagcontext('type', attrs):
265                 self._write_type(ntype.key_type)
266                 self._write_type(ntype.value_type)
267             return
268         # Not a special type, just write it out
269         self.write_tag('type', attrs)
270
271     def _write_enum(self, enum):
272         attrs = [('name', enum.name)]
273         if enum.doc:
274             attrs.append(('doc', enum.doc))
275         self._append_version(enum, attrs)
276         self._append_deprecated(enum, attrs)
277         if isinstance(enum, GLibEnum):
278             attrs.extend([('glib:type-name', enum.type_name),
279                           ('glib:get-type', enum.get_type),
280                           ('c:type', enum.ctype)])
281             if enum.error_quark:
282                 attrs.append(('glib:error-quark', enum.error_quark))
283         else:
284             attrs.append(('c:type', enum.symbol))
285
286         with self.tagcontext('enumeration', attrs):
287             self._write_attributes(enum)
288             for member in enum.members:
289                 self._write_member(member)
290
291     def _write_bitfield(self, bitfield):
292         attrs = [('name', bitfield.name)]
293         if bitfield.doc:
294             attrs.append(('doc', bitfield.doc))
295         self._append_version(bitfield, attrs)
296         self._append_deprecated(bitfield, attrs)
297         if isinstance(bitfield, GLibFlags):
298             attrs.extend([('glib:type-name', bitfield.type_name),
299                           ('glib:get-type', bitfield.get_type),
300                           ('c:type', bitfield.ctype)])
301         else:
302             attrs.append(('c:type', bitfield.symbol))
303         with self.tagcontext('bitfield', attrs):
304             self._write_attributes(bitfield)
305             for member in bitfield.members:
306                 self._write_member(member)
307
308     def _write_member(self, member):
309         if member.skip:
310             return
311         attrs = [('name', member.name),
312                  ('value', str(member.value)),
313                  ('c:identifier', member.symbol)]
314         if isinstance(member, GLibEnumMember):
315             attrs.append(('glib:nick', member.nick))
316         self.write_tag('member', attrs)
317
318     def _write_constant(self, constant):
319         attrs = [('name', constant.name),
320                  ('value', str(constant.value))]
321         with self.tagcontext('constant', attrs):
322             self._write_type(constant.type)
323
324     def _write_class(self, node):
325         attrs = [('name', node.name),
326                  ('c:type', node.ctype)]
327         if node.doc:
328             attrs.append(('doc', node.doc))
329         self._append_version(node, attrs)
330         self._append_deprecated(node, attrs)
331         if isinstance(node, Class):
332             tag_name = 'class'
333             if node.parent is not None:
334                 attrs.append(('parent', node.parent))
335             if node.is_abstract:
336                 attrs.append(('abstract', '1'))
337         else:
338             tag_name = 'interface'
339         if isinstance(node, (GLibObject, GLibInterface)):
340             attrs.append(('glib:type-name', node.type_name))
341             if node.get_type:
342                 attrs.append(('glib:get-type', node.get_type))
343             if node.glib_type_struct:
344                 attrs.append(('glib:type-struct', node.glib_type_struct.name))
345         with self.tagcontext(tag_name, attrs):
346             self._write_attributes(node)
347             if isinstance(node, GLibObject):
348                 for iface in node.interfaces:
349                     self.write_tag('implements', [('name', iface)])
350             if isinstance(node, Interface):
351                 for iface in node.prerequisites:
352                     self.write_tag('prerequisite', [('name', iface)])
353             if isinstance(node, Class):
354                 for method in node.constructors:
355                     self._write_constructor(method)
356                 for method in node.static_methods:
357                     self._write_static_method(method)
358             for vfunc in node.virtual_methods:
359                 self._write_vfunc(vfunc)
360             for method in node.methods:
361                 self._write_method(method)
362             for prop in node.properties:
363                 self._write_property(prop)
364             for field in node.fields:
365                 self._write_field(field)
366             for signal in node.signals:
367                 self._write_signal(signal)
368
369     def _write_boxed(self, boxed):
370         attrs = [('c:type', boxed.ctype),
371                  ('glib:name', boxed.name)]
372         if boxed.doc:
373             attrs.append(('doc', boxed.doc))
374         attrs.extend(self._boxed_attrs(boxed))
375         with self.tagcontext('glib:boxed', attrs):
376             self._write_attributes(boxed)
377             for method in boxed.constructors:
378                 self._write_constructor(method)
379             for method in boxed.methods:
380                 self._write_method(method)
381
382     def _write_property(self, prop):
383         if prop.skip:
384             return
385         attrs = [('name', prop.name)]
386         self._append_version(prop, attrs)
387         self._append_deprecated(prop, attrs)
388         # Properties are assumed to be readable (see also generate.c)
389         if not prop.readable:
390             attrs.append(('readable', '0'))
391         if prop.writable:
392             attrs.append(('writable', '1'))
393         if prop.construct:
394             attrs.append(('construct', '1'))
395         if prop.construct_only:
396             attrs.append(('construct-only', '1'))
397         if prop.doc:
398             attrs.append(('doc', prop.doc))
399         with self.tagcontext('property', attrs):
400             self._write_attributes(prop)
401             self._write_type(prop.type)
402
403     def _write_vfunc(self, vf):
404         attrs = []
405         if vf.invoker:
406             attrs.append(('invoker', vf.invoker))
407         self._write_callable(vf, 'virtual-method', attrs)
408
409     def _write_callback(self, callback):
410         attrs = [('c:type', callback.ctype)]
411         self._write_callable(callback, 'callback', attrs)
412
413     def _boxed_attrs(self, boxed):
414         return [('glib:type-name', boxed.type_name),
415                 ('glib:get-type', boxed.get_type)]
416
417     def _write_record(self, record, extra_attrs=[]):
418         is_gtype_struct = False
419         attrs = list(extra_attrs)
420         if record.name is not None:
421             attrs.append(('name', record.name))
422         if record.symbol is not None: # the record might be anonymous
423             attrs.append(('c:type', record.symbol))
424         if record.disguised:
425             attrs.append(('disguised', '1'))
426         if isinstance(record, GLibRecord):
427             if record.is_gtype_struct_for:
428                 is_gtype_struct = True
429                 attrs.append(('glib:is-gtype-struct-for',
430                               record.is_gtype_struct_for))
431         if record.doc:
432             attrs.append(('doc', record.doc))
433         self._append_version(record, attrs)
434         self._append_deprecated(record, attrs)
435         if isinstance(record, GLibBoxed):
436             attrs.extend(self._boxed_attrs(record))
437         with self.tagcontext('record', attrs):
438             self._write_attributes(record)
439             if record.fields:
440                 for field in record.fields:
441                     self._write_field(field, is_gtype_struct)
442             for method in record.constructors:
443                 self._write_constructor(method)
444             for method in record.methods:
445                 self._write_method(method)
446
447     def _write_union(self, union):
448         attrs = []
449         if union.name is not None:
450             attrs.append(('name', union.name))
451         if union.symbol is not None: # the union might be anonymous
452             attrs.append(('c:type', union.symbol))
453         if union.doc:
454             attrs.append(('doc', union.doc))
455         self._append_version(union, attrs)
456         self._append_deprecated(union, attrs)
457         if isinstance(union, GLibBoxed):
458             attrs.extend(self._boxed_attrs(union))
459         with self.tagcontext('union', attrs):
460             self._write_attributes(union)
461             if union.fields:
462                 for field in union.fields:
463                     self._write_field(field)
464             for method in union.constructors:
465                 self._write_constructor(method)
466             for method in union.methods:
467                 self._write_method(method)
468
469     def _write_field(self, field, is_gtype_struct=False):
470         if isinstance(field, Function):
471             self._write_method(field)
472             return
473
474         if isinstance(field, Callback):
475             attrs = [('name', field.name)]
476             with self.tagcontext('field', attrs):
477                 self._write_attributes(field)
478                 if is_gtype_struct:
479                     self._write_callback(field)
480                 else:
481                     attrs = [('name', 'any'), ('c:type', 'pointer')]
482                     self.write_tag('type', attrs)
483         elif isinstance(field, Struct):
484             self._write_record(field)
485         elif isinstance(field, Union):
486             self._write_union(field)
487         else:
488             attrs = [('name', field.name)]
489             # Fields are assumed to be read-only
490             # (see also girparser.c and generate.c)
491             if not field.readable:
492                 attrs.append(('readable', '0'))
493             if field.writable:
494                 attrs.append(('writable', '1'))
495             if field.bits:
496                 attrs.append(('bits', str(field.bits)))
497             with self.tagcontext('field', attrs):
498                 self._write_attributes(field)
499                 self._write_type(field.type)
500
501     def _write_signal(self, signal):
502         if signal.skip:
503             return
504         attrs = [('name', signal.name)]
505         if signal.doc:
506             attrs.append(('doc', signal.doc))
507         self._append_version(signal, attrs)
508         self._append_deprecated(signal, attrs)
509         with self.tagcontext('glib:signal', attrs):
510             self._write_attributes(signal)
511             self._write_return_type(signal.retval)
512             self._write_parameters(signal.parameters)