2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Johan Dahlin
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.
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.
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
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
33 class GLibTransformer(object):
34 def __init__(self, transformer):
35 self._transformer = transformer
36 self._namespace_name = None
37 self._output_ns = odict()
43 def load_library(self, libname):
44 if libname.endswith('.la'):
45 libname = resolve_libtool(libname)
46 self._library = ctypes.cdll.LoadLibrary(libname)
49 namespace = self._transformer.parse()
50 self._namespace_name = namespace.name
51 self._type_names = self._transformer.get_type_names()
53 for node in namespace.nodes:
54 self._parse_node(node)
57 for node in self._output_ns.values():
58 # associate GtkButtonClass with GtkButton
59 if isinstance(node, Struct):
60 self._pair_class_struct(node)
62 namespace = Namespace(namespace.name)
63 namespace.nodes = self._output_ns.values()
68 def _add_attribute(self, node, replace=False):
70 if node_name in self._output_ns and not replace:
72 self._output_ns[node_name] = node
74 def _remove_attribute(self, name):
75 del self._output_ns[name]
77 def _get_attribute(self, name):
78 return self._output_ns.get(name)
80 def _register_internal_type(self, type_name, node):
81 self._type_names[type_name] = (None, node)
83 def _resolve_type_name(self, type_name):
84 item = self._type_names.get(type_name)
89 return '%s.%s' % (nsname, item.name)
92 def _resolve_param_type(self, ptype):
93 type_name = ptype.name.replace('*', '')
94 ptype.name = self._resolve_type_name(type_name)
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
110 print 'GOBJECT BUILDER: Unhandled node:', node
112 def _parse_enum(self, enum):
113 self._add_attribute(enum)
115 def _parse_function(self, func):
116 if self._parse_get_type_function(func):
118 elif self._parse_constructor(func):
120 elif self._parse_method(func):
123 self._parse_parameters(func.parameters)
125 self._add_attribute(func)
127 def _parse_parameters(self, parameters):
128 for parameter in parameters:
129 parameter.type = self._resolve_param_type(parameter.type)
131 def _parse_get_type_function(self, func):
132 if self._library is None:
134 # GType *_get_type(void)
136 if not symbol.endswith('_get_type'):
138 if func.retval.type.name != 'GType':
144 func = getattr(self._library, symbol)
145 except AttributeError:
146 print 'Warning: could not find symbol: %s' % symbol
149 func.restype = cgobject.GType
152 self._introspect_type(type_id, symbol)
155 def _parse_method(self, func):
156 if not func.parameters:
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:
166 object_name = first_arg.replace('*', '')
167 return self._parse_method_common(func, object_name, is_method=True)
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:
177 object_name = rtype.name.replace('*', '')
178 return self._parse_method_common(func, object_name, is_method=False)
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)):
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):
193 # Strip namespace and object prefix: gtk_window_new -> new
194 func.name = func.symbol[len(prefix):]
196 class_.methods.append(func)
198 class_.constructors.append(func)
199 self._parse_parameters(func.parameters)
200 func.retval.type = self._resolve_param_type(func.retval.type)
203 def _parse_struct(self, struct):
204 node = self._output_ns.get(struct.name)
206 self._add_attribute(struct, replace=True)
208 node.fields = struct.fields[:]
210 def _parse_callback(self, callback):
211 self._add_attribute(callback)
213 def _pair_class_struct(self, class_node):
214 name = class_node.name
215 if (name.endswith('Class') or
216 name.endswith('Iface')):
218 elif name.endswith('Interface'):
223 node = self._output_ns.get(self._resolve_type_name(name))
224 del self._output_ns[class_node.name]
227 for field in class_node.fields[1:]:
228 node.fields.append(field)
230 def _create_type(self, type_id):
231 type_name = cgobject.type_name(type_id)
232 return Type(type_name)
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)
246 print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
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:
254 for enum_value in type_class.get_values():
255 members.append(GLibEnumMember(enum_value.value_nick,
257 enum_value.value_name,
258 enum_value.value_nick))
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)
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),
273 self._introspect_properties(node, type_id)
274 self._introspect_signals(node, type_id)
275 self._add_attribute(node)
277 self._remove_attribute(type_name)
279 print 'Warning: could not remove %s' % type_name
281 self._register_internal_type(type_name, node)
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),
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)
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),
297 self._add_attribute(node)
298 # GdkEvent raises KeyError, FooBoxed ends up duplicated if we don't
300 self._remove_attribute(type_name)
303 self._register_internal_type(type_name, node)
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)
315 if pspec.owner_type != type_id:
317 node.properties.append(Property(
319 cgobject.type_name(pspec.value_type)))
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()):
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)