]> git.0d.be Git - empathy.git/blob - tools/libglibcodegen.py
Update tools/.gitignore
[empathy.git] / tools / libglibcodegen.py
1 """Library code for GLib/D-Bus-related code generation.
2
3 The master copy of this library is in the telepathy-glib repository -
4 please make any changes there.
5 """
6
7 # Copyright (C) 2006, 2007 Collabora Limited
8 #
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
13 #
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22
23
24 from string import ascii_letters, digits
25
26
27 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
28
29 _ASCII_ALNUM = ascii_letters + digits
30
31
32 def camelcase_to_lower(s):
33     out ="";
34     out += s[0].lower()
35     last_upper=False
36     if s[0].isupper():
37         last_upper=True
38     for i in range(1,len(s)):
39         if s[i].isupper():
40             if last_upper:
41                 if (i+1) < len(s) and  s[i+1].islower():
42                     out += "_" + s[i].lower()
43                 else:
44                     out += s[i].lower()
45             else:
46                 out += "_" + s[i].lower()
47             last_upper=True
48         else:
49             out += s[i]
50             last_upper=False
51     return out
52
53
54 def camelcase_to_upper(s):
55     return camelcase_to_lower(s).upper()
56
57
58 def cmp_by_name(node1, node2):
59     return cmp(node1.getAttributeNode("name").nodeValue,
60                node2.getAttributeNode("name").nodeValue)
61
62
63 def dbus_gutils_wincaps_to_uscore(s):
64     """Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore
65     which gets sequences of capital letters wrong in the same way.
66     (e.g. in Telepathy, SendDTMF -> send_dt_mf)
67     """
68     ret = ''
69     for c in s:
70         if c >= 'A' and c <= 'Z':
71             length = len(ret)
72             if length > 0 and (length < 2 or ret[length-2] != '_'):
73                 ret += '_'
74             ret += c.lower()
75         else:
76             ret += c
77     return ret
78
79
80 def escape_as_identifier(identifier):
81     """Escape the given string to be a valid D-Bus object path or service
82     name component, using a reversible encoding to ensure uniqueness.
83
84     The reversible encoding is as follows:
85
86     * The empty string becomes '_'
87     * Otherwise, each non-alphanumeric character is replaced by '_' plus
88       two lower-case hex digits; the same replacement is carried out on
89       the first character, if it's a digit
90     """
91     # '' -> '_'
92     if not identifier:
93         return '_'
94
95     # A bit of a fast path for strings which are already OK.
96     # We deliberately omit '_' because, for reversibility, that must also
97     # be escaped.
98     if (identifier.strip(_ASCII_ALNUM) == '' and
99         identifier[0] in ascii_letters):
100         return identifier
101
102     # The first character may not be a digit
103     if identifier[0] not in ascii_letters:
104         ret = ['_%02x' % ord(identifier[0])]
105     else:
106         ret = [identifier[0]]
107
108     # Subsequent characters may be digits or ASCII letters
109     for c in identifier[1:]:
110         if c in _ASCII_ALNUM:
111             ret.append(c)
112         else:
113             ret.append('_%02x' % ord(c))
114
115     return ''.join(ret)
116
117
118 def get_docstring(element):
119     docstring = None
120     for x in element.childNodes:
121         if x.namespaceURI == NS_TP and x.localName == 'docstring':
122             docstring = x
123     if docstring is not None:
124         docstring = docstring.toxml().replace('\n', ' ').strip()
125         if docstring.startswith('<tp:docstring>'):
126             docstring = docstring[14:].lstrip()
127         if docstring.endswith('</tp:docstring>'):
128             docstring = docstring[:-15].rstrip()
129         if docstring in ('<tp:docstring/>', ''):
130             docstring = ''
131     return docstring
132
133
134 def signal_to_marshal_type(signal):
135     """
136     return a list of strings indicating the marshalling type for this signal.
137     """
138
139     mtype=[]
140     for i in signal.getElementsByTagName("arg"):
141         name =i.getAttribute("name")
142         type = i.getAttribute("type")
143         mtype.append(type_to_gtype(type)[2])
144
145     return mtype
146
147
148 _glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT',
149         'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT',
150         'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT',
151         'UINT_POINTER']
152
153
154 def signal_to_marshal_name(signal, prefix):
155
156     mtype = signal_to_marshal_type(signal)
157     if len(mtype):
158         name = '_'.join(mtype)
159     else:
160         name = 'VOID'
161
162     if name in _glib_marshallers:
163         return 'g_cclosure_marshal_VOID__' + name
164     else:
165         return prefix + '_marshal_VOID__' + name
166
167
168 def method_to_glue_marshal_name(method, prefix):
169
170     mtype = []
171     for i in method.getElementsByTagName("arg"):
172         if i.getAttribute("direction") != "out":
173             type = i.getAttribute("type")
174             mtype.append(type_to_gtype(type)[2])
175
176     mtype.append('POINTER')
177
178     name = '_'.join(mtype)
179
180     if name in _glib_marshallers:
181         return 'g_cclosure_marshal_VOID__' + name
182     else:
183         return prefix + '_marshal_VOID__' + name
184
185
186 class _SignatureIter:
187     """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
188     can run genginterface in a limited environment with only Python
189     (like Scratchbox).
190     """
191     def __init__(self, string):
192         self.remaining = string
193
194     def next(self):
195         if self.remaining == '':
196             raise StopIteration
197
198         signature = self.remaining
199         block_depth = 0
200         block_type = None
201         end = len(signature)
202
203         for marker in range(0, end):
204             cur_sig = signature[marker]
205
206             if cur_sig == 'a':
207                 pass
208             elif cur_sig == '{' or cur_sig == '(':
209                 if block_type == None:
210                     block_type = cur_sig
211
212                 if block_type == cur_sig:
213                     block_depth = block_depth + 1
214
215             elif cur_sig == '}':
216                 if block_type == '{':
217                     block_depth = block_depth - 1
218
219                 if block_depth == 0:
220                     end = marker
221                     break
222
223             elif cur_sig == ')':
224                 if block_type == '(':
225                     block_depth = block_depth - 1
226
227                 if block_depth == 0:
228                     end = marker
229                     break
230
231             else:
232                 if block_depth == 0:
233                     end = marker
234                     break
235
236         end = end + 1
237         self.remaining = signature[end:]
238         return Signature(signature[0:end])
239
240
241 class Signature(str):
242     """A string, iteration over which is by D-Bus single complete types
243     rather than characters.
244     """
245     def __iter__(self):
246         return _SignatureIter(self)
247
248
249 def type_to_gtype(s):
250     if s == 'y': #byte
251         return ("guchar ", "G_TYPE_UCHAR","UCHAR", False)
252     elif s == 'b': #boolean
253         return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False)
254     elif s == 'n': #int16
255         return ("gint ", "G_TYPE_INT","INT", False)
256     elif s == 'q': #uint16
257         return ("guint ", "G_TYPE_UINT","UINT", False)
258     elif s == 'i': #int32
259         return ("gint ", "G_TYPE_INT","INT", False)
260     elif s == 'u': #uint32
261         return ("guint ", "G_TYPE_UINT","UINT", False)
262     elif s == 'x': #int64
263         return ("gint64 ", "G_TYPE_INT64","INT64", False)
264     elif s == 't': #uint64
265         return ("guint64 ", "G_TYPE_UINT64","UINT64", False)
266     elif s == 'd': #double
267         return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False)
268     elif s == 's': #string
269         return ("gchar *", "G_TYPE_STRING", "STRING", True)
270     elif s == 'g': #signature - FIXME
271         return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True)
272     elif s == 'o': #object path
273         return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True)
274     elif s == 'v':  #variant
275         return ("GValue *", "G_TYPE_VALUE", "BOXED", True)
276     elif s == 'as':  #array of strings
277         return ("gchar **", "G_TYPE_STRV", "BOXED", True)
278     elif s == 'ay': #byte array
279         return ("GArray *",
280             "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED",
281             True)
282     elif s == 'au': #uint array
283         return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True)
284     elif s == 'ai': #int array
285         return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True)
286     elif s == 'ax': #int64 array
287         return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True)
288     elif s == 'at': #uint64 array
289         return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True)
290     elif s == 'ad': #double array
291         return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True)
292     elif s == 'ab': #boolean array
293         return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True)
294     elif s == 'ao': #object path array
295         return ("GArray *", "DBUS_TYPE_G_OBJECT_ARRAY", "BOXED", True)
296     elif s == 'a{ss}': #hash table of string to string
297         return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False)
298     elif s[:2] == 'a{':  #some arbitrary hash tables
299         if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'):
300             raise Exception, "can't index a hashtable off non-basic type " + s
301         first = type_to_gtype(s[2])
302         second = type_to_gtype(s[3:-1])
303         return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False)
304     elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse
305         gtype = type_to_gtype(s[1:])[1]
306         return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True)
307     elif s[:1] == '(': #struct
308         gtype = "(dbus_g_type_get_struct (\"GValueArray\", "
309         for subsig in Signature(s[1:-1]):
310             gtype = gtype + type_to_gtype(subsig)[1] + ", "
311         gtype = gtype + "G_TYPE_INVALID))"
312         return ("GValueArray *", gtype, "BOXED", True)
313
314     # we just don't know ..
315     raise Exception, "don't know the GType for " + s
316
317
318 def xml_escape(s):
319     s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
320     return s.replace('<', '&lt;').replace('>', '&gt;')