Rename most c:identifier to c:type. Add new ones to
[platform/upstream/gobject-introspection.git] / giscanner / glibtransformer.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 import ctypes
22 import re
23 import os
24
25 from . import cgobject
26 from .odict import odict
27 from .ast import (Callback, Enum, Function, Parameter, Property, Return,
28                   Sequence, Struct)
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30                       GLibInterface, GLibObject, GLibSignal)
31
32
33 # Copied from h2defs.py
34 _upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
35 _upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
36 _upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
37
38 def to_underscores(name):
39     """Converts a typename to the equivalent underscores name.
40     This is used to form the type conversion macros and enum/flag
41     name variables"""
42     name = _upperstr_pat1.sub(r'\1_\2', name)
43     name = _upperstr_pat2.sub(r'\1_\2', name)
44     name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
45     return name
46
47 _libtool_pat = re.compile("dlname='([A-z0-9\.]+)'\n")
48
49 def resolve_libtool(libname):
50     data = open(libname).read()
51     filename = _libtool_pat.search(data).groups()[0]
52     libname = os.path.join(os.path.dirname(libname),
53                            '.libs', filename)
54     return libname
55
56
57
58
59 class GLibTransformer(object):
60     def __init__(self, namespace_name):
61         self._namespace_name = namespace_name
62         self._output_ns = odict()
63         self._library = None
64         self._type_names = {}
65
66     # Public API
67
68     def get_nodes(self):
69         return self._output_ns.values()
70
71     def load_library(self, libname):
72         if libname.endswith('.la'):
73             libname = resolve_libtool(libname)
74         self._library = ctypes.cdll.LoadLibrary(libname)
75
76     def parse(self, nodes):
77         for node in nodes:
78             self._parse_node(node)
79
80         # Second round, associate GtkButtonClass with GtkButton
81         for node in self._output_ns.values():
82             if isinstance(node, Struct):
83                 self._pair_class_struct(node)
84
85     def register_include(self, filename):
86         if filename.endswith('.gir'):
87             from .girparser import GIRParser
88             parser = GIRParser(filename)
89         elif filename.endswith('.gidl'):
90             from .gidlparser import GIDLParser
91             parser = GIDLParser(filename)
92         else:
93             raise NotImplementedError(filename)
94         nsname = parser.get_namespace_name()
95         for node in parser.get_nodes():
96             self._type_names[node.type_name] = (nsname, node)
97
98     # Private
99
100     def _add_attribute(self, node, replace=False):
101         node_name = node.name
102         if node_name in self._output_ns and not replace:
103             return
104         self._output_ns[node_name] = node
105
106     def _remove_attribute(self, name):
107         del self._output_ns[name]
108
109     def _get_attribute(self, name):
110         return self._output_ns.get(name)
111
112     def _register_internal_type(self, type_name, node):
113         self._type_names[type_name] = (None, node)
114
115     def _strip_namespace_func(self, name):
116         orig_name = name
117         prefix = self._namespace_name.lower() + '_'
118         name = name.lower()
119         if name.startswith(prefix):
120             name = orig_name[len(prefix):]
121         return name
122
123     def _strip_namespace_object(self, name):
124         orig_name = name
125         prefix = self._namespace_name.lower()
126         name = name.lower()
127         if name.startswith(prefix):
128             name = orig_name[len(prefix):]
129         return name
130
131     def _resolve_type_name(self, type_name):
132         item = self._type_names.get(type_name)
133         if item is not None:
134             nsname, item = item
135             if nsname is None:
136                 return item.name
137             return '%s.%s' % (nsname, item.name)
138         return type_name
139
140     def _resolve_param_type(self, ptype):
141         type_name = ptype.name.replace('*', '')
142         ptype.name = self._resolve_type_name(type_name)
143         return ptype
144
145     def _parse_node(self, node):
146         if isinstance(node, Enum):
147             self._parse_enum(node)
148         elif isinstance(node, Function):
149             self._parse_function(node)
150         elif isinstance(node, Struct):
151             self._parse_struct(node)
152         elif isinstance(node, Callback):
153             self._parse_callback(node)
154         else:
155             print 'GOBJECT BUILDER: Unhandled node:', node
156
157     def _parse_enum(self, enum):
158         enum.name = self._strip_namespace_object(enum.name)
159         self._add_attribute(enum)
160
161     def _parse_function(self, func):
162         if self._parse_get_type_function(func):
163             return
164         elif self._parse_constructor(func):
165             return
166         elif self._parse_method(func):
167             return
168
169         self._parse_parameters(func.parameters)
170         func.retval.type = self._resolve_param_type(func.retval.type)
171
172         func.name = self._strip_namespace_func(func.name)
173         self._add_attribute(func)
174
175     def _parse_parameters(self, parameters):
176         for parameter in parameters:
177             parameter.type = self._resolve_param_type(parameter.type)
178
179     def _parse_get_type_function(self, func):
180         # GType *_get_type(void)
181         symbol = func.name
182         if not symbol.endswith('_get_type'):
183             return False
184         if func.retval.type.name != 'GType':
185             return False
186         if func.parameters:
187             return False
188
189         func = getattr(self._library, symbol)
190         func.restype = cgobject.GType
191         func.argtypes = []
192         type_id = func()
193         self._introspect_type(type_id, symbol)
194         return True
195
196     def _parse_method(self, func):
197         if not func.parameters:
198             return False
199
200         # FIXME: This is hackish, we should preserve the pointer structures
201         #        here, so we can find pointers to objects and not just
202         #        pointers to anything
203         first_arg = func.parameters[0].type.name
204         if first_arg.count('*') != 1:
205             return False
206
207         object_name = first_arg.replace('*', '')
208         return self._parse_method_common(func, object_name, is_method=True)
209
210     def _parse_constructor(self, func):
211         # FIXME: This is hackish, we should preserve the pointer structures
212         #        here, so we can find pointers to objects and not just
213         #        pointers to anything
214         rtype = func.retval.type
215         if rtype.name.count('*') != 1:
216             return False
217
218         object_name = rtype.name.replace('*', '')
219         return self._parse_method_common(func, object_name, is_method=False)
220
221     def _parse_method_common(self, func, object_name, is_method):
222         orig_name = object_name
223         if object_name.lower().startswith(self._namespace_name.lower()):
224             object_name = object_name[len(self._namespace_name):]
225         class_ = self._get_attribute(object_name)
226         if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
227             return False
228
229         # GtkButton -> gtk_button_, so we can figure out the method name
230         prefix = to_underscores(orig_name).lower() + '_'
231         if not func.name.startswith(prefix):
232             return False
233
234         # Strip namespace and object prefix: gtk_window_new -> new
235         func.name = func.name[len(prefix):]
236         if is_method:
237             class_.methods.append(func)
238         else:
239             class_.constructors.append(func)
240         self._parse_parameters(func.parameters)
241         func.retval.type = self._resolve_param_type(func.retval.type)
242         return True
243
244     def _parse_struct(self, struct):
245         self._add_attribute(struct)
246
247     def _parse_callback(self, callback):
248         self._add_attribute(callback)
249
250     def _pair_class_struct(self, class_node):
251         name = class_node.name
252         if (name.endswith('Class') or
253             name.endswith('Iface')):
254             name = name[:-5]
255         elif name.endswith('Interface'):
256             name = name[:-9]
257         else:
258             return
259
260         node = self._output_ns.get(self._resolve_type_name(name))
261         del self._output_ns[class_node.name]
262         for field in class_node.fields[1:]:
263             node.fields.append(field)
264
265     def _introspect_type(self, type_id, symbol):
266         fundamental_type_id = cgobject.type_fundamental(type_id)
267         if (fundamental_type_id == cgobject.TYPE_ENUM or
268             fundamental_type_id == cgobject.TYPE_FLAGS):
269             self._introspect_enum(fundamental_type_id, type_id, symbol)
270         elif fundamental_type_id == cgobject.TYPE_OBJECT:
271             self._introspect_object(type_id, symbol)
272         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
273             self._introspect_interface(type_id, symbol)
274         elif fundamental_type_id == cgobject.TYPE_BOXED:
275             self._introspect_boxed(type_id, symbol)
276         else:
277             print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
278
279     def _introspect_enum(self, ftype_id, type_id, symbol):
280         type_class = cgobject.type_class_ref(type_id)
281         if type_class is None:
282             return
283
284         members = []
285         for enum_value in type_class.get_values():
286             members.append(GLibEnumMember(enum_value.value_name,
287                                           enum_value.value,
288                                           enum_value.value_nick))
289
290         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
291         type_name = cgobject.type_name(type_id)
292         node = klass(self._strip_namespace_object(type_name),
293                       members, type_name, symbol)
294         self._add_attribute(node, replace=True)
295         self._register_internal_type(type_name, node)
296
297     def _introspect_object(self, type_id, symbol):
298         type_name = cgobject.type_name(type_id)
299         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
300         node = GLibObject(self._strip_namespace_object(type_name),
301                           self._resolve_type_name(parent_type_name),
302                           type_name, symbol)
303         self._introspect_properties(node, type_id)
304         self._introspect_signals(node, type_id)
305         self._add_attribute(node)
306         self._remove_attribute(type_name)
307         self._register_internal_type(type_name, node)
308
309     def _introspect_interface(self, type_id, symbol):
310         type_name = cgobject.type_name(type_id)
311         node = GLibInterface(self._strip_namespace_object(type_name),
312                              type_name, symbol)
313         self._introspect_properties(node, type_id)
314         self._introspect_signals(node, type_id)
315         self._add_attribute(node)
316         self._register_internal_type(type_name, node)
317
318     def _introspect_boxed(self, type_id, symbol):
319         type_name = cgobject.type_name(type_id)
320         node = GLibBoxed(self._strip_namespace_object(type_name),
321                          type_name, symbol)
322         self._add_attribute(node)
323         self._register_internal_type(type_name, node)
324
325     def _introspect_properties(self, node, type_id):
326         fundamental_type_id = cgobject.type_fundamental(type_id)
327         if fundamental_type_id == cgobject.TYPE_OBJECT:
328             pspecs = cgobject.object_class_list_properties(type_id)
329         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
330             pspecs = cgobject.object_interface_list_properties(type_id)
331         else:
332             raise AssertionError
333
334         for pspec in pspecs:
335             if pspec.owner_type != type_id:
336                 continue
337             node.properties.append(Property(
338                 pspec.name,
339                 cgobject.type_name(pspec.value_type)))
340
341     def _introspect_signals(self, node, type_id):
342         for signal_info in cgobject.signal_list(type_id):
343             return_ = Return(cgobject.type_name(signal_info.return_type))
344             signal = GLibSignal(signal_info.signal_name, return_)
345             for i, parameter in enumerate(signal_info.get_params()):
346                 if i == 0:
347                     name = 'object'
348                 else:
349                     name = 'p%s' % (i-1,)
350                 signal.parameters.append(
351                     Parameter(name, cgobject.type_name(parameter)))
352             node.signals.append(signal)