Add basic support for union, base the code much on Struct. Add a testcase.
[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 from .ast import (Callback, Class, Enum, Function, Interface, Member,
24                   Sequence, Struct, Alias, Union)
25 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
26                       GLibFlags, GLibObject, GLibInterface)
27 from .xmlwriter import XMLWriter
28
29
30 class GIRWriter(XMLWriter):
31
32     def __init__(self, namespace):
33         super(GIRWriter, self).__init__()
34         self._write_repository(namespace)
35
36     def _write_repository(self, namespace):
37         attrs = [
38             ('version', '1.0'),
39             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
40             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
41             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
42             ]
43         with self.tagcontext('repository', attrs):
44             self._write_namespace(namespace)
45
46     def _write_namespace(self, namespace):
47         attrs = [('name', namespace.name)]
48         with self.tagcontext('namespace', attrs):
49             for node in namespace.nodes:
50                 self._write_node(node)
51
52     def _write_node(self, node):
53         if isinstance(node, Function):
54             self._write_function(node)
55         elif isinstance(node, Enum):
56             self._write_enum(node)
57         elif isinstance(node, (Class, Interface)):
58             self._write_class(node)
59         elif isinstance(node, GLibBoxed):
60             self._write_boxed(node)
61         elif isinstance(node, Callback):
62             self._write_callback(node)
63         elif isinstance(node, Struct):
64             self._write_record(node)
65         elif isinstance(node, Union):
66             self._write_union(node)
67         elif isinstance(node, Member):
68             # FIXME: atk_misc_instance singleton
69             pass
70         elif isinstance(node, Alias):
71             self._write_alias(node)
72         else:
73             print 'WRITER: Unhandled node', node
74
75     def _write_alias(self, alias):
76         attrs = [('name', alias.name), ('target', alias.target)]
77         if alias.ctype is not None:
78             attrs.append(('c:type', alias.ctype))
79         self.write_tag('alias', attrs)
80
81     def _write_function(self, func, tag_name='function'):
82         attrs = [('name', func.name),
83                  ('c:identifier', func.symbol)]
84         with self.tagcontext(tag_name, attrs):
85             self._write_return_type(func.retval)
86             self._write_parameters(func.parameters)
87
88     def _write_method(self, method):
89         self._write_function(method, tag_name='method')
90
91     def _write_constructor(self, method):
92         self._write_function(method, tag_name='constructor')
93
94     def _write_return_type(self, return_):
95         if not return_:
96             return
97
98         attrs = []
99         if return_.transfer:
100             attrs.append(('transfer-ownership',
101                           str(int(return_.transfer))))
102         with self.tagcontext('return-value', attrs):
103             self._write_type(return_.type)
104
105     def _write_parameters(self, parameters):
106         if not parameters:
107             return
108         with self.tagcontext('parameters'):
109             for parameter in parameters:
110                 self._write_parameter(parameter)
111
112     def _write_parameter(self, parameter):
113         attrs = []
114         if parameter.name is not None:
115             attrs.append(('name', parameter.name))
116         if parameter.direction != 'in':
117             attrs.append(('direction', parameter.direction))
118         if parameter.transfer:
119             attrs.append(('transfer-ownership',
120                           str(int(parameter.transfer))))
121         if parameter.allow_none:
122             attrs.append(('allow-none', '1'))
123         with self.tagcontext('parameter', attrs):
124             self._write_type(parameter.type)
125
126     def _write_type(self, ntype, relation=None):
127         if isinstance(ntype, basestring):
128             typename = ntype
129             type_cname = None
130         else:
131             typename = ntype.name
132             type_cname = ntype.ctype
133         attrs = [('name', typename)]
134         if relation:
135             attrs.append(('relation', relation))
136         if isinstance(ntype, Sequence):
137             if ntype.transfer:
138                 attrs.append(('transfer-ownership',
139                               str(int(ntype.transfer))))
140             with self.tagcontext('type', attrs):
141                 self._write_type(ntype.element_type, relation="element")
142         else:
143             # FIXME: figure out if type references a basic type
144             #        or a boxed/class/interface etc. and skip
145             #        writing the ctype if the latter.
146             if type_cname is not None:
147                 attrs.append(('c:type', type_cname))
148             self.write_tag('type', attrs)
149
150     def _write_enum(self, enum):
151         attrs = [('name', enum.name),
152                  ('c:type', enum.symbol)]
153         tag_name = 'enumeration'
154         if isinstance(enum, GLibEnum):
155             attrs.extend([('glib:type-name', enum.type_name),
156                           ('glib:get-type', enum.get_type)])
157             if isinstance(enum, GLibFlags):
158                 tag_name = 'bitfield'
159
160         with self.tagcontext(tag_name, attrs):
161             for member in enum.members:
162                 self._write_member(member)
163
164     def _write_member(self, member):
165         attrs = [('name', member.name),
166                  ('value', str(member.value)),
167                  ('c:identifier', member.symbol)]
168         if isinstance(member, GLibEnumMember):
169             attrs.append(('glib:nick', member.nick))
170         self.write_tag('member', attrs)
171
172     def _write_class(self, node):
173         attrs = [('name', node.name),
174                  ('c:type', node.ctype)]
175         if isinstance(node, Class):
176             tag_name = 'class'
177             if node.parent is not None:
178                 attrs.append(('parent', node.parent))
179         else:
180             tag_name = 'interface'
181         if isinstance(node, (GLibObject, GLibInterface)):
182             attrs.append(('glib:type-name', node.type_name))
183             if node.get_type:
184                 attrs.append(('glib:get-type', node.get_type))
185         with self.tagcontext(tag_name, attrs):
186             if isinstance(node, Class):
187                 for method in node.constructors:
188                     self._write_constructor(method)
189             for method in node.methods:
190                 self._write_method(method)
191             for prop in node.properties:
192                 self._write_property(prop)
193             for field in node.fields:
194                 self._write_field(field)
195             for signal in node.signals:
196                 self._write_signal(signal)
197
198     def _write_boxed(self, boxed):
199         attrs = [('c:type', boxed.ctype),
200                  ('glib:name', boxed.name),
201                  ('glib:type-name', boxed.type_name),
202                  ('glib:get-type', boxed.get_type)]
203
204         with self.tagcontext('glib:boxed', attrs):
205             for method in boxed.constructors:
206                 self._write_constructor(method)
207             for method in boxed.methods:
208                 self._write_method(method)
209
210     def _write_property(self, prop):
211         attrs = [('name', prop.name)]
212         with self.tagcontext('property', attrs):
213             self._write_type(prop.type)
214
215     def _write_callback(self, callback):
216         # FIXME: reuse _write_function
217         attrs = [('name', callback.name), ('c:type', callback.ctype)]
218         with self.tagcontext('callback', attrs):
219             self._write_return_type(callback.retval)
220             self._write_parameters(callback.parameters)
221
222     def _write_record(self, record):
223         attrs = [('name', record.name),
224                  ('c:type', record.symbol)]
225         if record.fields:
226             with self.tagcontext('record', attrs):
227                 for field in record.fields:
228                     self._write_field(field)
229         else:
230             self.write_tag('record', attrs)
231
232     def _write_union(self, union):
233         attrs = [('name', union.name),
234                  ('c:type', union.symbol)]
235         if union.fields:
236             with self.tagcontext('union', attrs):
237                 for field in union.fields:
238                     self._write_field(field)
239         else:
240             self.write_tag('union', attrs)
241
242     def _write_field(self, field):
243         # FIXME: Just function
244         if isinstance(field, (Callback, Function)):
245             self._write_callback(field)
246             return
247
248         attrs = [('name', field.name)]
249         with self.tagcontext('field', attrs):
250             self._write_type(field.type)
251
252     def _write_signal(self, signal):
253         attrs = [('name', signal.name)]
254         with self.tagcontext('glib:signal', attrs):
255             self._write_return_type(signal.retval)
256             self._write_parameters(signal.parameters)