File indexing completed on 2024-04-28 12:45:42
0001 """Library code for Qt D-Bus-related code generation. 0002 0003 The master copy of this library is in the telepathy-qt repository - 0004 please make any changes there. 0005 """ 0006 0007 # Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> 0008 # Copyright (C) 2008 Nokia Corporation 0009 # 0010 # This library is free software; you can redistribute it and/or 0011 # modify it under the terms of the GNU Lesser General Public 0012 # License as published by the Free Software Foundation; either 0013 # version 2.1 of the License, or (at your option) any later version. 0014 # 0015 # This library is distributed in the hope that it will be useful, 0016 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0018 # Lesser General Public License for more details. 0019 # 0020 # You should have received a copy of the GNU Lesser General Public 0021 # License along with this library; if not, write to the Free Software 0022 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0023 0024 from sys import maxsize, stderr 0025 import re 0026 from libtpcodegen import get_by_path, get_descendant_text, NS_TP, xml_escape 0027 0028 class Xzibit(Exception): 0029 def __init__(self, parent, child): 0030 self.parent = parent 0031 self.child = child 0032 0033 def __str__(self): 0034 print(""" 0035 Nested <%s>s are forbidden. 0036 Parent: 0037 %s... 0038 Child: 0039 %s... 0040 """ % (self.parent.nodeName, self.parent.toxml()[:100], 0041 self.child.toxml()[:100])) 0042 0043 class _QtTypeBinding: 0044 def __init__(self, val, inarg, outarg, array_val, custom_type, array_of, 0045 array_depth=None): 0046 self.val = val 0047 self.inarg = inarg 0048 self.outarg = outarg 0049 self.array_val = array_val 0050 self.custom_type = custom_type 0051 self.array_of = array_of 0052 self.array_depth = array_depth 0053 0054 if array_depth is None: 0055 self.array_depth = int(bool(array_val)) 0056 elif array_depth >= 1: 0057 assert array_val 0058 else: 0059 assert not array_val 0060 0061 class RefTarget(object): 0062 KIND_INTERFACE, KIND_METHOD, KIND_SIGNAL, KIND_PROPERTY = 'node', 'method', 'signal', 'property' 0063 0064 def __init__(self, el): 0065 self.kind = el.localName 0066 assert self.kind in (self.KIND_INTERFACE, self.KIND_METHOD, self.KIND_SIGNAL, self.KIND_PROPERTY) 0067 0068 if self.kind == self.KIND_INTERFACE: 0069 self.dbus_text = el.getAttribute('name').lstrip('/').replace('_', '') + 'Interface' 0070 else: 0071 self.member_text = el.getAttribute('name') 0072 0073 assert el.parentNode.parentNode.localName == self.KIND_INTERFACE 0074 host_class = el.parentNode.parentNode.getAttribute('name').lstrip('/').replace('_', '') + 'Interface' 0075 0076 if self.kind == self.KIND_PROPERTY: 0077 self.member_link = 'requestProperty%s()' % (self.member_text) 0078 self.dbus_link = '%s::%s' % (host_class, self.member_link) 0079 else: 0080 self.member_text = '%s()' % self.member_text 0081 0082 self.dbus_text = '%s::%s' % (host_class, self.member_text) 0083 0084 class RefRegistry(object): 0085 def __init__(self, spec): 0086 self.targets = {} 0087 for node in spec.getElementsByTagName('node'): 0088 iface, = get_by_path(node, 'interface') 0089 iface_name = iface.getAttribute('name') 0090 0091 self.targets[iface_name] = RefTarget(node) 0092 0093 for method in iface.getElementsByTagName(RefTarget.KIND_METHOD): 0094 self.targets[iface_name + '.' + method.getAttribute('name')] = RefTarget(method) 0095 0096 for signal in iface.getElementsByTagName(RefTarget.KIND_SIGNAL): 0097 self.targets[iface_name + '.' + signal.getAttribute('name')] = RefTarget(signal) 0098 0099 for prop in iface.getElementsByTagName(RefTarget.KIND_PROPERTY): 0100 self.targets[iface_name + '.' + prop.getAttribute('name')] = RefTarget(prop) 0101 0102 def process(self, ref): 0103 assert ref.namespaceURI == NS_TP 0104 0105 def get_closest_parent(el, needle): 0106 node = el 0107 while node is not None and node.localName != needle: 0108 node = node.parentNode 0109 return node 0110 0111 local = get_descendant_text(ref).strip() 0112 if ref.localName == 'member-ref': 0113 ns = get_closest_parent(ref, 'interface').getAttribute('name') 0114 path = ns + '.' + local.strip() 0115 else: 0116 if ref.hasAttribute('namespace'): 0117 ns = ref.getAttribute('namespace').replace('ofdT', 'org.freedesktop.Telepathy') 0118 path = ns + '.' + local.strip() 0119 else: 0120 path = local 0121 0122 target = self.targets.get(path) 0123 0124 if target is None: 0125 parent = get_closest_parent(ref, 'interface') or get_closest_parent(ref, 'error') 0126 parent_name = parent.getAttribute('name') 0127 if (path + parent_name).find('.DRAFT') == -1 and (path + parent_name).find('.FUTURE') == -1: 0128 print(('WARNING: Failed to resolve %s to "%s" in "%s"' % ( 0129 ref.localName, path, parent_name)), file=stderr) 0130 return path 0131 0132 if ref.localName == 'member-ref': 0133 if target.kind == target.KIND_PROPERTY: 0134 return '\\link %s %s \\endlink' % (target.member_link, target.member_text) 0135 else: 0136 return target.member_text 0137 else: 0138 if target.kind == target.KIND_PROPERTY: 0139 return '\\link %s %s \\endlink' % (target.dbus_link, target.dbus_text) 0140 else: 0141 return target.dbus_text 0142 0143 def binding_from_usage(sig, tptype, custom_lists, external=False, explicit_own_ns=None): 0144 # 'signature' : ('qt-type', 'pass-by-reference', 'array-type') 0145 natives = { 0146 'y' : ('uchar', False, None), 0147 'b' : ('bool', False, 'BoolList'), 0148 'n' : ('short', False, 'ShortList'), 0149 'q' : ('ushort', False, 'UShortList'), 0150 'i' : ('int', False, 'IntList'), 0151 'u' : ('uint', False, 'UIntList'), 0152 'x' : ('qlonglong', False, 'LongLongList'), 0153 't' : ('qulonglong', False, 'ULongLongList'), 0154 'd' : ('double', False, 'DoubleList'), 0155 's' : ('QString', True, None), 0156 'v' : ('QDBusVariant', True, None), 0157 'o' : ('QDBusObjectPath', True, 'ObjectPathList'), 0158 'g' : ('QDBusSignature', True, 'SignatureList'), 0159 'as' : ('QStringList', True, "StringListList"), 0160 'ay' : ('QByteArray', True, "ByteArrayList"), 0161 'av' : ('QVariantList', True, "VariantListList"), 0162 'a{sv}' : ('QVariantMap', True, None) 0163 } 0164 0165 val, inarg = None, None 0166 custom_type = False 0167 array_of = None 0168 0169 if sig in natives: 0170 typename, pass_by_ref, array_name = natives[sig] 0171 val = typename 0172 inarg = (pass_by_ref and ('const %s&' % val)) or val 0173 elif sig[0] == 'a' and sig[1:] in natives and natives[sig[1:]][2]: 0174 val = natives[sig[1:]][2] 0175 if explicit_own_ns: 0176 val = explicit_own_ns + '::' + val 0177 inarg = 'const %s&' % val 0178 array_of = natives[sig[1:]][0] 0179 elif tptype: 0180 tptype = tptype.replace('_', '') 0181 custom_type = True 0182 0183 if external: 0184 tptype = 'Tp::' + tptype 0185 elif explicit_own_ns: 0186 tptype = explicit_own_ns + '::' + tptype 0187 0188 if tptype.endswith('[]'): 0189 tptype = tptype[:-2] 0190 extra_list_nesting = 0 0191 0192 while tptype.endswith('[]'): 0193 extra_list_nesting += 1 0194 tptype = tptype[:-2] 0195 0196 assert tptype in custom_lists, ('No array version of custom type %s in the spec, but array version used' % tptype) 0197 val = custom_lists[tptype] + 'List' * extra_list_nesting 0198 else: 0199 val = tptype 0200 0201 inarg = 'const %s&' % val 0202 else: 0203 assert False, 'Don\'t know how to map type (%s, %s)' % (sig, tptype) 0204 0205 outarg = val + '&' 0206 return _QtTypeBinding(val, inarg, outarg, None, custom_type, array_of) 0207 0208 def binding_from_decl(name, array_name, array_depth=None, external=False, explicit_own_ns=''): 0209 val = name.replace('_', '') 0210 if external: 0211 val = 'Tp::' + val 0212 elif explicit_own_ns: 0213 val = explicit_own_ns + '::' + val 0214 inarg = 'const %s&' % val 0215 outarg = '%s&' % val 0216 return _QtTypeBinding(val, inarg, outarg, array_name.replace('_', ''), True, None, array_depth) 0217 0218 def extract_arg_or_member_info(els, custom_lists, externals, typesns, refs, docstring_indent=' * ', docstring_brackets=None, docstring_maxwidth=80): 0219 names = [] 0220 docstrings = [] 0221 bindings = [] 0222 0223 for el in els: 0224 names.append(get_qt_name(el)) 0225 docstrings.append(format_docstring(el, refs, docstring_indent, docstring_brackets, docstring_maxwidth)) 0226 0227 sig = el.getAttribute('type') 0228 tptype = el.getAttributeNS(NS_TP, 'type') 0229 bindings.append(binding_from_usage(sig, tptype, custom_lists, (sig, tptype) in externals, typesns)) 0230 0231 return names, docstrings, bindings 0232 0233 def format_docstring(el, refs, indent=' * ', brackets=None, maxwidth=80): 0234 docstring_el = None 0235 0236 for x in el.childNodes: 0237 if x.namespaceURI == NS_TP and x.localName == 'docstring': 0238 docstring_el = x 0239 0240 if not docstring_el: 0241 return '' 0242 0243 lines = [] 0244 0245 # escape backslashes, so they won't be interpreted starting doxygen commands and we can later 0246 # insert doxygen commands we actually want 0247 def escape_slashes(x): 0248 if x.nodeType == x.TEXT_NODE: 0249 x.data = x.data.replace('\\', '\\\\') 0250 elif x.nodeType == x.ELEMENT_NODE: 0251 for y in x.childNodes: 0252 escape_slashes(y) 0253 else: 0254 return 0255 0256 escape_slashes(docstring_el) 0257 doc = docstring_el.ownerDocument 0258 0259 for n in docstring_el.getElementsByTagNameNS(NS_TP, 'rationale'): 0260 nested = n.getElementsByTagNameNS(NS_TP, 'rationale') 0261 if nested: 0262 raise Xzibit(n, nested[0]) 0263 0264 div = doc.createElement('div') 0265 div.setAttribute('class', 'rationale') 0266 0267 for rationale_body in n.childNodes: 0268 div.appendChild(rationale_body.cloneNode(True)) 0269 0270 n.parentNode.replaceChild(div, n) 0271 0272 if docstring_el.getAttribute('xmlns') == 'http://www.w3.org/1999/xhtml': 0273 for ref in docstring_el.getElementsByTagNameNS(NS_TP, 'member-ref') + docstring_el.getElementsByTagNameNS(NS_TP, 'dbus-ref'): 0274 nested = ref.getElementsByTagNameNS(NS_TP, 'member-ref') + ref.getElementsByTagNameNS(NS_TP, 'dbus-ref') 0275 if nested: 0276 raise Xzibit(n, nested[0]) 0277 0278 text = doc.createTextNode(' \\endhtmlonly ') 0279 text.data += refs.process(ref) 0280 text.data += ' \\htmlonly ' 0281 0282 ref.parentNode.replaceChild(text, ref) 0283 0284 splitted = ''.join([el.toxml() for el in docstring_el.childNodes]).strip(' ').strip('\n').split('\n') 0285 level = min([not match and maxsize or match.end() - 1 for match in [re.match('^ *[^ ]', line) for line in splitted]]) 0286 assert level != maxsize 0287 lines = ['\\htmlonly'] + [line[level:] for line in splitted] + ['\\endhtmlonly'] 0288 else: 0289 content = xml_escape(get_descendant_text(docstring_el).replace('\n', ' ').strip()) 0290 0291 while content.find(' ') != -1: 0292 content = content.replace(' ', ' ') 0293 0294 left = maxwidth - len(indent) - 1 0295 line = '' 0296 0297 while content: 0298 step = (content.find(' ') + 1) or len(content) 0299 0300 if step > left: 0301 lines.append(line) 0302 line = '' 0303 left = maxwidth - len(indent) - 1 0304 0305 left = left - step 0306 line = line + content[:step] 0307 content = content[step:] 0308 0309 if line: 0310 lines.append(line) 0311 0312 output = [] 0313 0314 if lines: 0315 if brackets: 0316 output.append(brackets[0]) 0317 else: 0318 output.append(indent) 0319 0320 output.append('\n') 0321 0322 for line in lines: 0323 output.append(indent) 0324 output.append(line) 0325 output.append('\n') 0326 0327 if lines and brackets: 0328 output.append(brackets[1]) 0329 output.append('\n') 0330 0331 return ''.join(output) 0332 0333 def gather_externals(spec): 0334 externals = [] 0335 0336 for ext in spec.getElementsByTagNameNS(NS_TP, 'external-type'): 0337 sig = ext.getAttribute('type') 0338 tptype = ext.getAttribute('name') 0339 externals.append((sig, tptype)) 0340 0341 return externals 0342 0343 def gather_custom_lists(spec, typesns): 0344 custom_lists = {} 0345 structs = [(provider, typesns) for provider in spec.getElementsByTagNameNS(NS_TP, 'struct')] 0346 mappings = [(provider, typesns) for provider in spec.getElementsByTagNameNS(NS_TP, 'mapping')] 0347 exts = [(provider, 'Telepathy') for provider in spec.getElementsByTagNameNS(NS_TP, 'external-type')] 0348 0349 for (provider, ns) in structs + mappings + exts: 0350 tptype = provider.getAttribute('name').replace('_', '') 0351 array_val = provider.getAttribute('array-name').replace('_', '') 0352 array_depth = provider.getAttribute('array-depth') 0353 if array_depth: 0354 array_depth = int(array_depth) 0355 else: 0356 array_depth = -1 0357 0358 if array_val: 0359 custom_lists[tptype] = array_val 0360 custom_lists[ns + '::' + tptype] = ns + '::' + array_val 0361 if array_depth >= 2: 0362 for i in range(array_depth): 0363 custom_lists[tptype + ('[]' * (i+1))] = ( 0364 array_val + ('List' * i)) 0365 custom_lists[ns + '::' + tptype + ('[]' * (i+1))] = ( 0366 ns + '::' + array_val + ('List' * i)) 0367 0368 return custom_lists 0369 0370 def get_headerfile_cmd(realinclude, prettyinclude, indent=' * '): 0371 prettyinclude = prettyinclude or realinclude 0372 if realinclude: 0373 if prettyinclude: 0374 return indent + ('\\headerfile %s <%s>\n' % (realinclude, prettyinclude)) 0375 else: 0376 return indent + ('\\headerfile %s <%s>\n' % (realinclude)) 0377 else: 0378 return '' 0379 0380 def get_qt_name(el): 0381 name = el.getAttribute('name') 0382 0383 if el.localName in ('method', 'signal', 'property'): 0384 bname = el.getAttributeNS(NS_TP, 'name-for-bindings') 0385 0386 if bname: 0387 name = bname 0388 0389 if not name: 0390 return None 0391 0392 if name[0].isupper() and name[1].islower(): 0393 name = name[0].lower() + name[1:] 0394 0395 return qt_identifier_escape(name.replace('_', '')) 0396 0397 def qt_identifier_escape(str): 0398 built = (str[0].isdigit() and ['_']) or [] 0399 0400 for c in str: 0401 if c.isalnum(): 0402 built.append(c) 0403 else: 0404 built.append('_') 0405 0406 str = ''.join(built) 0407 0408 # List of reserved identifiers 0409 # Initial list from http://cs.smu.ca/~porter/csc/ref/cpp_keywords.html 0410 0411 # Keywords inherited from C90 0412 reserved = ['auto', 0413 'const', 0414 'double', 0415 'float', 0416 'int', 0417 'short', 0418 'struct', 0419 'unsigned', 0420 'break', 0421 'continue', 0422 'else', 0423 'for', 0424 'long', 0425 'signed', 0426 'switch', 0427 'void', 0428 'case', 0429 'default', 0430 'enum', 0431 'goto', 0432 'register', 0433 'sizeof', 0434 'typedef', 0435 'volatile', 0436 'char', 0437 'do', 0438 'extern', 0439 'if', 0440 'return', 0441 'static', 0442 'union', 0443 'while', 0444 # C++-only keywords 0445 'asm', 0446 'dynamic_cast', 0447 'namespace', 0448 'reinterpret_cast', 0449 'try', 0450 'bool', 0451 'explicit', 0452 'new', 0453 'static_cast', 0454 'typeid', 0455 'catch', 0456 'false', 0457 'operator', 0458 'template', 0459 'typename', 0460 'class', 0461 'friend', 0462 'private', 0463 'this', 0464 'using', 0465 'const_cast', 0466 'inline', 0467 'public', 0468 'throw', 0469 'virtual', 0470 'delete', 0471 'mutable', 0472 'protected', 0473 'true', 0474 'wchar_t', 0475 # Operator replacements 0476 'and', 0477 'bitand', 0478 'compl', 0479 'not_eq', 0480 'or_eq', 0481 'xor_eq', 0482 'and_eq', 0483 'bitor', 0484 'not', 0485 'or', 0486 'xor', 0487 # Predefined identifiers 0488 'INT_MIN', 0489 'INT_MAX', 0490 'MAX_RAND', 0491 'NULL', 0492 # Qt 0493 'SIGNAL', 0494 'SLOT', 0495 'signals', 0496 'slots'] 0497 0498 while str in reserved: 0499 str = str + '_' 0500 0501 return str 0502