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