chat-window: expose the individual mgr as a property
[empathy-mirror.git] / tools / libtpcodegen.py
blob7e9eb9a5078dffdbe068d399595813f9b7f46d73
1 """Library code for language-independent D-Bus-related code generation.
3 The master copy of this library is in the telepathy-glib repository -
4 please make any changes there.
5 """
7 # Copyright (C) 2006-2008 Collabora Limited
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.
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.
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
23 import os
24 from string import ascii_letters, digits
27 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
29 _ASCII_ALNUM = ascii_letters + digits
31 def file_set_contents(filename, contents):
32 try:
33 os.remove(filename)
34 except OSError:
35 pass
36 try:
37 os.remove(filename + '.tmp')
38 except OSError:
39 pass
41 open(filename + '.tmp', 'w').write(contents)
42 os.rename(filename + '.tmp', filename)
44 def cmp_by_name(node1, node2):
45 return cmp(node1.getAttributeNode("name").nodeValue,
46 node2.getAttributeNode("name").nodeValue)
49 def escape_as_identifier(identifier):
50 """Escape the given string to be a valid D-Bus object path or service
51 name component, using a reversible encoding to ensure uniqueness.
53 The reversible encoding is as follows:
55 * The empty string becomes '_'
56 * Otherwise, each non-alphanumeric character is replaced by '_' plus
57 two lower-case hex digits; the same replacement is carried out on
58 the first character, if it's a digit
59 """
60 # '' -> '_'
61 if not identifier:
62 return '_'
64 # A bit of a fast path for strings which are already OK.
65 # We deliberately omit '_' because, for reversibility, that must also
66 # be escaped.
67 if (identifier.strip(_ASCII_ALNUM) == '' and
68 identifier[0] in ascii_letters):
69 return identifier
71 # The first character may not be a digit
72 if identifier[0] not in ascii_letters:
73 ret = ['_%02x' % ord(identifier[0])]
74 else:
75 ret = [identifier[0]]
77 # Subsequent characters may be digits or ASCII letters
78 for c in identifier[1:]:
79 if c in _ASCII_ALNUM:
80 ret.append(c)
81 else:
82 ret.append('_%02x' % ord(c))
84 return ''.join(ret)
87 def get_by_path(element, path):
88 branches = path.split('/')
89 branch = branches[0]
91 # Is the current branch an attribute, if so, return the attribute value
92 if branch[0] == '@':
93 return element.getAttribute(branch[1:])
95 # Find matching children for the branch
96 children = []
97 if branch == '..':
98 children.append(element.parentNode)
99 else:
100 for x in element.childNodes:
101 if x.localName == branch:
102 children.append(x)
104 ret = []
105 # If this is not the last path element, recursively gather results from
106 # children
107 if len(branches) > 1:
108 for x in children:
109 add = get_by_path(x, '/'.join(branches[1:]))
110 if isinstance(add, list):
111 ret += add
112 else:
113 return add
114 else:
115 ret = children
117 return ret
120 def get_docstring(element):
121 docstring = None
122 for x in element.childNodes:
123 if x.namespaceURI == NS_TP and x.localName == 'docstring':
124 docstring = x
125 if docstring is not None:
126 docstring = docstring.toxml().replace('\n', ' ').strip()
127 if docstring.startswith('<tp:docstring>'):
128 docstring = docstring[14:].lstrip()
129 if docstring.endswith('</tp:docstring>'):
130 docstring = docstring[:-15].rstrip()
131 if docstring in ('<tp:docstring/>', ''):
132 docstring = ''
133 return docstring
135 def get_deprecated(element):
136 text = []
137 for x in element.childNodes:
138 if hasattr(x, 'data'):
139 text.append(x.data.replace('\n', ' ').strip())
140 else:
141 # This caters for tp:dbus-ref elements, but little else.
142 if x.childNodes and hasattr(x.childNodes[0], 'data'):
143 text.append(x.childNodes[0].data.replace('\n', ' ').strip())
144 return ' '.join(text)
146 def get_descendant_text(element_or_elements):
147 if not element_or_elements:
148 return ''
149 if isinstance(element_or_elements, list):
150 return ''.join(map(get_descendant_text, element_or_elements))
151 parts = []
152 for x in element_or_elements.childNodes:
153 if x.nodeType == x.TEXT_NODE:
154 parts.append(x.nodeValue)
155 elif x.nodeType == x.ELEMENT_NODE:
156 parts.append(get_descendant_text(x))
157 else:
158 pass
159 return ''.join(parts)
162 class _SignatureIter:
163 """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
164 can run genginterface in a limited environment with only Python
165 (like Scratchbox).
167 def __init__(self, string):
168 self.remaining = string
170 def next(self):
171 if self.remaining == '':
172 raise StopIteration
174 signature = self.remaining
175 block_depth = 0
176 block_type = None
177 end = len(signature)
179 for marker in range(0, end):
180 cur_sig = signature[marker]
182 if cur_sig == 'a':
183 pass
184 elif cur_sig == '{' or cur_sig == '(':
185 if block_type == None:
186 block_type = cur_sig
188 if block_type == cur_sig:
189 block_depth = block_depth + 1
191 elif cur_sig == '}':
192 if block_type == '{':
193 block_depth = block_depth - 1
195 if block_depth == 0:
196 end = marker
197 break
199 elif cur_sig == ')':
200 if block_type == '(':
201 block_depth = block_depth - 1
203 if block_depth == 0:
204 end = marker
205 break
207 else:
208 if block_depth == 0:
209 end = marker
210 break
212 end = end + 1
213 self.remaining = signature[end:]
214 return Signature(signature[0:end])
217 class Signature(str):
218 """A string, iteration over which is by D-Bus single complete types
219 rather than characters.
221 def __iter__(self):
222 return _SignatureIter(self)
225 def xml_escape(s):
226 s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
227 return s.replace('<', '&lt;').replace('>', '&gt;')