File indexing completed on 2024-05-05 17:01:41

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 
0024 from string import ascii_letters, digits
0025 
0026 
0027 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
0028 
0029 _ASCII_ALNUM = ascii_letters + digits
0030 
0031 
0032 def cmp_by_name(node1, node2):
0033     return cmp(node1.getAttributeNode("name").nodeValue,
0034                node2.getAttributeNode("name").nodeValue)
0035 
0036 
0037 def escape_as_identifier(identifier):
0038     """Escape the given string to be a valid D-Bus object path or service
0039     name component, using a reversible encoding to ensure uniqueness.
0040 
0041     The reversible encoding is as follows:
0042 
0043     * The empty string becomes '_'
0044     * Otherwise, each non-alphanumeric character is replaced by '_' plus
0045       two lower-case hex digits; the same replacement is carried out on
0046       the first character, if it's a digit
0047     """
0048     # '' -> '_'
0049     if not identifier:
0050         return '_'
0051 
0052     # A bit of a fast path for strings which are already OK.
0053     # We deliberately omit '_' because, for reversibility, that must also
0054     # be escaped.
0055     if (identifier.strip(_ASCII_ALNUM) == '' and
0056         identifier[0] in ascii_letters):
0057         return identifier
0058 
0059     # The first character may not be a digit
0060     if identifier[0] not in ascii_letters:
0061         ret = ['_%02x' % ord(identifier[0])]
0062     else:
0063         ret = [identifier[0]]
0064 
0065     # Subsequent characters may be digits or ASCII letters
0066     for c in identifier[1:]:
0067         if c in _ASCII_ALNUM:
0068             ret.append(c)
0069         else:
0070             ret.append('_%02x' % ord(c))
0071 
0072     return ''.join(ret)
0073 
0074 
0075 def get_by_path(element, path):
0076     branches = path.split('/')
0077     branch = branches[0]
0078 
0079     # Is the current branch an attribute, if so, return the attribute value
0080     if branch[0] == '@':
0081         return element.getAttribute(branch[1:])
0082 
0083     # Find matching children for the branch
0084     children = []
0085     if branch == '..':
0086         children.append(element.parentNode)
0087     else:
0088         for x in element.childNodes:
0089             if x.localName == branch:
0090                 children.append(x)
0091 
0092     ret = []
0093     # If this is not the last path element, recursively gather results from
0094     # children
0095     if len(branches) > 1:
0096         for x in children:
0097             add = get_by_path(x, '/'.join(branches[1:]))
0098             if isinstance(add, list):
0099                 ret += add
0100             else:
0101                 return add
0102     else:
0103         ret = children
0104 
0105     return ret
0106 
0107 
0108 def get_docstring(element):
0109     docstring = None
0110     for x in element.childNodes:
0111         if x.namespaceURI == NS_TP and x.localName == 'docstring':
0112             docstring = x
0113     if docstring is not None:
0114         docstring = docstring.toxml().replace('\n', ' ').strip()
0115         if docstring.startswith('<tp:docstring>'):
0116             docstring = docstring[14:].lstrip()
0117         if docstring.endswith('</tp:docstring>'):
0118             docstring = docstring[:-15].rstrip()
0119         if docstring in ('<tp:docstring/>', ''):
0120             docstring = ''
0121     return docstring
0122 
0123 def get_deprecated(element):
0124     text = []
0125     for x in element.childNodes:
0126         if hasattr(x, 'data'):
0127             text.append(x.data.replace('\n', ' ').strip())
0128         else:
0129             # This caters for tp:dbus-ref elements, but little else.
0130             if x.childNodes and hasattr(x.childNodes[0], 'data'):
0131                 text.append(x.childNodes[0].data.replace('\n', ' ').strip())
0132     return ' '.join(text)
0133 
0134 def get_descendant_text(element_or_elements):
0135     if not element_or_elements:
0136         return ''
0137     if isinstance(element_or_elements, list):
0138         return ''.join(map(get_descendant_text, element_or_elements))
0139     parts = []
0140     for x in element_or_elements.childNodes:
0141         if x.nodeType == x.TEXT_NODE:
0142             parts.append(x.nodeValue)
0143         elif x.nodeType == x.ELEMENT_NODE:
0144             parts.append(get_descendant_text(x))
0145         else:
0146             pass
0147     return ''.join(parts)
0148 
0149 
0150 class _SignatureIter:
0151     """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
0152     can run genginterface in a limited environment with only Python
0153     (like Scratchbox).
0154     """
0155     def __init__(self, string):
0156         self.remaining = string
0157 
0158     def next(self):
0159         if self.remaining == '':
0160             raise StopIteration
0161 
0162         signature = self.remaining
0163         block_depth = 0
0164         block_type = None
0165         end = len(signature)
0166 
0167         for marker in range(0, end):
0168             cur_sig = signature[marker]
0169 
0170             if cur_sig == 'a':
0171                 pass
0172             elif cur_sig == '{' or cur_sig == '(':
0173                 if block_type == None:
0174                     block_type = cur_sig
0175 
0176                 if block_type == cur_sig:
0177                     block_depth = block_depth + 1
0178 
0179             elif cur_sig == '}':
0180                 if block_type == '{':
0181                     block_depth = block_depth - 1
0182 
0183                 if block_depth == 0:
0184                     end = marker
0185                     break
0186 
0187             elif cur_sig == ')':
0188                 if block_type == '(':
0189                     block_depth = block_depth - 1
0190 
0191                 if block_depth == 0:
0192                     end = marker
0193                     break
0194 
0195             else:
0196                 if block_depth == 0:
0197                     end = marker
0198                     break
0199 
0200         end = end + 1
0201         self.remaining = signature[end:]
0202         return Signature(signature[0:end])
0203 
0204 
0205 class Signature(str):
0206     """A string, iteration over which is by D-Bus single complete types
0207     rather than characters.
0208     """
0209     def __iter__(self):
0210         return _SignatureIter(self)
0211 
0212 
0213 def xml_escape(s):
0214     s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
0215     return s.replace('<', '&lt;').replace('>', '&gt;')