[typelib] Remove space/indent mismatch
[gnome.gobject-introspection] / giscanner / girparser.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library 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 GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 from xml.etree.cElementTree import parse
22
23 from .ast import (Alias, Array, Callback, Constant, Enum, Function, Field,
24                   Namespace, Parameter, Property, Return, Union, Struct, Type,
25                   Varargs, Include)
26 from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
27                       GLibInterface, GLibObject, GLibBoxedStruct,
28                       GLibBoxedUnion, GLibBoxedOther)
29
30 CORE_NS = "http://www.gtk.org/introspection/core/1.0"
31 C_NS = "http://www.gtk.org/introspection/c/1.0"
32 GLIB_NS = "http://www.gtk.org/introspection/glib/1.0"
33
34
35 def _corens(tag):
36     return '{%s}%s' % (CORE_NS, tag)
37
38
39 def _glibns(tag):
40     return '{%s}%s' % (GLIB_NS, tag)
41
42
43 def _cns(tag):
44     return '{%s}%s' % (C_NS, tag)
45
46
47 class GIRParser(object):
48
49     def __init__(self):
50         self._include_parsing = False
51         self._shared_libraries = []
52         self._includes = set()
53         self._pkgconfig_packages = set()
54         self._namespace = None
55
56     # Public API
57
58     def parse(self, filename):
59         tree = parse(filename)
60         self.parse_tree(tree)
61
62     def parse_tree(self, tree):
63         self._includes.clear()
64         self._namespace = None
65         self._shared_libraries = []
66         self._pkgconfig_packages = set()
67         self._parse_api(tree.getroot())
68
69     def get_namespace(self):
70         return self._namespace
71
72     def get_shared_libraries(self):
73         return self._shared_libraries
74
75     def get_includes(self):
76         return self._includes
77
78     def get_pkgconfig_packages(self):
79         if not hasattr(self, '_pkgconfig_packages'):
80             self._pkgconfig_packages = []
81         return self._pkgconfig_packages
82
83     def get_doc(self):
84         return parse(self._filename)
85
86     def set_include_parsing(self, include_parsing):
87         self._include_parsing = include_parsing
88
89     # Private
90
91     def _add_node(self, node):
92         self._namespace.nodes.append(node)
93
94     def _parse_api(self, root):
95         assert root.tag == _corens('repository')
96         for node in root.getchildren():
97             if node.tag == _corens('include'):
98                 self._parse_include(node)
99             elif node.tag == _corens('package'):
100                 self._parse_pkgconfig_package(node)
101
102         ns = root.find(_corens('namespace'))
103         assert ns is not None
104         self._namespace = Namespace(ns.attrib['name'],
105                                     ns.attrib['version'])
106         if 'shared-library' in ns.attrib:
107             self._shared_libraries.extend(
108                 ns.attrib['shared-library'].split(','))
109
110         parser_methods = {
111             _corens('alias'): self._parse_alias,
112             _corens('bitfield'): self._parse_enumeration_bitfield,
113             _corens('callback'): self._parse_callback,
114             _corens('class'): self._parse_object_interface,
115             _corens('constant'): self._parse_constant,
116             _corens('function'): self._parse_function,
117             _corens('enumeration'): self._parse_enumeration_bitfield,
118             _corens('interface'): self._parse_object_interface,
119             _corens('record'): self._parse_record,
120             _corens('union'): self._parse_union,
121             _corens('boxed'): self._parse_boxed,
122             }
123
124         for node in ns.getchildren():
125             method = parser_methods.get(node.tag)
126             if method is not None:
127                 method(node)
128
129     def _parse_include(self, node):
130         include = Include(node.attrib['name'],
131                           node.attrib['version'])
132         self._includes.add(include)
133
134     def _parse_pkgconfig_package(self, node):
135         if not hasattr(self, '_pkgconfig_packages'):
136             self._pkgconfig_packages = []
137         self._pkgconfig_packages.add(node.attrib['name'])
138
139     def _parse_alias(self, node):
140         alias = Alias(node.attrib['name'],
141                       node.attrib['target'],
142                       node.attrib.get(_cns('type')))
143         self._add_node(alias)
144
145     def _parse_object_interface(self, node):
146         ctor_args = [node.attrib['name'],
147                      node.attrib.get('parent'),
148                      node.attrib[_glibns('type-name')],
149                      node.attrib[_glibns('get-type')]]
150         if node.tag == _corens('interface'):
151             klass = GLibInterface
152         elif node.tag == _corens('class'):
153             klass = GLibObject
154             is_abstract = node.attrib.get('abstract')
155             is_abstract = is_abstract and is_abstract != '0'
156             ctor_args.append(is_abstract)
157         else:
158             raise AssertionError(node)
159
160         obj = klass(*ctor_args)
161         self._add_node(obj)
162
163         if self._include_parsing:
164             return
165         ctor_args.append(node.attrib.get(_cns('type')))
166         for iface in node.findall(_corens('implements')):
167             obj.interfaces.append(iface.attrib['name'])
168         for iface in node.findall(_corens('prerequisites')):
169             obj.prerequisities.append(iface.attrib['name'])
170         for method in node.findall(_corens('method')):
171             func = self._parse_function_common(method, Function)
172             func.is_method = True
173             obj.methods.append(func)
174         for ctor in node.findall(_corens('constructor')):
175             obj.constructors.append(
176                 self._parse_function_common(ctor, Function))
177         for callback in node.findall(_corens('callback')):
178             obj.fields.append(self._parse_function_common(callback, Callback))
179         for field in node.findall(_corens('field')):
180             obj.fields.append(self._parse_field(field))
181         for prop in node.findall(_corens('property')):
182             obj.properties.append(self._parse_property(prop))
183         for signal in node.findall(_glibns('signal')):
184             obj.signals.append(self._parse_function_common(signal, Function))
185
186     def _parse_callback(self, node):
187         callback = self._parse_function_common(node, Callback)
188         self._add_node(callback)
189
190     def _parse_function(self, node):
191         function = self._parse_function_common(node, Function)
192         self._add_node(function)
193
194     def _parse_function_common(self, node, klass):
195         name = node.attrib['name']
196         returnnode = node.find(_corens('return-value'))
197         if not returnnode:
198             raise ValueError('node %r has no return-value' % (name, ))
199         transfer = returnnode.attrib.get('transfer-ownership')
200         retval = Return(self._parse_type(returnnode), transfer)
201         parameters = []
202
203         if klass is Callback:
204             func = klass(name, retval, parameters,
205                          node.attrib.get(_cns('type')))
206         else:
207             identifier = node.attrib.get(_cns('identifier'))
208             throws = (node.attrib.get('throws') == '1')
209             func = klass(name, retval, parameters, identifier, throws)
210
211         if self._include_parsing:
212             return func
213
214         parameters_node = node.find(_corens('parameters'))
215         if (parameters_node is not None):
216             for paramnode in parameters_node.findall(_corens('parameter')):
217                 param = Parameter(paramnode.attrib.get('name'),
218                                   self._parse_type(paramnode),
219                                   paramnode.attrib.get('direction'),
220                                   paramnode.attrib.get('transfer-ownership'),
221                                   paramnode.attrib.get('allow-none') == '1')
222                 parameters.append(param)
223
224         return func
225
226     def _parse_record(self, node):
227         if _glibns('type-name') in node.attrib:
228             struct = GLibBoxedStruct(node.attrib['name'],
229                                      node.attrib[_glibns('type-name')],
230                                      node.attrib[_glibns('get-type')],
231                                      node.attrib.get(_cns('type')))
232         else:
233             disguised = node.attrib.get('disguised') == '1'
234             struct = Struct(node.attrib['name'],
235                             node.attrib.get(_cns('type')),
236                             disguised=disguised)
237         self._add_node(struct)
238
239         if self._include_parsing:
240             return
241         for field in node.findall(_corens('field')):
242             struct.fields.append(self._parse_field(field))
243         for callback in node.findall(_corens('callback')):
244             struct.fields.append(
245                 self._parse_function_common(callback, Callback))
246         for method in node.findall(_corens('method')):
247             struct.fields.append(
248                 self._parse_function_common(method, Function))
249         for ctor in node.findall(_corens('constructor')):
250             struct.constructors.append(
251                 self._parse_function_common(ctor, Function))
252
253     def _parse_union(self, node):
254         if _glibns('type-name') in node.attrib:
255             union = GLibBoxedUnion(node.attrib['name'],
256                                     node.attrib[_glibns('type-name')],
257                                     node.attrib[_glibns('get-type')],
258                                     node.attrib.get(_cns('type')))
259         else:
260             union = Union(node.attrib['name'],
261                           node.attrib.get(_cns('type')))
262         self._add_node(union)
263
264         if self._include_parsing:
265             return
266         for callback in node.findall(_corens('callback')):
267             union.fields.append(
268                 self._parse_function_common(callback, Callback))
269         for field in node.findall(_corens('field')):
270             union.fields.append(self._parse_field(field))
271         for method in node.findall(_corens('method')):
272             union.fields.append(
273                 self._parse_function_common(method, Function))
274         for ctor in node.findall(_corens('constructor')):
275             union.constructors.append(
276                 self._parse_function_common(ctor, Function))
277
278     def _parse_type(self, node):
279         typenode = node.find(_corens('type'))
280         if typenode is not None:
281             return Type(typenode.attrib['name'],
282                         typenode.attrib.get(_cns('type')))
283         typenode = node.find(_corens('array'))
284         if typenode is not None:
285             ret = Array(typenode.attrib.get(_cns('type')),
286                         self._parse_type(typenode))
287             lenidx = typenode.attrib.get('length')
288             if lenidx:
289                 ret.length_param_index = int(lenidx)
290             return ret
291         typenode = node.find(_corens('varargs'))
292         if typenode is not None:
293             return Varargs()
294         raise ValueError("Couldn't parse type of node %r; children=%r",
295                          node, list(node))
296
297     def _parse_boxed(self, node):
298         obj = GLibBoxedOther(node.attrib[_glibns('name')],
299                              node.attrib[_glibns('type-name')],
300                              node.attrib[_glibns('get-type')])
301         self._add_node(obj)
302         if self._include_parsing:
303             return
304         for method in node.findall(_corens('method')):
305             func = self._parse_function_common(method, Function)
306             func.is_method = True
307             obj.methods.append(func)
308         for ctor in node.findall(_corens('constructor')):
309             obj.constructors.append(
310                 self._parse_function_common(ctor, Function))
311         for callback in node.findall(_corens('callback')):
312             obj.fields.append(
313                 self._parse_function_common(callback, Callback))
314
315     def _parse_field(self, node):
316         type_node = self._parse_type(node)
317         return Field(node.attrib['name'],
318                      type_node,
319                      type_node.ctype,
320                      node.attrib.get('readable') != '0',
321                      node.attrib.get('writable') == '1',
322                      node.attrib.get('bits'))
323
324     def _parse_property(self, node):
325         type_node = self._parse_type(node)
326         return Property(node.attrib['name'],
327                         type_node.name,
328                         node.attrib.get('readable') != '0',
329                         node.attrib.get('writable') == '1',
330                         node.attrib.get('construct') == '1',
331                         node.attrib.get('construct-only') == '1',
332                         type_node.ctype)
333
334     def _parse_member(self, node):
335         return GLibEnumMember(node.attrib['name'],
336                               node.attrib['value'],
337                               node.attrib.get(_cns('identifier')),
338                               node.attrib.get(_glibns('nick')))
339
340     def _parse_constant(self, node):
341         type_node = self._parse_type(node)
342         constant = Constant(node.attrib['name'],
343                             type_node.name,
344                             node.attrib['value'])
345         self._add_node(constant)
346
347     def _parse_enumeration_bitfield(self, node):
348         name = node.attrib.get('name')
349         ctype = node.attrib.get(_cns('type'))
350         get_type = node.attrib.get(_glibns('get-type'))
351         type_name = node.attrib.get(_glibns('type-name'))
352         if get_type:
353             if node.tag == _corens('bitfield'):
354                 klass = GLibFlags
355             else:
356                 klass = GLibEnum
357         else:
358             klass = Enum
359             type_name = ctype
360         members = []
361         if klass is Enum:
362             obj = klass(name, type_name, members)
363         else:
364             obj = klass(name, type_name, members, get_type)
365             obj.ctype = ctype
366         self._add_node(obj)
367
368         if self._include_parsing:
369             return
370         for member in node.findall(_corens('member')):
371             members.append(self._parse_member(member))