]> git.0d.be Git - empathy.git/blob - tools/libtpcodegen.py
disable tab coding style check for now
[empathy.git] / tools / libtpcodegen.py
1 """Library code for language-independent 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-2008 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 escape_as_identifier(identifier):
64     """Escape the given string to be a valid D-Bus object path or service
65     name component, using a reversible encoding to ensure uniqueness.
66
67     The reversible encoding is as follows:
68
69     * The empty string becomes '_'
70     * Otherwise, each non-alphanumeric character is replaced by '_' plus
71       two lower-case hex digits; the same replacement is carried out on
72       the first character, if it's a digit
73     """
74     # '' -> '_'
75     if not identifier:
76         return '_'
77
78     # A bit of a fast path for strings which are already OK.
79     # We deliberately omit '_' because, for reversibility, that must also
80     # be escaped.
81     if (identifier.strip(_ASCII_ALNUM) == '' and
82         identifier[0] in ascii_letters):
83         return identifier
84
85     # The first character may not be a digit
86     if identifier[0] not in ascii_letters:
87         ret = ['_%02x' % ord(identifier[0])]
88     else:
89         ret = [identifier[0]]
90
91     # Subsequent characters may be digits or ASCII letters
92     for c in identifier[1:]:
93         if c in _ASCII_ALNUM:
94             ret.append(c)
95         else:
96             ret.append('_%02x' % ord(c))
97
98     return ''.join(ret)
99
100
101 def get_by_path(element, path):
102     branches = path.split('/')
103     branch = branches[0]
104
105     # Is the current branch an attribute, if so, return the attribute value
106     if branch[0] == '@':
107         return element.getAttribute(branch[1:])
108
109     # Find matching children for the branch
110     children = []
111     if branch == '..':
112         children.append(element.parentNode)
113     else:
114         for x in element.childNodes:
115             if x.localName == branch:
116                 children.append(x)
117
118     ret = []
119     # If this is not the last path element, recursively gather results from
120     # children
121     if len(branches) > 1:
122         for x in children:
123             add = get_by_path(x, '/'.join(branches[1:]))
124             if isinstance(add, list):
125                 ret += add
126             else:
127                 return add
128     else:
129         ret = children
130
131     return ret
132
133
134 def get_docstring(element):
135     docstring = None
136     for x in element.childNodes:
137         if x.namespaceURI == NS_TP and x.localName == 'docstring':
138             docstring = x
139     if docstring is not None:
140         docstring = docstring.toxml().replace('\n', ' ').strip()
141         if docstring.startswith('<tp:docstring>'):
142             docstring = docstring[14:].lstrip()
143         if docstring.endswith('</tp:docstring>'):
144             docstring = docstring[:-15].rstrip()
145         if docstring in ('<tp:docstring/>', ''):
146             docstring = ''
147     return docstring
148
149
150 def get_descendant_text(element_or_elements):
151     if not element_or_elements:
152         return ''
153     if isinstance(element_or_elements, list):
154         return ''.join(map(get_descendant_text, element_or_elements))
155     parts = []
156     for x in element_or_elements.childNodes:
157         if x.nodeType == x.TEXT_NODE:
158             parts.append(x.nodeValue)
159         elif x.nodeType == x.ELEMENT_NODE:
160             parts.append(get_descendant_text(x))
161         else:
162             pass
163     return ''.join(parts)
164
165
166 class _SignatureIter:
167     """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
168     can run genginterface in a limited environment with only Python
169     (like Scratchbox).
170     """
171     def __init__(self, string):
172         self.remaining = string
173
174     def next(self):
175         if self.remaining == '':
176             raise StopIteration
177
178         signature = self.remaining
179         block_depth = 0
180         block_type = None
181         end = len(signature)
182
183         for marker in range(0, end):
184             cur_sig = signature[marker]
185
186             if cur_sig == 'a':
187                 pass
188             elif cur_sig == '{' or cur_sig == '(':
189                 if block_type == None:
190                     block_type = cur_sig
191
192                 if block_type == cur_sig:
193                     block_depth = block_depth + 1
194
195             elif cur_sig == '}':
196                 if block_type == '{':
197                     block_depth = block_depth - 1
198
199                 if block_depth == 0:
200                     end = marker
201                     break
202
203             elif cur_sig == ')':
204                 if block_type == '(':
205                     block_depth = block_depth - 1
206
207                 if block_depth == 0:
208                     end = marker
209                     break
210
211             else:
212                 if block_depth == 0:
213                     end = marker
214                     break
215
216         end = end + 1
217         self.remaining = signature[end:]
218         return Signature(signature[0:end])
219
220
221 class Signature(str):
222     """A string, iteration over which is by D-Bus single complete types
223     rather than characters.
224     """
225     def __iter__(self):
226         return _SignatureIter(self)
227
228
229 def xml_escape(s):
230     s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
231     return s.replace('<', '&lt;').replace('>', '&gt;')