Bug 557241 – "throws" flag for functions
[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 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program 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
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19 #
20
21 from __future__ import with_statement
22
23 import os
24
25 from .ast import (Callback, Class, Constant, Enum, Function, Interface, Member,
26                   Array, Struct, Alias, Union, List, Map, Varargs)
27 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
28                       GLibFlags, GLibObject, GLibInterface)
29 from .xmlwriter import XMLWriter
30
31
32 class GIRWriter(XMLWriter):
33
34     def __init__(self, namespace, shlibs, includes):
35         super(GIRWriter, self).__init__()
36         self._write_repository(namespace, shlibs, includes)
37
38     def _write_repository(self, namespace, shlibs, includes=set()):
39         attrs = [
40             ('version', '1.0'),
41             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
42             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
43             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
44             ]
45         with self.tagcontext('repository', attrs):
46             for include in sorted(includes):
47                 self._write_include(include)
48             self._write_namespace(namespace, shlibs)
49
50     def _write_include(self, include):
51         attrs = [('name', include.name), ('version', include.version)]
52         self.write_tag('include', attrs)
53
54     def _write_namespace(self, namespace, shlibs):
55         libraries = []
56         for l in shlibs:
57             libraries.append(os.path.basename(l))
58
59         attrs = [('name', namespace.name),
60                  ('version', namespace.version),
61                  ('shared-library', ','.join(libraries))]
62         with self.tagcontext('namespace', attrs):
63             for node in namespace.nodes:
64                 self._write_node(node)
65
66     def _write_node(self, node):
67         if isinstance(node, Function):
68             self._write_function(node)
69         elif isinstance(node, Enum):
70             self._write_enum(node)
71         elif isinstance(node, (Class, Interface)):
72             self._write_class(node)
73         elif isinstance(node, Callback):
74             self._write_callback(node)
75         elif isinstance(node, Struct):
76             self._write_record(node)
77         elif isinstance(node, Union):
78             self._write_union(node)
79         elif isinstance(node, GLibBoxed):
80             self._write_boxed(node)
81         elif isinstance(node, Member):
82             # FIXME: atk_misc_instance singleton
83             pass
84         elif isinstance(node, Alias):
85             self._write_alias(node)
86         elif isinstance(node, Constant):
87             self._write_constant(node)
88         else:
89             print 'WRITER: Unhandled node', node
90
91     def _append_deprecated(self, node, attrs):
92         if node.deprecated:
93             attrs.append(('deprecated', node.deprecated))
94             if node.deprecated_version:
95                 attrs.append(('deprecated-version',
96                               node.deprecated_version))
97
98     def _append_throws(self, func, attrs):
99         if func.throws:
100             attrs.append(('throws', '1'))
101
102     def _write_alias(self, alias):
103         attrs = [('name', alias.name), ('target', alias.target)]
104         if alias.ctype is not None:
105             attrs.append(('c:type', alias.ctype))
106         self.write_tag('alias', attrs)
107
108     def _write_function(self, func, tag_name='function'):
109         attrs = [('name', func.name),
110                  ('c:identifier', func.symbol)]
111         self._append_deprecated(func, attrs)
112         self._append_throws(func, attrs)
113         with self.tagcontext(tag_name, attrs):
114             self._write_return_type(func.retval)
115             self._write_parameters(func.parameters)
116
117     def _write_method(self, method):
118         self._write_function(method, tag_name='method')
119
120     def _write_constructor(self, method):
121         self._write_function(method, tag_name='constructor')
122
123     def _write_return_type(self, return_):
124         if not return_:
125             return
126
127         attrs = []
128         if return_.transfer:
129             attrs.append(('transfer-ownership',
130                           return_.transfer))
131         with self.tagcontext('return-value', attrs):
132             self._write_type(return_.type)
133
134     def _write_parameters(self, parameters):
135         if not parameters:
136             return
137         with self.tagcontext('parameters'):
138             for parameter in parameters:
139                 self._write_parameter(parameter)
140
141     def _write_parameter(self, parameter):
142         attrs = []
143         if parameter.name is not None:
144             attrs.append(('name', parameter.name))
145         if parameter.direction != 'in':
146             attrs.append(('direction', parameter.direction))
147         if parameter.transfer:
148             attrs.append(('transfer-ownership',
149                           parameter.transfer))
150         if parameter.allow_none:
151             attrs.append(('allow-none', '1'))
152         with self.tagcontext('parameter', attrs):
153             self._write_type(parameter.type)
154
155     def _type_to_string(self, ntype):
156         if isinstance(ntype, basestring):
157             return ntype
158         return ntype.name
159
160     def _write_type(self, ntype, relation=None):
161         if isinstance(ntype, basestring):
162             typename = ntype
163             type_cname = None
164         else:
165             typename = ntype.name
166             type_cname = ntype.ctype
167         if isinstance(ntype, Varargs):
168             with self.tagcontext('varargs', []):
169                 pass
170             return
171         if isinstance(ntype, Array):
172             attrs = []
173             if not ntype.zeroterminated:
174                 attrs.append(('zero-terminated', '0'))
175             if ntype.length_param_index >= 0:
176                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
177             attrs.append(('c:type', ntype.ctype))
178             with self.tagcontext('array', attrs):
179                 self._write_type(ntype.element_type)
180             return
181         attrs = [('name', self._type_to_string(ntype))]
182         # FIXME: figure out if type references a basic type
183         #        or a boxed/class/interface etc. and skip
184         #        writing the ctype if the latter.
185         if type_cname is not None:
186             attrs.append(('c:type', type_cname))
187         if isinstance(ntype, List) and ntype.element_type:
188             with self.tagcontext('type', attrs):
189                 self._write_type(ntype.element_type)
190             return
191         if isinstance(ntype, Map) and ntype.key_type:
192             with self.tagcontext('type', attrs):
193                 self._write_type(ntype.key_type)
194                 self._write_type(ntype.value_type)
195             return
196         # Not a special type, just write it out
197         self.write_tag('type', attrs)
198
199     def _write_enum(self, enum):
200         attrs = [('name', enum.name)]
201         self._append_deprecated(enum, attrs)
202         if isinstance(enum, GLibFlags):
203             tag_name = 'bitfield'
204         else:
205             tag_name = 'enumeration'
206         if isinstance(enum, GLibEnum) or isinstance(enum, GLibFlags):
207             attrs.extend([('glib:type-name', enum.type_name),
208                           ('glib:get-type', enum.get_type),
209                           ('c:type', enum.ctype)])
210         else:
211             attrs.append(('c:type', enum.symbol))
212         with self.tagcontext(tag_name, attrs):
213             for member in enum.members:
214                 self._write_member(member)
215
216     def _write_member(self, member):
217         attrs = [('name', member.name),
218                  ('value', str(member.value)),
219                  ('c:identifier', member.symbol)]
220         if isinstance(member, GLibEnumMember):
221             attrs.append(('glib:nick', member.nick))
222         self.write_tag('member', attrs)
223
224     def _write_constant(self, constant):
225         attrs = [('name', constant.name),
226                  ('value', str(constant.value))]
227         with self.tagcontext('constant', attrs):
228             self._write_type(constant.type)
229
230     def _write_class(self, node):
231         attrs = [('name', node.name),
232                  ('c:type', node.ctype)]
233         self._append_deprecated(node, attrs)
234         if isinstance(node, Class):
235             tag_name = 'class'
236             if node.parent is not None:
237                 attrs.append(('parent', node.parent))
238             if node.is_abstract:
239                 attrs.append(('abstract', '1'))
240         else:
241             tag_name = 'interface'
242         if isinstance(node, (GLibObject, GLibInterface)):
243             attrs.append(('glib:type-name', node.type_name))
244             if node.get_type:
245                 attrs.append(('glib:get-type', node.get_type))
246         with self.tagcontext(tag_name, attrs):
247             if isinstance(node, GLibObject):
248                 for iface in node.interfaces:
249                     self.write_tag('implements', [('name', iface)])
250             if isinstance(node, Class):
251                 for method in node.constructors:
252                     self._write_constructor(method)
253             for method in node.methods:
254                 self._write_method(method)
255             for prop in node.properties:
256                 self._write_property(prop)
257             for field in node.fields:
258                 self._write_field(field)
259             for signal in node.signals:
260                 self._write_signal(signal)
261
262     def _write_boxed(self, boxed):
263         attrs = [('c:type', boxed.ctype),
264                  ('glib:name', boxed.name)]
265         attrs.extend(self._boxed_attrs(boxed))
266         with self.tagcontext('glib:boxed', attrs):
267             self._write_boxed_ctors_methods(boxed)
268
269     def _write_property(self, prop):
270         attrs = [('name', prop.name)]
271         # Properties are assumed to be readable (see also generate.c)
272         if not prop.readable:
273             attrs.append(('readable', '0'))
274         if prop.writable:
275             attrs.append(('writable', '1'))
276         if prop.construct:
277             attrs.append(('construct', '1'))
278         if prop.construct_only:
279             attrs.append(('construct-only', '1'))
280         with self.tagcontext('property', attrs):
281             self._write_type(prop.type)
282
283     def _write_callback(self, callback):
284         # FIXME: reuse _write_function
285         attrs = [('name', callback.name), ('c:type', callback.ctype)]
286         self._append_deprecated(callback, attrs)
287         with self.tagcontext('callback', attrs):
288             self._write_return_type(callback.retval)
289             self._write_parameters(callback.parameters)
290
291     def _boxed_attrs(self, boxed):
292         return [('glib:type-name', boxed.type_name),
293                 ('glib:get-type', boxed.get_type)]
294
295     def _write_boxed_ctors_methods(self, boxed):
296         for method in boxed.constructors:
297             self._write_constructor(method)
298         for method in boxed.methods:
299             self._write_method(method)
300
301     def _write_record(self, record):
302         attrs = [('name', record.name),
303                  ('c:type', record.symbol)]
304         self._append_deprecated(record, attrs)
305         if isinstance(record, GLibBoxed):
306             attrs.extend(self._boxed_attrs(record))
307         with self.tagcontext('record', attrs):
308             if record.fields:
309                 for field in record.fields:
310                     self._write_field(field)
311             if isinstance(record, GLibBoxed):
312                 self._write_boxed_ctors_methods(record)
313
314     def _write_union(self, union):
315         attrs = [('name', union.name),
316                  ('c:type', union.symbol)]
317         self._append_deprecated(union, attrs)
318         if isinstance(union, GLibBoxed):
319             attrs.extend(self._boxed_attrs(union))
320         with self.tagcontext('union', attrs):
321             if union.fields:
322                 for field in union.fields:
323                     self._write_field(field)
324             if isinstance(union, GLibBoxed):
325                 self._write_boxed_ctors_methods(union)
326
327     def _write_field(self, field):
328         if isinstance(field, Function):
329             self._write_method(field)
330             return
331
332         if isinstance(field, Callback):
333             self._write_callback(field)
334             return
335
336         attrs = [('name', field.name)]
337         # Fields are assumed to be read-only
338         # (see also girparser.c and generate.c)
339         if not field.readable:
340             attrs.append(('readable', '0'))
341         if field.writable:
342             attrs.append(('writable', '1'))
343         if field.bits:
344             attrs.append(('bits', str(field.bits)))
345         with self.tagcontext('field', attrs):
346             self._write_type(field.type)
347
348     def _write_signal(self, signal):
349         attrs = [('name', signal.name)]
350         with self.tagcontext('glib:signal', attrs):
351             self._write_return_type(signal.retval)
352             self._write_parameters(signal.parameters)