5649e0365932ddc431670962063198e79849fed8
[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 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 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, filename, initial_parse=True):
50         self._includes = set()
51         self._namespace = None
52         self._shared_libraries = []
53         self._tree = parse(filename)
54
55         if (initial_parse):
56             self.parse()
57
58     # Public API
59
60     def parse(self):
61         self._includes.clear()
62         del self._namespace
63         del self._shared_libraries[:]
64
65         self._parse_api(self._tree.getroot())
66
67     def get_namespace(self):
68         return self._namespace
69
70     def get_shared_libraries(self):
71         return self._shared_libraries
72
73     def get_includes(self):
74         return self._includes
75
76     def get_doc(self):
77         return self._tree
78
79     # Private
80
81     def _add_node(self, node):
82         self._namespace.nodes.append(node)
83
84     def _parse_api(self, root):
85         assert root.tag == _corens('repository')
86         for node in root.getchildren():
87             if node.tag == _corens('include'):
88                 include = Include(node.attrib['name'],
89                                   node.attrib['version'])
90                 self._includes.add(include)
91         ns = root.find(_corens('namespace'))
92         assert ns is not None
93         self._namespace = Namespace(ns.attrib['name'], ns.attrib['version'])
94         self._shared_libraries.extend(ns.attrib['shared-library'].split(','))
95         for child in ns.getchildren():
96             self._parse_node(child)
97
98     def _parse_node(self, node):
99         if node.tag == _corens('alias'):
100             self._add_node(self._parse_alias(node))
101         elif node.tag in [_corens('callback')]:
102             self._add_node(self._parse_function(node, Callback))
103         elif node.tag in [_corens('function')]:
104             self._add_node(self._parse_function(node, Function))
105         elif node.tag in [_corens('class'),
106                           _corens('interface')]:
107             self._parse_object_interface(node)
108         elif node.tag == _corens('record'):
109             self._parse_struct(node)
110         elif node.tag == _corens('union'):
111             self._parse_union(node)
112         elif node.tag == _glibns('boxed'):
113             self._parse_boxed(node)
114         elif node.tag in [_corens('enumeration'),
115                           _corens('bitfield')]:
116             self._parse_enumeration_bitfield(node)
117         elif node.tag in _corens('constant'):
118             self._add_node(self._parse_constant(node))
119
120     def _parse_alias(self, node):
121         return Alias(node.attrib['name'],
122                      node.attrib['target'],
123                      node.attrib.get(_cns('type')))
124
125     def _parse_object_interface(self, node):
126         if node.tag == _corens('interface'):
127             klass = GLibInterface
128         elif node.tag == _corens('class'):
129             klass = GLibObject
130         else:
131             raise AssertionError(node)
132
133         obj = klass(node.attrib['name'],
134                     node.attrib.get('parent'),
135                     node.attrib[_glibns('type-name')],
136                     node.attrib[_glibns('get-type')],
137                     node.attrib.get(_cns('type')))
138         for iface in node.findall(_corens('implements')):
139             obj.interfaces.append(iface.attrib['name'])
140         for method in node.findall(_corens('method')):
141             obj.methods.append(self._parse_function(method, Function))
142         for ctor in node.findall(_corens('constructor')):
143             obj.constructors.append(self._parse_function(ctor, Function))
144         for callback in node.findall(_corens('callback')):
145             obj.fields.append(self._parse_function(callback, Callback))
146         for field in node.findall(_corens('field')):
147             obj.fields.append(self._parse_field(field))
148         for property in node.findall(_corens('property')):
149             obj.properties.append(self._parse_property(property))
150         for signal in node.findall(_glibns('signal')):
151             obj.signals.append(self._parse_function(signal, Function))
152         self._add_node(obj)
153
154     def _parse_function(self, node, klass):
155         name = node.attrib['name']
156         returnnode = node.find(_corens('return-value'))
157         if not returnnode:
158             raise ValueError('node %r has no return-value' % (name, ))
159         transfer = returnnode.attrib.get('transfer-ownership')
160         retval = Return(self._parse_type(returnnode), transfer)
161         parameters_node = node.find(_corens('parameters'))
162         parameters = []
163         if (parameters_node is not None):
164             for paramnode in parameters_node.findall(_corens('parameter')):
165                 parameters.append(Parameter(paramnode.attrib.get('name'),
166                                   self._parse_type(paramnode)))
167         if klass is Callback:
168             return klass(name, retval, parameters,
169                          node.attrib.get(_cns('type')))
170         else:
171             identifier = node.attrib.get(_cns('identifier'))
172             return klass(name, retval, parameters, identifier)
173
174     def _parse_struct(self, node):
175         if _glibns('type-name') in node.attrib:
176             struct = GLibBoxedStruct(node.attrib['name'],
177                                      node.attrib[_glibns('type-name')],
178                                      node.attrib[_glibns('get-type')],
179                                      node.attrib.get(_cns('type')))
180         else:
181             struct = Struct(node.attrib['name'],
182                             node.attrib.get(_cns('type')))
183         for field in node.findall(_corens('field')):
184             struct.fields.append(self._parse_field(field))
185         for callback in node.findall(_corens('callback')):
186             struct.fields.append(self._parse_function(callback, Callback))
187         for method in node.findall(_corens('method')):
188             struct.fields.append(self._parse_function(method, Function))
189         for ctor in node.findall(_corens('constructor')):
190             struct.constructors.append(self._parse_function(ctor, Function))
191         self._add_node(struct)
192
193     def _parse_union(self, node):
194         if _glibns('type-name') in node.attrib:
195             struct = GLibBoxedUnion(node.attrib['name'],
196                                     node.attrib[_glibns('type-name')],
197                                     node.attrib[_glibns('get-type')],
198                                     node.attrib.get(_cns('type')))
199         else:
200             struct = Union(node.attrib['name'],
201                            node.attrib.get(_cns('type')))
202         for callback in node.findall(_corens('callback')):
203             struct.fields.append(self._parse_function(callback, Callback))
204         for field in node.findall(_corens('field')):
205             struct.fields.append(self._parse_field(field))
206         for method in node.findall(_corens('method')):
207             struct.fields.append(self._parse_function(method, Function))
208         for ctor in node.findall(_corens('constructor')):
209             struct.constructors.append(self._parse_function(ctor, Function))
210         self._add_node(struct)
211
212     def _parse_type(self, node):
213         typenode = node.find(_corens('type'))
214         if typenode is not None:
215             return Type(typenode.attrib['name'],
216                         typenode.attrib.get(_cns('type')))
217         typenode = node.find(_corens('array'))
218         if typenode is not None:
219             return Array(typenode.attrib.get(_cns('type')),
220                          self._parse_type(typenode))
221         typenode = node.find(_corens('varargs'))
222         if typenode is not None:
223             return Varargs()
224         raise ValueError("Couldn't parse type of node %r; children=%r",
225                          node, list(node))
226
227     def _parse_boxed(self, node):
228         obj = GLibBoxedOther(node.attrib[_glibns('name')],
229                              node.attrib[_glibns('type-name')],
230                              node.attrib[_glibns('get-type')])
231         for method in node.findall(_corens('method')):
232             obj.methods.append(self._parse_function(method, Function))
233         for ctor in node.findall(_corens('constructor')):
234             obj.constructors.append(self._parse_function(ctor, Function))
235         for callback in node.findall(_corens('callback')):
236             obj.fields.append(self._parse_function(callback, Callback))
237         self._add_node(obj)
238
239     def _parse_field(self, node):
240         type_node = self._parse_type(node)
241         return Field(node.attrib['name'],
242                      type_node,
243                      type_node.ctype,
244                      node.attrib.get('bits'))
245
246     def _parse_property(self, node):
247         type_node = self._parse_type(node)
248         return Property(node.attrib['name'],
249                         type_node.name,
250                         node.attrib.get('readable') != '0',
251                         node.attrib.get('writable') == '1',
252                         node.attrib.get('construct') == '1',
253                         node.attrib.get('construct-only') == '1',
254                         type_node.ctype)
255
256     def _parse_member(self, node):
257         return GLibEnumMember(node.attrib['name'],
258                               node.attrib['value'],
259                               node.attrib.get(_cns('identifier')),
260                               node.attrib.get(_glibns('nick')))
261
262     def _parse_constant(self, node):
263         type_node = self._parse_type(node)
264         return Constant(node.attrib['name'],
265                         type_node.name,
266                         node.attrib['value'])
267
268     def _parse_enumeration_bitfield(self, node):
269         name = node.attrib.get('name')
270         ctype = node.attrib.get(_cns('type'))
271         get_type = node.attrib.get(_glibns('get-type'))
272         type_name = node.attrib.get(_glibns('type-name'))
273         if get_type:
274             if node.tag == _corens('bitfield'):
275                 klass = GLibFlags
276             else:
277                 klass = GLibEnum
278         else:
279             klass = Enum
280             type_name = ctype
281         members = []
282         for member in node.findall(_corens('member')):
283             members.append(self._parse_member(member))
284
285         if klass is Enum:
286             obj = klass(name, type_name, members)
287         else:
288             obj = klass(name, type_name, members, get_type)
289             obj.ctype = ctype
290         self._add_node(obj)