Write record/structs to gir file too. Add a couple of tests, fix an off by
[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, Sequence,
24                   Struct)
25 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
26                       GLibFlags, GLibObject, GLibInterface)
27 from .xmlwriter import XMLWriter
28
29
30 class GIRWriter(XMLWriter):
31     def __init__(self, namespace, nodes):
32         super(GIRWriter, self).__init__()
33         self._write_repository(namespace, nodes)
34
35     def _write_repository(self, namespace, nodes):
36         attrs = [
37             ('version', '1.0'),
38             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
39             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
40             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
41             ]
42         with self.tagcontext('repository', attrs):
43             self._write_namespace(namespace, nodes)
44
45     def _write_namespace(self, namespace, nodes):
46         with self.tagcontext('namespace', [('name', namespace)]):
47             for node in nodes:
48                 self._write_node(node)
49
50     def _write_node(self, node):
51         if isinstance(node, Function):
52             self._write_function(node)
53         elif isinstance(node, Enum):
54             self._write_enum(node)
55         elif isinstance(node, (Class, Interface)):
56             self._write_class(node)
57         elif isinstance(node, GLibBoxed):
58             self._write_boxed(node)
59         elif isinstance(node, Callback):
60             self._write_callback(node)
61         elif isinstance(node, Struct):
62             self._write_record(node)
63         else:
64             print 'WRITER: Unhandled node', node
65
66     def _write_function(self, func, tag_name='function'):
67         attrs = [('name', func.name),
68                  ('c:identifier', func.symbol)]
69         with self.tagcontext(tag_name, attrs):
70             self._write_return_type(func.retval)
71             self._write_parameters(func.parameters)
72
73     def _write_method(self, method):
74         self._write_function(method, tag_name='method')
75
76     def _write_constructor(self, method):
77         self._write_function(method, tag_name='constructor')
78
79     def _write_return_type(self, return_):
80         if not return_:
81             return
82         with self.tagcontext('return-value'):
83             if isinstance(return_.type, Sequence):
84                 self._write_sequence(return_.type)
85             else:
86                 self._write_type(return_.type)
87
88     def _write_parameters(self, parameters):
89         if not parameters:
90             return
91         with self.tagcontext('parameters'):
92             for parameter in parameters:
93                 self._write_parameter(parameter)
94
95     def _write_parameter(self, parameter):
96         attrs = [('name', parameter.name)]
97         if parameter.direction != 'in':
98             attrs.append(('direction', parameter.direction))
99         if parameter.transfer != 'none':
100             attrs.append(('transfer', parameter.transfer))
101         with self.tagcontext('parameter', attrs):
102             self._write_type(parameter.type)
103
104     def _write_type(self, type):
105         attrs = [('name', type.name)]
106         # FIXME: figure out if type references a basic type
107         #        or a boxed/class/interface etc. and skip
108         #        writing the ctype if the latter.
109         if 1:
110             attrs.append(('c:type', type.ctype))
111         self.write_tag('type', attrs)
112
113     def _write_sequence(self, sequence):
114         attrs = [('c:owner', sequence.cowner)]
115         with self.tagcontext('sequence', attrs):
116             attrs = [('c:identifier', sequence.element_type)]
117             self.write_tag('element-type', attrs)
118
119     def _write_enum(self, enum):
120         attrs = [('name', enum.name),
121                  ('c:type', enum.ctype)]
122         tag_name = 'enumeration'
123         if isinstance(enum, GLibEnum):
124             attrs.extend([('glib:type-name', enum.type_name),
125                           ('glib:get-type', enum.get_type)])
126             if isinstance(enum, GLibFlags):
127                 tag_name = 'bitfield'
128
129         with self.tagcontext(tag_name, attrs):
130             for member in enum.members:
131                 self._write_member(member)
132
133     def _write_member(self, member):
134         attrs = [('name', member.name),
135                  ('value', str(member.value))]
136         if isinstance(member, GLibEnumMember):
137             attrs.append(('glib:nick', member.nick))
138         self.write_tag('member', attrs)
139
140     def _write_class(self, node):
141         attrs = [('name', node.name),
142                  ('c:type', node.ctype)]
143         if isinstance(node, Class):
144             tag_name = 'class'
145             if node.parent is not None:
146                 attrs.append(('parent', node.parent))
147         else:
148             tag_name = 'interface'
149         if isinstance(node, (GLibObject, GLibInterface)):
150             attrs.append(('glib:type-name', node.type_name))
151             attrs.append(('glib:get-type', node.get_type))
152         with self.tagcontext(tag_name, attrs):
153             if isinstance(node, Class):
154                 for method in node.constructors:
155                     self._write_constructor(method)
156             for method in node.methods:
157                 self._write_method(method)
158             for prop in node.properties:
159                 self._write_property(prop)
160             for field in node.fields:
161                 self._write_field(field)
162             for signal in node.signals:
163                 self._write_signal(signal)
164
165     def _write_boxed(self, boxed):
166         attrs = [('c:type', boxed.ctype),
167                  ('glib:name', boxed.name),
168                  ('glib:type-name', boxed.type_name),
169                  ('glib:get-type', boxed.get_type)]
170
171         with self.tagcontext('glib:boxed', attrs):
172             for method in boxed.constructors:
173                 self._write_constructor(method)
174             for method in boxed.methods:
175                 self._write_method(method)
176
177     def _write_property(self, prop):
178         attrs = [('name', prop.name)]
179         with self.tagcontext('property', attrs):
180             self._write_type(prop.type)
181
182     def _write_callback(self, callback):
183         attrs = [('name', callback.name)]
184         with self.tagcontext('callback', attrs):
185             self._write_return_type(callback.retval)
186             self._write_parameters(callback.parameters)
187
188     def _write_record(self, record):
189         attrs = [('name', record.name),
190                  ('c:type', record.symbol)]
191         if record.fields:
192             with self.tagcontext('record', attrs):
193                 for field in record.fields:
194                     self._write_field(field)
195         else:
196             self.write_tag('record', attrs)
197
198     def _write_field(self, field):
199         if isinstance(field, Callback):
200             self._write_callback(field)
201             return
202
203         attrs = [('name', field.name),
204                  ('type', str(field.type))]
205         self.write_tag('field', attrs)
206
207     def _write_signal(self, signal):
208         attrs = [('name', signal.name)]
209         with self.tagcontext('glib:signal', attrs):
210             self._write_return_type(signal.retval)
211             self._write_parameters(signal.parameters)
212