Rework to use recursive XML
[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, List, Map)
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         return ntype.name
147
148     def _write_type(self, ntype, relation=None):
149         if isinstance(ntype, basestring):
150             typename = ntype
151             type_cname = None
152         else:
153             typename = ntype.name
154             type_cname = ntype.ctype
155         if isinstance(ntype, Array):
156             attrs = []
157             if not ntype.zeroterminated:
158                 attrs.append(('zero-terminated', '0'))
159             if ntype.length_param_index >= 0:
160                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
161             attrs.append(('c:type', ntype.ctype))
162             with self.tagcontext('array', attrs):
163                 self._write_type(ntype.element_type)
164             return
165         attrs = [('name', self._type_to_string(ntype))]
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         if isinstance(ntype, List) and ntype.element_type:
172             with self.tagcontext('type', attrs):
173                 self._write_type(ntype.element_type)
174             return
175         if isinstance(ntype, Map) and ntype.key_type:
176             with self.tagcontext('type', attrs):
177                 self._write_type(ntype.key_type)
178                 self._write_type(ntype.value_type)
179             return
180         # Not a special type, just write it out
181         self.write_tag('type', attrs)
182
183     def _write_enum(self, enum):
184         attrs = [('name', enum.name),
185                  ('c:type', enum.symbol)]
186         self._append_deprecated(enum, attrs)
187         tag_name = 'enumeration'
188         if isinstance(enum, GLibEnum):
189             attrs.extend([('glib:type-name', enum.type_name),
190                           ('glib:get-type', enum.get_type)])
191             if isinstance(enum, GLibFlags):
192                 tag_name = 'bitfield'
193
194         with self.tagcontext(tag_name, attrs):
195             for member in enum.members:
196                 self._write_member(member)
197
198     def _write_member(self, member):
199         attrs = [('name', member.name),
200                  ('value', str(member.value)),
201                  ('c:identifier', member.symbol)]
202         if isinstance(member, GLibEnumMember):
203             attrs.append(('glib:nick', member.nick))
204         self.write_tag('member', attrs)
205
206     def _write_class(self, node):
207         attrs = [('name', node.name),
208                  ('c:type', node.ctype)]
209         self._append_deprecated(node, attrs)
210         if isinstance(node, Class):
211             tag_name = 'class'
212             if node.parent is not None:
213                 attrs.append(('parent', node.parent))
214         else:
215             tag_name = 'interface'
216         if isinstance(node, (GLibObject, GLibInterface)):
217             attrs.append(('glib:type-name', node.type_name))
218             if node.get_type:
219                 attrs.append(('glib:get-type', node.get_type))
220         with self.tagcontext(tag_name, attrs):
221             if isinstance(node, GLibObject):
222                 for iface in node.interfaces:
223                     self.write_tag('implements', [('name', iface)])
224             if isinstance(node, Class):
225                 for method in node.constructors:
226                     self._write_constructor(method)
227             for method in node.methods:
228                 self._write_method(method)
229             for prop in node.properties:
230                 self._write_property(prop)
231             for field in node.fields:
232                 self._write_field(field)
233             for signal in node.signals:
234                 self._write_signal(signal)
235
236     def _write_boxed(self, boxed):
237         attrs = [('c:type', boxed.ctype),
238                  ('glib:name', boxed.name)]
239         attrs.extend(self._boxed_attrs(boxed))
240         with self.tagcontext('glib:boxed', attrs):
241             self._write_boxed_ctors_methods(boxed)
242
243     def _write_property(self, prop):
244         attrs = [('name', prop.name)]
245         # Properties are assumed to be readable
246         if not prop.readable:
247             attrs.append(('readable', '0'))
248         if prop.writable:
249             attrs.append(('writable', '1'))
250         if prop.construct_only:
251             attrs.append(('construct-only', '1'))
252         with self.tagcontext('property', attrs):
253             self._write_type(prop.type)
254
255     def _write_callback(self, callback):
256         # FIXME: reuse _write_function
257         attrs = [('name', callback.name), ('c:type', callback.ctype)]
258         self._append_deprecated(callback, attrs)
259         with self.tagcontext('callback', attrs):
260             self._write_return_type(callback.retval)
261             self._write_parameters(callback.parameters)
262
263     def _boxed_attrs(self, boxed):
264         return [('glib:type-name', boxed.type_name),
265                 ('glib:get-type', boxed.get_type)]
266
267     def _write_boxed_ctors_methods(self, boxed):
268         for method in boxed.constructors:
269             self._write_constructor(method)
270         for method in boxed.methods:
271             self._write_method(method)
272
273     def _write_record(self, record):
274         attrs = [('name', record.name),
275                  ('c:type', record.symbol)]
276         self._append_deprecated(record, attrs)
277         if isinstance(record, GLibBoxed):
278             attrs.extend(self._boxed_attrs(record))
279         with self.tagcontext('record', attrs):
280             if record.fields:
281                 for field in record.fields:
282                     self._write_field(field)
283             if isinstance(record, GLibBoxed):
284                 self._write_boxed_ctors_methods(record)
285
286     def _write_union(self, union):
287         attrs = [('name', union.name),
288                  ('c:type', union.symbol)]
289         self._append_deprecated(union, attrs)
290         if isinstance(union, GLibBoxed):
291             attrs.extend(self._boxed_attrs(union))
292         with self.tagcontext('union', attrs):
293             if union.fields:
294                 for field in union.fields:
295                     self._write_field(field)
296             if isinstance(union, GLibBoxed):
297                 self._write_boxed_ctors_methods(union)
298
299     def _write_field(self, field):
300         # FIXME: Just function
301         if isinstance(field, (Callback, Function)):
302             self._write_callback(field)
303             return
304
305         attrs = [('name', field.name)]
306         with self.tagcontext('field', attrs):
307             self._write_type(field.type)
308
309     def _write_signal(self, signal):
310         attrs = [('name', signal.name)]
311         with self.tagcontext('glib:signal', attrs):
312             self._write_return_type(signal.retval)
313             self._write_parameters(signal.parameters)