Ignore <include>. Parse them. Generate them. Process <include>
[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                   Sequence, 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, GLibBoxed):
69             self._write_boxed(node)
70         elif isinstance(node, Callback):
71             self._write_callback(node)
72         elif isinstance(node, Struct):
73             self._write_record(node)
74         elif isinstance(node, Union):
75             self._write_union(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 _write_alias(self, alias):
85         attrs = [('name', alias.name), ('target', alias.target)]
86         if alias.ctype is not None:
87             attrs.append(('c:type', alias.ctype))
88         self.write_tag('alias', attrs)
89
90     def _write_function(self, func, tag_name='function'):
91         attrs = [('name', func.name),
92                  ('c:identifier', func.symbol)]
93         with self.tagcontext(tag_name, attrs):
94             self._write_return_type(func.retval)
95             self._write_parameters(func.parameters)
96
97     def _write_method(self, method):
98         self._write_function(method, tag_name='method')
99
100     def _write_constructor(self, method):
101         self._write_function(method, tag_name='constructor')
102
103     def _write_return_type(self, return_):
104         if not return_:
105             return
106
107         attrs = []
108         if return_.transfer:
109             attrs.append(('transfer-ownership',
110                           str(int(return_.transfer))))
111         with self.tagcontext('return-value', attrs):
112             self._write_type(return_.type)
113
114     def _write_parameters(self, parameters):
115         if not parameters:
116             return
117         with self.tagcontext('parameters'):
118             for parameter in parameters:
119                 self._write_parameter(parameter)
120
121     def _write_parameter(self, parameter):
122         attrs = []
123         if parameter.name is not None:
124             attrs.append(('name', parameter.name))
125         if parameter.direction != 'in':
126             attrs.append(('direction', parameter.direction))
127         if parameter.transfer:
128             attrs.append(('transfer-ownership',
129                           str(int(parameter.transfer))))
130         if parameter.allow_none:
131             attrs.append(('allow-none', '1'))
132         with self.tagcontext('parameter', attrs):
133             self._write_type(parameter.type)
134
135     def _write_type(self, ntype, relation=None):
136         if isinstance(ntype, basestring):
137             typename = ntype
138             type_cname = None
139         else:
140             typename = ntype.name
141             type_cname = ntype.ctype
142         attrs = [('name', typename)]
143         if relation:
144             attrs.append(('relation', relation))
145         if isinstance(ntype, Sequence):
146             if ntype.transfer:
147                 attrs.append(('transfer-ownership',
148                               str(int(ntype.transfer))))
149             with self.tagcontext('type', attrs):
150                 self._write_type(ntype.element_type, relation="element")
151         else:
152             # FIXME: figure out if type references a basic type
153             #        or a boxed/class/interface etc. and skip
154             #        writing the ctype if the latter.
155             if type_cname is not None:
156                 attrs.append(('c:type', type_cname))
157             self.write_tag('type', attrs)
158
159     def _write_enum(self, enum):
160         attrs = [('name', enum.name),
161                  ('c:type', enum.symbol)]
162         tag_name = 'enumeration'
163         if isinstance(enum, GLibEnum):
164             attrs.extend([('glib:type-name', enum.type_name),
165                           ('glib:get-type', enum.get_type)])
166             if isinstance(enum, GLibFlags):
167                 tag_name = 'bitfield'
168
169         with self.tagcontext(tag_name, attrs):
170             for member in enum.members:
171                 self._write_member(member)
172
173     def _write_member(self, member):
174         attrs = [('name', member.name),
175                  ('value', str(member.value)),
176                  ('c:identifier', member.symbol)]
177         if isinstance(member, GLibEnumMember):
178             attrs.append(('glib:nick', member.nick))
179         self.write_tag('member', attrs)
180
181     def _write_class(self, node):
182         attrs = [('name', node.name),
183                  ('c:type', node.ctype)]
184         if isinstance(node, Class):
185             tag_name = 'class'
186             if node.parent is not None:
187                 attrs.append(('parent', node.parent))
188         else:
189             tag_name = 'interface'
190         if isinstance(node, (GLibObject, GLibInterface)):
191             attrs.append(('glib:type-name', node.type_name))
192             if node.get_type:
193                 attrs.append(('glib:get-type', node.get_type))
194         with self.tagcontext(tag_name, attrs):
195             if isinstance(node, Class):
196                 for method in node.constructors:
197                     self._write_constructor(method)
198             for method in node.methods:
199                 self._write_method(method)
200             for prop in node.properties:
201                 self._write_property(prop)
202             for field in node.fields:
203                 self._write_field(field)
204             for signal in node.signals:
205                 self._write_signal(signal)
206
207     def _write_boxed(self, boxed):
208         attrs = [('c:type', boxed.ctype),
209                  ('glib:name', boxed.name),
210                  ('glib:type-name', boxed.type_name),
211                  ('glib:get-type', boxed.get_type)]
212
213         with self.tagcontext('glib:boxed', attrs):
214             for method in boxed.constructors:
215                 self._write_constructor(method)
216             for method in boxed.methods:
217                 self._write_method(method)
218
219     def _write_property(self, prop):
220         attrs = [('name', prop.name)]
221         with self.tagcontext('property', attrs):
222             self._write_type(prop.type)
223
224     def _write_callback(self, callback):
225         # FIXME: reuse _write_function
226         attrs = [('name', callback.name), ('c:type', callback.ctype)]
227         with self.tagcontext('callback', attrs):
228             self._write_return_type(callback.retval)
229             self._write_parameters(callback.parameters)
230
231     def _write_record(self, record):
232         attrs = [('name', record.name),
233                  ('c:type', record.symbol)]
234         if record.fields:
235             with self.tagcontext('record', attrs):
236                 for field in record.fields:
237                     self._write_field(field)
238         else:
239             self.write_tag('record', attrs)
240
241     def _write_union(self, union):
242         attrs = [('name', union.name),
243                  ('c:type', union.symbol)]
244         if union.fields:
245             with self.tagcontext('union', attrs):
246                 for field in union.fields:
247                     self._write_field(field)
248         else:
249             self.write_tag('union', attrs)
250
251     def _write_field(self, field):
252         # FIXME: Just function
253         if isinstance(field, (Callback, Function)):
254             self._write_callback(field)
255             return
256
257         attrs = [('name', field.name)]
258         with self.tagcontext('field', attrs):
259             self._write_type(field.type)
260
261     def _write_signal(self, signal):
262         attrs = [('name', signal.name)]
263         with self.tagcontext('glib:signal', attrs):
264             self._write_return_type(signal.retval)
265             self._write_parameters(signal.parameters)