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