Some work on arrays
[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, Enum, Function, Interface, Member,
26                   Array, Struct, Alias, Union)
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, shlib, includes):
35         super(GIRWriter, self).__init__()
36         self._write_repository(namespace, shlib, includes)
37
38     def _write_repository(self, namespace, shlib, 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 includes:
47                 self._write_include(include)
48             self._write_namespace(namespace, shlib)
49
50     def _write_include(self, include):
51         attrs = [('name', include)]
52         self.write_tag('include', attrs)
53
54     def _write_namespace(self, namespace, shlib):
55         attrs = [('name', namespace.name),
56                  ('shared-library', os.path.basename(shlib))]
57         with self.tagcontext('namespace', attrs):
58             for node in namespace.nodes:
59                 self._write_node(node)
60
61     def _write_node(self, node):
62         if isinstance(node, Function):
63             self._write_function(node)
64         elif isinstance(node, Enum):
65             self._write_enum(node)
66         elif isinstance(node, (Class, Interface)):
67             self._write_class(node)
68         elif isinstance(node, Callback):
69             self._write_callback(node)
70         elif isinstance(node, Struct):
71             self._write_record(node)
72         elif isinstance(node, Union):
73             self._write_union(node)
74         elif isinstance(node, GLibBoxed):
75             self._write_boxed(node)
76         elif isinstance(node, Member):
77             # FIXME: atk_misc_instance singleton
78             pass
79         elif isinstance(node, Alias):
80             self._write_alias(node)
81         else:
82             print 'WRITER: Unhandled node', node
83
84     def _append_deprecated(self, node, attrs):
85         if node.deprecated:
86             attrs.append(('deprecated', node.deprecated))
87             if node.deprecated_version:
88                 attrs.append(('deprecated-version',
89                               node.deprecated_version))
90
91     def _write_alias(self, alias):
92         attrs = [('name', alias.name), ('target', alias.target)]
93         if alias.ctype is not None:
94             attrs.append(('c:type', alias.ctype))
95         self.write_tag('alias', attrs)
96
97     def _write_function(self, func, tag_name='function'):
98         attrs = [('name', func.name),
99                  ('c:identifier', func.symbol)]
100         self._append_deprecated(func, attrs)
101         with self.tagcontext(tag_name, attrs):
102             self._write_return_type(func.retval)
103             self._write_parameters(func.parameters)
104
105     def _write_method(self, method):
106         self._write_function(method, tag_name='method')
107
108     def _write_constructor(self, method):
109         self._write_function(method, tag_name='constructor')
110
111     def _write_return_type(self, return_):
112         if not return_:
113             return
114
115         attrs = []
116         if return_.transfer:
117             attrs.append(('transfer-ownership',
118                           str(int(return_.transfer))))
119         with self.tagcontext('return-value', attrs):
120             self._write_type(return_.type)
121
122     def _write_parameters(self, parameters):
123         if not parameters:
124             return
125         with self.tagcontext('parameters'):
126             for parameter in parameters:
127                 self._write_parameter(parameter)
128
129     def _write_parameter(self, parameter):
130         attrs = []
131         if parameter.name is not None:
132             attrs.append(('name', parameter.name))
133         if parameter.direction != 'in':
134             attrs.append(('direction', parameter.direction))
135         if parameter.transfer:
136             attrs.append(('transfer-ownership',
137                           str(int(parameter.transfer))))
138         if parameter.allow_none:
139             attrs.append(('allow-none', '1'))
140         with self.tagcontext('parameter', attrs):
141             self._write_type(parameter.type)
142
143     def _type_to_string(self, ntype):
144         if isinstance(ntype, basestring):
145             return ntype
146         if isinstance(ntype, Array):
147             options = []
148             if not ntype.zeroterminated:
149                 options.append('zero-terminated=0')
150             if ntype.length_param_index >= 0:
151                 options.append('length=%d' % (ntype.length_param_index, ))
152             return self._type_to_string(ntype.element_type) + \
153                 '[%s]' % (','.join(options), )
154         return ntype.name
155
156     def _write_type(self, ntype, relation=None):
157         if isinstance(ntype, basestring):
158             typename = ntype
159             type_cname = None
160         else:
161             typename = ntype.name
162             type_cname = ntype.ctype
163         attrs = [('name', self._type_to_string(ntype))]
164         if relation:
165             attrs.append(('relation', relation))
166         # FIXME: figure out if type references a basic type
167         #        or a boxed/class/interface etc. and skip
168         #        writing the ctype if the latter.
169         if type_cname is not None:
170             attrs.append(('c:type', type_cname))
171             self.write_tag('type', attrs)
172
173     def _write_enum(self, enum):
174         attrs = [('name', enum.name),
175                  ('c:type', enum.symbol)]
176         self._append_deprecated(enum, attrs)
177         tag_name = 'enumeration'
178         if isinstance(enum, GLibEnum):
179             attrs.extend([('glib:type-name', enum.type_name),
180                           ('glib:get-type', enum.get_type)])
181             if isinstance(enum, GLibFlags):
182                 tag_name = 'bitfield'
183
184         with self.tagcontext(tag_name, attrs):
185             for member in enum.members:
186                 self._write_member(member)
187
188     def _write_member(self, member):
189         attrs = [('name', member.name),
190                  ('value', str(member.value)),
191                  ('c:identifier', member.symbol)]
192         if isinstance(member, GLibEnumMember):
193             attrs.append(('glib:nick', member.nick))
194         self.write_tag('member', attrs)
195
196     def _write_class(self, node):
197         attrs = [('name', node.name),
198                  ('c:type', node.ctype)]
199         self._append_deprecated(node, attrs)
200         if isinstance(node, Class):
201             tag_name = 'class'
202             if node.parent is not None:
203                 attrs.append(('parent', node.parent))
204         else:
205             tag_name = 'interface'
206         if isinstance(node, (GLibObject, GLibInterface)):
207             attrs.append(('glib:type-name', node.type_name))
208             if node.get_type:
209                 attrs.append(('glib:get-type', node.get_type))
210         with self.tagcontext(tag_name, attrs):
211             if isinstance(node, GLibObject):
212                 for iface in node.interfaces:
213                     self.write_tag('implements', [('name', iface)])
214             if isinstance(node, Class):
215                 for method in node.constructors:
216                     self._write_constructor(method)
217             for method in node.methods:
218                 self._write_method(method)
219             for prop in node.properties:
220                 self._write_property(prop)
221             for field in node.fields:
222                 self._write_field(field)
223             for signal in node.signals:
224                 self._write_signal(signal)
225
226     def _write_boxed(self, boxed):
227         attrs = [('c:type', boxed.ctype),
228                  ('glib:name', boxed.name)]
229         attrs.extend(self._boxed_attrs(boxed))
230         with self.tagcontext('glib:boxed', attrs):
231             self._write_boxed_ctors_methods(boxed)
232
233     def _write_property(self, prop):
234         attrs = [('name', prop.name)]
235         # Properties are assumed to be readable
236         if not prop.readable:
237             attrs.append(('readable', '0'))
238         if prop.writable:
239             attrs.append(('writable', '1'))
240         if prop.construct_only:
241             attrs.append(('construct-only', '1'))
242         with self.tagcontext('property', attrs):
243             self._write_type(prop.type)
244
245     def _write_callback(self, callback):
246         # FIXME: reuse _write_function
247         attrs = [('name', callback.name), ('c:type', callback.ctype)]
248         self._append_deprecated(callback, attrs)
249         with self.tagcontext('callback', attrs):
250             self._write_return_type(callback.retval)
251             self._write_parameters(callback.parameters)
252
253     def _boxed_attrs(self, boxed):
254         return [('glib:type-name', boxed.type_name),
255                 ('glib:get-type', boxed.get_type)]
256
257     def _write_boxed_ctors_methods(self, boxed):
258         for method in boxed.constructors:
259             self._write_constructor(method)
260         for method in boxed.methods:
261             self._write_method(method)
262
263     def _write_record(self, record):
264         attrs = [('name', record.name),
265                  ('c:type', record.symbol)]
266         self._append_deprecated(record, attrs)
267         if isinstance(record, GLibBoxed):
268             attrs.extend(self._boxed_attrs(record))
269         with self.tagcontext('record', attrs):
270             if record.fields:
271                 for field in record.fields:
272                     self._write_field(field)
273             if isinstance(record, GLibBoxed):
274                 self._write_boxed_ctors_methods(record)
275
276     def _write_union(self, union):
277         attrs = [('name', union.name),
278                  ('c:type', union.symbol)]
279         self._append_deprecated(union, attrs)
280         if isinstance(union, GLibBoxed):
281             attrs.extend(self._boxed_attrs(union))
282         with self.tagcontext('union', attrs):
283             if union.fields:
284                 for field in union.fields:
285                     self._write_field(field)
286             if isinstance(union, GLibBoxed):
287                 self._write_boxed_ctors_methods(union)
288
289     def _write_field(self, field):
290         # FIXME: Just function
291         if isinstance(field, (Callback, Function)):
292             self._write_callback(field)
293             return
294
295         attrs = [('name', field.name)]
296         with self.tagcontext('field', attrs):
297             self._write_type(field.type)
298
299     def _write_signal(self, signal):
300         attrs = [('name', signal.name)]
301         with self.tagcontext('glib:signal', attrs):
302             self._write_return_type(signal.retval)
303             self._write_parameters(signal.parameters)