File indexing completed on 2024-04-21 05:01:51

0001 """Library code for language-independent D-Bus-related code generation.
0002 
0003 The master copy of this library is in the telepathy-glib repository -
0004 please make any changes there.
0005 """
0006 
0007 # Copyright (C) 2006-2008 Collabora Limited
0008 #
0009 # This library is free software; you can redistribute it and/or
0010 # modify it under the terms of the GNU Lesser General Public
0011 # License as published by the Free Software Foundation; either
0012 # version 2.1 of the License, or (at your option) any later version.
0013 #
0014 # This library is distributed in the hope that it will be useful,
0015 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017 # Lesser General Public License for more details.
0018 #
0019 # You should have received a copy of the GNU Lesser General Public
0020 # License along with this library; if not, write to the Free Software
0021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0022 
0023 import os
0024 import sys
0025 from string import ascii_letters, digits
0026 
0027 
0028 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
0029 
0030 _ASCII_ALNUM = ascii_letters + digits
0031 
0032 if sys.version_info[0] >= 3:
0033     def u(s):
0034         """Return s, which must be a str literal with no non-ASCII characters.
0035         This is like a more restricted form of the Python 2 u'' syntax.
0036         """
0037         return s.encode('ascii').decode('ascii')
0038 else:
0039     def u(s):
0040         """Return a Unicode version of s, which must be a str literal
0041         (a bytestring) in which each byte is an ASCII character.
0042         This is like a more restricted form of the u'' syntax.
0043         """
0044         return s.decode('ascii')
0045 
0046 def file_set_contents(filename, contents):
0047     try:
0048         os.remove(filename)
0049     except OSError:
0050         pass
0051     try:
0052         os.remove(filename + '.tmp')
0053     except OSError:
0054         pass
0055 
0056     open(filename + '.tmp', 'wb').write(contents)
0057     os.rename(filename + '.tmp', filename)
0058 
0059 def cmp_by_name(node1, node2):
0060     return cmp(node1.getAttributeNode("name").nodeValue,
0061                node2.getAttributeNode("name").nodeValue)
0062 
0063 def key_by_name(node):
0064     return node.getAttributeNode("name").nodeValue
0065 
0066 def escape_as_identifier(identifier):
0067     """Escape the given string to be a valid D-Bus object path or service
0068     name component, using a reversible encoding to ensure uniqueness.
0069 
0070     The reversible encoding is as follows:
0071 
0072     * The empty string becomes '_'
0073     * Otherwise, each non-alphanumeric character is replaced by '_' plus
0074       two lower-case hex digits; the same replacement is carried out on
0075       the first character, if it's a digit
0076     """
0077     # '' -> '_'
0078     if not identifier:
0079         return '_'
0080 
0081     # A bit of a fast path for strings which are already OK.
0082     # We deliberately omit '_' because, for reversibility, that must also
0083     # be escaped.
0084     if (identifier.strip(_ASCII_ALNUM) == '' and
0085         identifier[0] in ascii_letters):
0086         return identifier
0087 
0088     # The first character may not be a digit
0089     if identifier[0] not in ascii_letters:
0090         ret = ['_%02x' % ord(identifier[0])]
0091     else:
0092         ret = [identifier[0]]
0093 
0094     # Subsequent characters may be digits or ASCII letters
0095     for c in identifier[1:]:
0096         if c in _ASCII_ALNUM:
0097             ret.append(c)
0098         else:
0099             ret.append('_%02x' % ord(c))
0100 
0101     return ''.join(ret)
0102 
0103 
0104 def get_by_path(element, path):
0105     branches = path.split('/')
0106     branch = branches[0]
0107 
0108     # Is the current branch an attribute, if so, return the attribute value
0109     if branch[0] == '@':
0110         return element.getAttribute(branch[1:])
0111 
0112     # Find matching children for the branch
0113     children = []
0114     if branch == '..':
0115         children.append(element.parentNode)
0116     else:
0117         for x in element.childNodes:
0118             if x.localName == branch:
0119                 children.append(x)
0120 
0121     ret = []
0122     # If this is not the last path element, recursively gather results from
0123     # children
0124     if len(branches) > 1:
0125         for x in children:
0126             add = get_by_path(x, '/'.join(branches[1:]))
0127             if isinstance(add, list):
0128                 ret += add
0129             else:
0130                 return add
0131     else:
0132         ret = children
0133 
0134     return ret
0135 
0136 
0137 def get_docstring(element):
0138     docstring = None
0139     for x in element.childNodes:
0140         if x.namespaceURI == NS_TP and x.localName == 'docstring':
0141             docstring = x
0142     if docstring is not None:
0143         docstring = docstring.toxml().replace('\n', ' ').strip()
0144         if docstring.startswith('<tp:docstring>'):
0145             docstring = docstring[14:].lstrip()
0146         if docstring.endswith('</tp:docstring>'):
0147             docstring = docstring[:-15].rstrip()
0148         if docstring in ('<tp:docstring/>', ''):
0149             docstring = ''
0150     return docstring
0151 
0152 def get_deprecated(element):
0153     text = []
0154     for x in element.childNodes:
0155         if hasattr(x, 'data'):
0156             text.append(x.data.replace('\n', ' ').strip())
0157         else:
0158             # This caters for tp:dbus-ref elements, but little else.
0159             if x.childNodes and hasattr(x.childNodes[0], 'data'):
0160                 text.append(x.childNodes[0].data.replace('\n', ' ').strip())
0161     return ' '.join(text)
0162 
0163 def get_descendant_text(element_or_elements):
0164     if not element_or_elements:
0165         return ''
0166     if isinstance(element_or_elements, list):
0167         return ''.join(map(get_descendant_text, element_or_elements))
0168     parts = []
0169     for x in element_or_elements.childNodes:
0170         if x.nodeType == x.TEXT_NODE:
0171             parts.append(x.nodeValue)
0172         elif x.nodeType == x.ELEMENT_NODE:
0173             parts.append(get_descendant_text(x))
0174         else:
0175             pass
0176     return ''.join(parts)
0177 
0178 
0179 class _SignatureIter:
0180     """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
0181     can run genginterface in a limited environment with only Python
0182     (like Scratchbox).
0183     """
0184     def __init__(self, string):
0185         self.remaining = string
0186 
0187     def next(self):
0188         return self.__next__()
0189 
0190     def __next__(self):
0191         if self.remaining == '':
0192             raise StopIteration
0193 
0194         signature = self.remaining
0195         block_depth = 0
0196         block_type = None
0197         end = len(signature)
0198 
0199         for marker in range(0, end):
0200             cur_sig = signature[marker]
0201 
0202             if cur_sig == 'a':
0203                 pass
0204             elif cur_sig == '{' or cur_sig == '(':
0205                 if block_type == None:
0206                     block_type = cur_sig
0207 
0208                 if block_type == cur_sig:
0209                     block_depth = block_depth + 1
0210 
0211             elif cur_sig == '}':
0212                 if block_type == '{':
0213                     block_depth = block_depth - 1
0214 
0215                 if block_depth == 0:
0216                     end = marker
0217                     break
0218 
0219             elif cur_sig == ')':
0220                 if block_type == '(':
0221                     block_depth = block_depth - 1
0222 
0223                 if block_depth == 0:
0224                     end = marker
0225                     break
0226 
0227             else:
0228                 if block_depth == 0:
0229                     end = marker
0230                     break
0231 
0232         end = end + 1
0233         self.remaining = signature[end:]
0234         return Signature(signature[0:end])
0235 
0236 
0237 class Signature(str):
0238     """A string, iteration over which is by D-Bus single complete types
0239     rather than characters.
0240     """
0241     def __iter__(self):
0242         return _SignatureIter(self)
0243 
0244 
0245 def xml_escape(s):
0246     s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
0247     return s.replace('<', '&lt;').replace('>', '&gt;')