Warning, file /network/telepathy-logger-qt/tools/qt-client-gen.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #!/usr/bin/python
0002 #
0003 # Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk>
0004 # Copyright (C) 2008 Nokia Corporation
0005 #
0006 # This library is free software; you can redistribute it and/or
0007 # modify it under the terms of the GNU Lesser General Public
0008 # License as published by the Free Software Foundation; either
0009 # version 2.1 of the License, or (at your option) any later version.
0010 #
0011 # This library is distributed in the hope that it will be useful,
0012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014 # Lesser General Public License for more details.
0015 #
0016 # You should have received a copy of the GNU Lesser General Public
0017 # License along with this library; if not, write to the Free Software
0018 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0019 
0020 from sys import argv
0021 import xml.dom.minidom
0022 import codecs
0023 from getopt import gnu_getopt
0024 
0025 from libtpcodegen import NS_TP, get_descendant_text, get_by_path
0026 from libqtcodegen import binding_from_usage, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_headerfile_cmd, get_qt_name, qt_identifier_escape, RefRegistry
0027 
0028 class Generator(object):
0029     def __init__(self, opts):
0030         try:
0031             self.group = opts.get('--group', '')
0032             self.headerfile = opts['--headerfile']
0033             self.implfile = opts['--implfile']
0034             self.namespace = opts['--namespace']
0035             self.typesnamespace = opts['--typesnamespace']
0036             self.realinclude = opts['--realinclude']
0037             self.prettyinclude = opts.get('--prettyinclude')
0038             self.extraincludes = opts.get('--extraincludes', None)
0039             self.mainiface = opts.get('--mainiface', None)
0040             self.must_define = opts.get('--must-define', None)
0041             self.dbus_proxy = opts.get('--dbus-proxy',
0042                     'Tp::DBusProxy')
0043             self.visibility = opts.get('--visibility', '')
0044             ifacedom = xml.dom.minidom.parse(opts['--ifacexml'])
0045             specdom = xml.dom.minidom.parse(opts['--specxml'])
0046         except KeyError, k:
0047             assert False, 'Missing required parameter %s' % k.args[0]
0048 
0049         self.hs = []
0050         self.bs = []
0051         self.ifacenodes = ifacedom.getElementsByTagName('node')
0052         self.spec, = get_by_path(specdom, "spec")
0053         self.custom_lists = gather_custom_lists(self.spec, self.typesnamespace)
0054         self.externals = gather_externals(self.spec)
0055         self.refs = RefRegistry(self.spec)
0056 
0057     def __call__(self):
0058         # Output info header and includes
0059         self.h("""\
0060 /*
0061  * This file contains D-Bus client proxy classes generated by qt-client-gen.py.
0062  *
0063  * This file can be distributed under the same terms as the specification from
0064  * which it was generated.
0065  */
0066 """)
0067 
0068         if self.must_define:
0069             self.h('\n')
0070             self.h('#ifndef %s\n' % self.must_define)
0071             self.h('#error %s\n' % self.must_define)
0072             self.h('#endif\n')
0073 
0074         self.h('\n')
0075 
0076         if self.extraincludes:
0077             for include in self.extraincludes.split(','):
0078                 self.h('#include %s\n' % include)
0079 
0080         self.h("""
0081 #include <QtGlobal>
0082 
0083 #include <QString>
0084 #include <QObject>
0085 #include <QVariant>
0086 
0087 #include <QDBusPendingReply>
0088 
0089 #include <TelepathyQt/AbstractInterface>
0090 #include <TelepathyQt/DBusProxy>
0091 #include <TelepathyQt/Global>
0092 
0093 namespace Tp
0094 {
0095 class PendingVariant;
0096 class PendingOperation;
0097 }
0098 
0099 """)
0100 
0101         if self.must_define:
0102             self.b("""#define %s\n""" % (self.must_define))
0103 
0104         self.b("""#include "%s"
0105 
0106 """ % self.realinclude)
0107 
0108         # Begin namespace
0109         for ns in self.namespace.split('::'):
0110             self.hb("""\
0111 namespace %s
0112 {
0113 """ % ns)
0114 
0115         # Output interface proxies
0116         def ifacenodecmp(x, y):
0117             xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' for node in x, y]
0118 
0119             if xname == self.mainiface:
0120                 return -1
0121             elif yname == self.mainiface:
0122                 return 1
0123             else:
0124                 return cmp(xname, yname)
0125 
0126         self.ifacenodes.sort(cmp=ifacenodecmp)
0127         for ifacenode in self.ifacenodes:
0128             self.do_ifacenode(ifacenode)
0129 
0130         # End namespace
0131         self.hb(''.join(['}\n' for ns in self.namespace.split('::')]))
0132 
0133         # Add metatype declaration - otherwise QTBUG #2151 might be triggered
0134         for ifacenode in self.ifacenodes:
0135             classname = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface'
0136             self.h("Q_DECLARE_METATYPE(" + self.namespace + "::" + classname + "*)\n")
0137 
0138         # Write output to files
0139         (codecs.getwriter('utf-8')(open(self.headerfile, 'w'))).write(''.join(self.hs))
0140         (codecs.getwriter('utf-8')(open(self.implfile, 'w'))).write(''.join(self.bs))
0141 
0142     def do_ifacenode(self, ifacenode):
0143         # Extract info
0144         name = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface'
0145         iface, = get_by_path(ifacenode, 'interface')
0146         dbusname = iface.getAttribute('name')
0147 
0148         # Begin class, constructors
0149         self.h("""
0150 /**
0151  * \\class %(name)s
0152 %(headercmd)s\
0153 %(groupcmd)s\
0154  *
0155  * Proxy class providing a 1:1 mapping of the D-Bus interface "%(dbusname)s".
0156  */
0157 class %(visibility)s %(name)s : public Tp::AbstractInterface
0158 {
0159     Q_OBJECT
0160 
0161 public:
0162     /**
0163      * Returns the name of the interface "%(dbusname)s", which this class
0164      * represents.
0165      *
0166      * \\return The D-Bus interface name.
0167      */
0168     static inline QLatin1String staticInterfaceName()
0169     {
0170         return QLatin1String("%(dbusname)s");
0171     }
0172 
0173     /**
0174      * Creates a %(name)s associated with the given object on the session bus.
0175      *
0176      * \\param busName Name of the service the object is on.
0177      * \\param objectPath Path to the object on the service.
0178      * \\param parent Passed to the parent class constructor.
0179      */
0180     %(name)s(
0181         const QString& busName,
0182         const QString& objectPath,
0183         QObject* parent = 0
0184     );
0185 
0186     /**
0187      * Creates a %(name)s associated with the given object on the given bus.
0188      *
0189      * \\param connection The bus via which the object can be reached.
0190      * \\param busName Name of the service the object is on.
0191      * \\param objectPath Path to the object on the service.
0192      * \\param parent Passed to the parent class constructor.
0193      */
0194     %(name)s(
0195         const QDBusConnection& connection,
0196         const QString& busName,
0197         const QString& objectPath,
0198         QObject* parent = 0
0199     );
0200 """ % {'name' : name,
0201        'headercmd' : get_headerfile_cmd(self.realinclude, self.prettyinclude),
0202        'groupcmd' : self.group and (' * \\ingroup %s\n' % self.group),
0203        'dbusname' : dbusname,
0204        'visibility': self.visibility,
0205        })
0206 
0207         self.b("""
0208 %(name)s::%(name)s(const QString& busName, const QString& objectPath, QObject *parent)
0209     : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), QDBusConnection::sessionBus(), parent)
0210 {
0211 }
0212 
0213 %(name)s::%(name)s(const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject *parent)
0214     : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), connection, parent)
0215 {
0216 }
0217 """ % {'name' : name})
0218 
0219         # Construct from DBusProxy subclass
0220         self.h("""
0221     /**
0222      * Creates a %(name)s associated with the same object as the given proxy.
0223      *
0224      * \\param proxy The proxy to use. It will also be the QObject::parent()
0225      *               for this object.
0226      */
0227     %(name)s(%(dbus_proxy)s *proxy);
0228 """ % {'name' : name,
0229        'dbus_proxy' : self.dbus_proxy})
0230 
0231         self.b("""
0232 %(name)s::%(name)s(%(dbus_proxy)s *proxy)
0233     : Tp::AbstractInterface(proxy, staticInterfaceName())
0234 {
0235 }
0236 """ % {'name' : name,
0237        'dbus_proxy' : self.dbus_proxy})
0238 
0239         # Main interface
0240         mainiface = self.mainiface or 'Tp::AbstractInterface'
0241 
0242         if mainiface != self.namespace + '::' + name:
0243             self.h("""
0244     /**
0245      * Creates a %(name)s associated with the same object as the given proxy.
0246      * Additionally, the created proxy will have the same parent as the given
0247      * proxy.
0248      *
0249      * \\param mainInterface The proxy to use.
0250      */
0251     explicit %(name)s(const %(mainiface)s& mainInterface);
0252 
0253     /**
0254      * Creates a %(name)s associated with the same object as the given proxy.
0255      * However, a different parent object can be specified.
0256      *
0257      * \\param mainInterface The proxy to use.
0258      * \\param parent Passed to the parent class constructor.
0259      */
0260     %(name)s(const %(mainiface)s& mainInterface, QObject* parent);
0261 """ % {'name' : name,
0262        'mainiface' : mainiface})
0263 
0264             self.b("""
0265 %(name)s::%(name)s(const %(mainiface)s& mainInterface)
0266     : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), mainInterface.parent())
0267 {
0268 }
0269 
0270 %(name)s::%(name)s(const %(mainiface)s& mainInterface, QObject *parent)
0271     : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), parent)
0272 {
0273 }
0274 """ % {'name' : name,
0275        'mainiface' : mainiface})
0276 
0277         # Properties
0278         has_props = False
0279         for prop in get_by_path(iface, 'property'):
0280             # Skip tp:properties
0281             if not prop.namespaceURI:
0282                 self.do_prop(prop)
0283                 has_props = True
0284 
0285         self.h("""
0286     /**
0287      * Request all of the DBus properties on the interface.
0288      *
0289      * \\return A pending variant map which will emit finished when the properties have
0290      *          been retrieved.
0291      */
0292     Tp::PendingVariantMap *requestAllProperties() const
0293     {
0294         return internalRequestAllProperties();
0295     }
0296 """)
0297 
0298         # Methods
0299         methods = get_by_path(iface, 'method')
0300 
0301         if methods:
0302             self.h("""
0303 public Q_SLOTS:\
0304 """)
0305 
0306             for method in methods:
0307                 self.do_method(method)
0308 
0309         # Signals
0310         signals = get_by_path(iface, 'signal')
0311 
0312         if signals:
0313             self.h("""
0314 Q_SIGNALS:\
0315 """)
0316 
0317             for signal in signals:
0318                 self.do_signal(signal)
0319 
0320         # invalidated handler (already a slot in the superclass)
0321         # we can't just use disconnect(this, NULL, NULL, NULL) because
0322         # (a) that would disconnect QObject::destroyed() and other non-D-Bus
0323         # signals, and (b) QtDBus doesn't support that usage anyway (it needs
0324         # specific signals in order to remove its signal match rules)
0325         self.h("""
0326 protected:
0327     virtual void invalidate(Tp::DBusProxy *, const QString &, const QString &);
0328 """)
0329 
0330         self.b("""
0331 void %(name)s::invalidate(Tp::DBusProxy *proxy,
0332         const QString &error, const QString &message)
0333 {
0334 """ % {'name' : name})
0335 
0336         for signal in signals:
0337             self.do_signal_disconnect(signal)
0338 
0339         self.b("""
0340     Tp::AbstractInterface::invalidate(proxy, error, message);
0341 }
0342 """)
0343 
0344         # Close class
0345         self.h("""\
0346 };
0347 """)
0348 
0349     def do_prop(self, prop):
0350         name = prop.getAttribute('name')
0351         access = prop.getAttribute('access')
0352         gettername = name
0353         settername = None
0354         docstring = format_docstring(prop, self.refs, '     * ').replace('*/', '&#42;&#47;')
0355 
0356         sig = prop.getAttribute('type')
0357         tptype = prop.getAttributeNS(NS_TP, 'type')
0358         binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace)
0359 
0360         if 'write' in access:
0361             settername = 'set' + name
0362 
0363         if 'read' in access:
0364             self.h("""
0365     /**
0366      * Asynchronous getter for the remote object property \\c %(name)s of type \\c %(val)s.
0367      *
0368 %(docstring)s\
0369      *
0370      * \\return A pending variant which will emit finished when the property has been
0371      *          retrieved.
0372      */
0373     inline Tp::PendingVariant *%(gettername)s() const
0374     {
0375         return internalRequestProperty(QLatin1String("%(name)s"));
0376     }
0377 """ % {'name' : name,
0378        'docstring' : docstring,
0379        'val' : binding.val,
0380        'gettername' : 'requestProperty' + name})
0381 
0382         if 'write' in access:
0383             self.h("""
0384     /**
0385      * Asynchronous setter for the remote object property \\c %(name)s of type \\c %(type)s.
0386      *
0387 %(docstring)s\
0388      *
0389      * \\return A pending operation which will emit finished when the property has been
0390      *          set.
0391      */
0392     inline Tp::PendingOperation *%(settername)s(%(type)s newValue)
0393     {
0394         return internalSetProperty(QLatin1String("%(name)s"), QVariant::fromValue(newValue));
0395     }
0396 """ % {'name' : name,
0397        'docstring' : docstring,
0398        'type' : binding.val,
0399        'name' : name,
0400        'settername' : 'setProperty' + name})
0401 
0402     def do_method(self, method):
0403         name = method.getAttribute('name')
0404         args = get_by_path(method, 'arg')
0405         argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists,
0406                 self.externals, self.typesnamespace, self.refs, '     *     ')
0407 
0408         inargs = []
0409         outargs = []
0410 
0411         for i in xrange(len(args)):
0412             if args[i].getAttribute('direction') == 'out':
0413                 outargs.append(i)
0414             else:
0415                 inargs.append(i)
0416                 assert argnames[i] != None, 'No argument name for input argument at index %d for method %s' % (i, name)
0417 
0418         rettypes = ', '.join([argbindings[i].val for i in outargs])
0419         params = ', '.join([argbindings[i].inarg + ' ' + argnames[i] for i in inargs])
0420         if params:
0421             params += ', int timeout = -1'
0422         else:
0423             params = 'int timeout = -1'
0424 
0425         self.h("""
0426     /**
0427      * Begins a call to the D-Bus method \\c %s on the remote object.
0428 %s\
0429      *
0430      * Note that \\a timeout is ignored as of now. It will be used once
0431      * http://bugreports.qt.nokia.com/browse/QTBUG-11775 is fixed.
0432      *
0433 """ % (name, format_docstring(method, self.refs, '     * ')))
0434 
0435         for i in inargs:
0436             if argdocstrings[i]:
0437                 self.h("""\
0438      *
0439      * \\param %s
0440 %s\
0441 """ % (argnames[i], argdocstrings[i]))
0442 
0443         self.h("""\
0444      * \\param timeout The timeout in milliseconds.
0445 """)
0446 
0447         for i in outargs:
0448             if argdocstrings[i]:
0449                 self.h("""\
0450      *
0451      * \\return
0452 %s\
0453 """ % argdocstrings[i])
0454 
0455         self.h("""\
0456      */
0457     inline QDBusPendingReply<%(rettypes)s> %(name)s(%(params)s)
0458     {
0459         if (!invalidationReason().isEmpty()) {
0460             return QDBusPendingReply<%(rettypes)s>(QDBusMessage::createError(
0461                 invalidationReason(),
0462                 invalidationMessage()
0463             ));
0464         }
0465 """ % {'rettypes' : rettypes,
0466        'name' : name,
0467        'params' : params})
0468 
0469         if inargs:
0470             self.h("""
0471         QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(),
0472                 this->staticInterfaceName(), QLatin1String("%s"));
0473         callMessage << %s;
0474         return this->connection().asyncCall(callMessage, timeout);
0475     }
0476 """ % (name, ' << '.join(['QVariant::fromValue(%s)' % argnames[i] for i in inargs])))
0477         else:
0478             self.h("""
0479         QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(),
0480                 this->staticInterfaceName(), QLatin1String("%s"));
0481         return this->connection().asyncCall(callMessage, timeout);
0482     }
0483 """ % name)
0484 
0485     def do_signal(self, signal):
0486         name = signal.getAttribute('name')
0487         argnames, argdocstrings, argbindings = extract_arg_or_member_info(get_by_path(signal,
0488             'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, '     *     ')
0489 
0490         self.h("""
0491     /**
0492      * Represents the signal \\c %s on the remote object.
0493 %s\
0494 """ % (name, format_docstring(signal, self.refs, '     * ')))
0495 
0496         for i in xrange(len(argnames)):
0497             assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name)
0498             if argdocstrings[i]:
0499                 self.h("""\
0500      *
0501      * \\param %s
0502 %s\
0503 """ % (argnames[i], argdocstrings[i]))
0504 
0505         self.h("""\
0506      */
0507     void %s(%s);
0508 """ % (name, ', '.join(['%s %s' % (binding.inarg, name) for binding, name in zip(argbindings, argnames)])))
0509 
0510     def do_signal_disconnect(self, signal):
0511         name = signal.getAttribute('name')
0512         _, _, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'),
0513                 self.custom_lists, self.externals, self.typesnamespace, self.refs, '     *     ')
0514 
0515         self.b("""\
0516     disconnect(this, SIGNAL(%s(%s)), NULL, NULL);
0517 """ % (name, ', '.join([binding.inarg for binding in argbindings])))
0518 
0519     def h(self, str):
0520         self.hs.append(str)
0521 
0522     def b(self, str):
0523         self.bs.append(str)
0524 
0525     def hb(self, str):
0526         self.h(str)
0527         self.b(str)
0528 
0529 
0530 if __name__ == '__main__':
0531     options, argv = gnu_getopt(argv[1:], '',
0532             ['group=',
0533              'namespace=',
0534              'typesnamespace=',
0535              'headerfile=',
0536              'implfile=',
0537              'ifacexml=',
0538              'specxml=',
0539              'realinclude=',
0540              'prettyinclude=',
0541              'extraincludes=',
0542              'mainiface=',
0543              'must-define=',
0544              'dbus-proxy=',
0545              'visibility='])
0546 
0547     Generator(dict(options))()