89457004a7098f71151f22c0bfff126b31594bf1
[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 _write_alias(self, alias):
99         attrs = [('name', alias.name), ('target', alias.target)]
100         if alias.ctype is not None:
101             attrs.append(('c:type', alias.ctype))
102         self.write_tag('alias', attrs)
103
104     def _write_function(self, func, tag_name='function'):
105         attrs = [('name', func.name),
106                  ('c:identifier', func.symbol)]
107         self._append_deprecated(func, attrs)
108         with self.tagcontext(tag_name, attrs):
109             self._write_return_type(func.retval)
110             self._write_parameters(func.parameters)
111
112     def _write_method(self, method):
113         self._write_function(method, tag_name='method')
114
115     def _write_constructor(self, method):
116         self._write_function(method, tag_name='constructor')
117
118     def _write_return_type(self, return_):
119         if not return_:
120             return
121
122         attrs = []
123         if return_.transfer:
124             attrs.append(('transfer-ownership',
125                           return_.transfer))
126         with self.tagcontext('return-value', attrs):
127             self._write_type(return_.type)
128
129     def _write_parameters(self, parameters):
130         if not parameters:
131             return
132         with self.tagcontext('parameters'):
133             for parameter in parameters:
134                 self._write_parameter(parameter)
135
136     def _write_parameter(self, parameter):
137         attrs = []
138         if parameter.name is not None:
139             attrs.append(('name', parameter.name))
140         if parameter.direction != 'in':
141             attrs.append(('direction', parameter.direction))
142         if parameter.transfer:
143             attrs.append(('transfer-ownership',
144                           parameter.transfer))
145         if parameter.allow_none:
146             attrs.append(('allow-none', '1'))
147         with self.tagcontext('parameter', attrs):
148             self._write_type(parameter.type)
149
150     def _type_to_string(self, ntype):
151         if isinstance(ntype, basestring):
152             return ntype
153         return ntype.name
154
155     def _write_type(self, ntype, relation=None):
156         if isinstance(ntype, basestring):
157             typename = ntype
158             type_cname = None
159         else:
160             typename = ntype.name
161             type_cname = ntype.ctype
162         if isinstance(ntype, Varargs):
163             with self.tagcontext('varargs', []):
164                 pass
165             return
166         if isinstance(ntype, Array):
167             attrs = []
168             if not ntype.zeroterminated:
169                 attrs.append(('zero-terminated', '0'))
170             if ntype.length_param_index >= 0:
171                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
172             attrs.append(('c:type', ntype.ctype))
173             with self.tagcontext('array', attrs):
174                 self._write_type(ntype.element_type)
175             return
176         attrs = [('name', self._type_to_string(ntype))]
177         # FIXME: figure out if type references a basic type
178         #        or a boxed/class/interface etc. and skip
179         #        writing the ctype if the latter.
180         if type_cname is not None:
181             attrs.append(('c:type', type_cname))
182         if isinstance(ntype, List) and ntype.element_type:
183             with self.tagcontext('type', attrs):
184                 self._write_type(ntype.element_type)
185             return
186         if isinstance(ntype, Map) and ntype.key_type:
187             with self.tagcontext('type', attrs):
188                 self._write_type(ntype.key_type)
189                 self._write_type(ntype.value_type)
190             return
191         # Not a special type, just write it out
192         self.write_tag('type', attrs)
193
194     def _write_enum(self, enum):
195         attrs = [('name', enum.name)]
196         self._append_deprecated(enum, attrs)
197         if isinstance(enum, GLibFlags):
198             tag_name = 'bitfield'
199         else:
200             tag_name = 'enumeration'
201         if isinstance(enum, GLibEnum) or isinstance(enum, GLibFlags):
202             attrs.extend([('glib:type-name', enum.type_name),
203                           ('glib:get-type', enum.get_type),
204                           ('c:type', enum.ctype)])
205         else:
206             attrs.append(('c:type', enum.symbol))
207         with self.tagcontext(tag_name, attrs):
208             for member in enum.members:
209                 self._write_member(member)
210
211     def _write_member(self, member):
212         attrs = [('name', member.name),
213                  ('value', str(member.value)),
214                  ('c:identifier', member.symbol)]
215         if isinstance(member, GLibEnumMember):
216             attrs.append(('glib:nick', member.nick))
217         self.write_tag('member', attrs)
218
219     def _write_constant(self, constant):
220         attrs = [('name', constant.name),
221                  ('value', str(constant.value))]
222         with self.tagcontext('constant', attrs):
223             self._write_type(constant.type)
224
225     def _write_class(self, node):
226         attrs = [('name', node.name),
227                  ('c:type', node.ctype)]
228         self._append_deprecated(node, attrs)
229         if isinstance(node, Class):
230             tag_name = 'class'
231             if node.parent is not None:
232                 attrs.append(('parent', node.parent))
233             if node.is_abstract:
234                 attrs.append(('abstract', '1'))
235         else:
236             tag_name = 'interface'
237         if isinstance(node, (GLibObject, GLibInterface)):
238             attrs.append(('glib:type-name', node.type_name))
239             if node.get_type:
240                 attrs.append(('glib:get-type', node.get_type))
241         with self.tagcontext(tag_name, attrs):
242             if isinstance(node, GLibObject):
243                 for iface in node.interfaces:
244                     self.write_tag('implements', [('name', iface)])
245             if isinstance(node, Class):
246                 for method in node.constructors:
247                     self._write_constructor(method)
248             for method in node.methods:
249                 self._write_method(method)
250             for prop in node.properties:
251                 self._write_property(prop)
252             for field in node.fields:
253                 self._write_field(field)
254             for signal in node.signals:
255                 self._write_signal(signal)
256
257     def _write_boxed(self, boxed):
258         attrs = [('c:type', boxed.ctype),
259                  ('glib:name', boxed.name)]
260         attrs.extend(self._boxed_attrs(boxed))
261         with self.tagcontext('glib:boxed', attrs):
262             self._write_boxed_ctors_methods(boxed)
263
264     def _write_property(self, prop):
265         attrs = [('name', prop.name)]
266         # Properties are assumed to be readable (see also generate.c)
267         if not prop.readable:
268             attrs.append(('readable', '0'))
269         if prop.writable:
270             attrs.append(('writable', '1'))
271         if prop.construct:
272             attrs.append(('construct', '1'))
273         if prop.construct_only:
274             attrs.append(('construct-only', '1'))
275         with self.tagcontext('property', attrs):
276             self._write_type(prop.type)
277
278     def _write_callback(self, callback):
279         # FIXME: reuse _write_function
280         attrs = [('name', callback.name), ('c:type', callback.ctype)]
281         self._append_deprecated(callback, attrs)
282         with self.tagcontext('callback', attrs):
283             self._write_return_type(callback.retval)
284             self._write_parameters(callback.parameters)
285
286     def _boxed_attrs(self, boxed):
287         return [('glib:type-name', boxed.type_name),
288                 ('glib:get-type', boxed.get_type)]
289
290     def _write_boxed_ctors_methods(self, boxed):
291         for method in boxed.constructors:
292             self._write_constructor(method)
293         for method in boxed.methods:
294             self._write_method(method)
295
296     def _write_record(self, record):
297         attrs = [('name', record.name),
298                  ('c:type', record.symbol)]
299         self._append_deprecated(record, attrs)
300         if isinstance(record, GLibBoxed):
301             attrs.extend(self._boxed_attrs(record))
302         with self.tagcontext('record', attrs):
303             if record.fields:
304                 for field in record.fields:
305                     self._write_field(field)
306             if isinstance(record, GLibBoxed):
307                 self._write_boxed_ctors_methods(record)
308
309     def _write_union(self, union):
310         attrs = [('name', union.name),
311                  ('c:type', union.symbol)]
312         self._append_deprecated(union, attrs)
313         if isinstance(union, GLibBoxed):
314             attrs.extend(self._boxed_attrs(union))
315         with self.tagcontext('union', attrs):
316             if union.fields:
317                 for field in union.fields:
318                     self._write_field(field)
319             if isinstance(union, GLibBoxed):
320                 self._write_boxed_ctors_methods(union)
321
322     def _write_field(self, field):
323         if isinstance(field, Function):
324             self._write_method(field)
325             return
326
327         if isinstance(field, Callback):
328             self._write_callback(field)
329             return
330
331         attrs = [('name', field.name)]
332         # Fields are assumed to be read-only
333         # (see also girparser.c and generate.c)
334         if not field.readable:
335             attrs.append(('readable', '0'))
336         if field.writable:
337             attrs.append(('writable', '1'))
338         if field.bits:
339             attrs.append(('bits', str(field.bits)))
340         with self.tagcontext('field', attrs):
341             self._write_type(field.type)
342
343     def _write_signal(self, signal):
344         attrs = [('name', signal.name)]
345         with self.tagcontext('glib:signal', attrs):
346             self._write_return_type(signal.retval)
347             self._write_parameters(signal.parameters)