summaryrefslogtreecommitdiff
path: root/qt4/tools/qt4-types-gen.py
diff options
context:
space:
mode:
Diffstat (limited to 'qt4/tools/qt4-types-gen.py')
-rw-r--r--qt4/tools/qt4-types-gen.py557
1 files changed, 557 insertions, 0 deletions
diff --git a/qt4/tools/qt4-types-gen.py b/qt4/tools/qt4-types-gen.py
new file mode 100644
index 000000000..0f5545d60
--- /dev/null
+++ b/qt4/tools/qt4-types-gen.py
@@ -0,0 +1,557 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk>
+# Copyright (C) 2008 Nokia Corporation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import sys
+import xml.dom.minidom
+from getopt import gnu_getopt
+
+from libtpcodegen import NS_TP, get_descendant_text, get_by_path
+from libqt4codegen import binding_from_usage, binding_from_decl, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_qt4_name, get_headerfile_cmd, RefRegistry
+
+class BrokenSpecException(Exception):
+ pass
+
+class MissingTypes(BrokenSpecException):
+ def __init__(self, types):
+ super(MissingTypes, self).__init__(self)
+ self.types = types
+
+ def __str__(self):
+ typelist = ''.join([' %s' % t for t in self.types])
+ return "The following types were used, but not provided by the spec " \
+ "or by <tp:external-type/> declarations in all.xml:\n%s" % typelist
+
+class UnresolvedDependency(BrokenSpecException):
+ def __init__(self, child, parent):
+ super(UnresolvedDependency, self).__init__(self)
+ self.child = child
+ self.parent = parent
+
+ def __str__(self):
+ return 'Type %s has unresolved dependency on %s' % (
+ self.child, self.parent)
+
+class EmptyStruct(BrokenSpecException):
+ def __init__(self, struct_name):
+ super(EmptyStruct, self).__init__(self)
+ self.struct_name = struct_name
+
+ def __str__(self):
+ return 'tp:struct %s should have some members' % self.struct_name
+
+class MalformedMapping(BrokenSpecException):
+ def __init__(self, mapping_name, members):
+ super(MalformedMapping, self).__init__(self)
+ self.mapping_name = mapping_name
+ self.members = members
+
+ def __str__(self):
+ return 'tp:mapping %s should have 2 members, not %u' % (
+ self.mapping_name, self.members)
+
+class WTF(BrokenSpecException):
+ def __init__(self, element_name):
+ super(BrokenSpecException, self).__init__(self)
+ self.element_name = element_name
+
+ def __str__(self):
+ return 'What the hell is a tp:%s?' % self.element_name
+
+
+class DepInfo:
+ def __init__(self, el, externals, custom_lists):
+ self.el = el
+ name = get_by_path(el, '@name')
+ array_name = get_by_path(el, '@array-name')
+ array_depth = get_by_path(el, '@array-depth')
+ if array_depth:
+ array_depth = int(array_depth)
+ else:
+ array_depth = None
+ self.binding = binding_from_decl(name, array_name, array_depth)
+ self.deps = []
+
+ for member in get_by_path(el, 'member'):
+ sig = member.getAttribute('type')
+ tptype = member.getAttributeNS(NS_TP, 'type')
+
+ if (sig, tptype) in externals:
+ continue
+
+ if tptype.endswith('[]'):
+ tptype = tptype[:-2]
+
+ binding = binding_from_usage(sig, tptype, custom_lists)
+
+ if binding.custom_type:
+ self.deps.append(binding.val)
+
+ self.revdeps = []
+
+class Generator(object):
+ def __init__(self, opts):
+ try:
+ self.namespace = opts['--namespace']
+ self.declfile = opts['--declfile']
+ self.implfile = opts['--implfile']
+ self.realinclude = opts['--realinclude']
+ self.prettyinclude = opts.get('--prettyinclude', self.realinclude)
+ self.extraincludes = opts.get('--extraincludes', None)
+ self.must_define = opts.get('--must-define', None)
+ self.visibility = opts.get('--visibility', '')
+ dom = xml.dom.minidom.parse(opts['--specxml'])
+ except KeyError, k:
+ assert False, 'Missing required parameter %s' % k.args[0]
+
+ self.decls = []
+ self.impls = []
+ self.spec = get_by_path(dom, "spec")[0]
+ self.externals = gather_externals(self.spec)
+ self.custom_lists = gather_custom_lists(self.spec, self.namespace)
+ self.required_custom = []
+ self.required_arrays = []
+ self.to_declare = []
+ self.depinfos = {}
+ self.refs = RefRegistry(self.spec)
+
+ def __call__(self):
+ # Emit comment header
+
+ self.both('/* Generated from ')
+ self.both(get_descendant_text(get_by_path(self.spec, 'title')))
+ version = get_by_path(self.spec, "version")
+
+ if version:
+ self.both(', version ' + get_descendant_text(version))
+
+ self.both(' */\n')
+
+ # Gather info on available and required types
+
+ self.gather_required()
+
+ if self.must_define:
+ self.decl('\n')
+ self.decl('#ifndef %s\n' % self.must_define)
+ self.decl('#error %s\n' % self.must_define)
+ self.decl('#endif')
+
+ self.decl('\n')
+
+ if self.extraincludes:
+ for include in self.extraincludes.split(','):
+ self.decl('#include %s\n' % include)
+
+ self.decl("""
+#include <QtGlobal>
+
+#include <QByteArray>
+#include <QString>
+#include <QStringList>
+#include <QVariantList>
+#include <QVariantMap>
+
+#include <QDBusArgument>
+#include <QDBusMetaType>
+#include <QDBusObjectPath>
+#include <QDBusSignature>
+#include <QDBusVariant>
+
+#include <TelepathyQt4/Global>
+
+/**
+ * \\addtogroup typesconstants Types and constants
+ *
+ * Enumerated, flag, structure, list and mapping types and utility constants.
+ */
+
+/**
+ * \\defgroup struct Structure types
+ * \\ingroup typesconstants
+ *
+ * Structure types generated from the specification.
+ */
+
+/**
+ * \\defgroup list List types
+ * \\ingroup typesconstants
+ *
+ * List types generated from the specification.
+ */
+
+/**
+ * \\defgroup mapping Mapping types
+ * \\ingroup typesconstants
+ *
+ * Mapping types generated from the specification.
+ */
+
+""")
+
+ if self.must_define:
+ self.impl("""
+#define %s""" % self.must_define)
+
+ self.impl("""
+#include "%s"
+""" % self.realinclude)
+
+ self.both("""
+namespace %s
+{
+""" % self.namespace)
+
+ # Emit type definitions for types provided in the spec
+
+ self.provide_all()
+
+ # Emit type registration function
+
+ self.decl("""
+} // namespace %s
+
+""" % self.namespace)
+
+ self.impl("""\
+TELEPATHY_QT4_NO_EXPORT void _registerTypes()
+{
+ static bool registered = false;
+ if (registered)
+ return;
+ registered = true;
+
+""")
+
+ # Emit Qt4 metatype declarations
+
+ self.to_declare.sort()
+
+ for metatype in self.to_declare:
+ self.decl('Q_DECLARE_METATYPE(%s)\n' % metatype)
+ self.impl(' qDBusRegisterMetaType<%s>();\n' % ((metatype.endswith('>') and metatype + ' ') or metatype))
+
+ self.impl("""\
+}
+
+} // namespace %s
+""" % self.namespace)
+
+ # Write output to files
+
+ open(self.declfile, 'w').write(''.join(self.decls).encode("utf-8"))
+ open(self.implfile, 'w').write(''.join(self.impls).encode("utf-8"))
+
+ def decl(self, str):
+ self.decls.append(str)
+
+ def impl(self, str):
+ self.impls.append(str)
+
+ def both(self, str):
+ self.decl(str)
+ self.impl(str)
+
+ def gather_required(self):
+ members = self.spec.getElementsByTagNameNS(NS_TP, 'member')
+ args = self.spec.getElementsByTagName('arg')
+ props = self.spec.getElementsByTagName('property')
+ tp_props = self.spec.getElementsByTagNameNS(NS_TP, 'property')
+
+ for requirer in members + args + props + tp_props:
+ sig = requirer.getAttribute('type')
+ tptype = requirer.getAttributeNS(NS_TP, 'type')
+ external = (sig, tptype) in self.externals
+ binding = binding_from_usage(sig, tptype, self.custom_lists, external)
+
+ if binding.custom_type and binding.val not in self.required_custom:
+ self.required_custom.append(binding.val)
+
+ if not binding.custom_type and binding.array_of and (binding.val, binding.array_of) not in self.required_arrays:
+ self.required_arrays.append((binding.val, binding.array_of))
+
+ def provide_all(self):
+ self.required_arrays.sort()
+ for (val, array_of) in self.required_arrays:
+ real = 'QList<%s>' % array_of
+ self.decl("""\
+/**
+ * \\struct %s
+ * \\ingroup list
+%s\
+ *
+ * Generic list type with %s elements. Convertible with
+ * %s, but needed to have a discrete type in the Qt4 type system.
+ */
+""" % (val, get_headerfile_cmd(self.realinclude, self.prettyinclude), array_of, real))
+ self.decl(self.faketype(val, real))
+ self.to_declare.append(self.namespace + '::' + val)
+
+ structs = self.spec.getElementsByTagNameNS(NS_TP, 'struct')
+ mappings = self.spec.getElementsByTagNameNS(NS_TP, 'mapping')
+ exts = self.spec.getElementsByTagNameNS(NS_TP, 'external-type')
+
+ for deptype in structs + mappings:
+ info = DepInfo(deptype, self.externals, self.custom_lists)
+ self.depinfos[info.binding.val] = info
+
+ leaves = []
+ next_leaves = []
+
+ for val, depinfo in self.depinfos.iteritems():
+ leaf = True
+
+ for dep in depinfo.deps:
+ if not self.depinfos.has_key(dep):
+ raise UnresolvedDependency(val, dep)
+
+ leaf = False
+ self.depinfos[dep].revdeps.append(val)
+
+ if leaf:
+ next_leaves.append(val)
+
+ while leaves or next_leaves:
+ if not leaves:
+ leaves = next_leaves
+ leaves.sort()
+ next_leaves = []
+
+ val = leaves.pop(0)
+ depinfo = self.depinfos[val]
+ self.output_by_depinfo(depinfo)
+
+ for revdep in depinfo.revdeps:
+ revdepinfo = self.depinfos[revdep]
+ revdepinfo.deps.remove(val)
+
+ if not revdepinfo.deps:
+ next_leaves.append(revdep)
+
+ del self.depinfos[val]
+
+ for provider in structs + mappings + exts:
+ name = get_by_path(provider, '@name')
+ array_name = get_by_path(provider, '@array-name')
+ array_depth = get_by_path(provider, '@array-depth')
+ if array_depth:
+ array_depth = int(array_depth)
+ else:
+ array_depth = None
+ sig = provider.getAttribute('type')
+ tptype = provider.getAttribute('name')
+ external = (sig, tptype) in self.externals
+ binding = binding_from_decl(name, array_name, array_depth, external)
+ self.provide(binding.val)
+
+ if binding.array_val:
+ self.provide(binding.array_val)
+
+ d = binding.array_depth
+ while d > 1:
+ d -= 1
+ self.provide(binding.array_val + ('List' * d))
+
+ if self.required_custom:
+ raise MissingTypes(self.required_custom)
+
+ def provide(self, type):
+ if type in self.required_custom:
+ self.required_custom.remove(type)
+
+ def output_by_depinfo(self, depinfo):
+ names, docstrings, bindings = extract_arg_or_member_info(get_by_path(depinfo.el, 'member'), self.custom_lists, self.externals, None, self.refs, ' * ', (' /**', ' */'))
+ members = len(names)
+
+ if depinfo.el.localName == 'struct':
+ if members == 0:
+ raise EmptyStruct(depinfo.binding.val)
+
+ self.decl("""\
+/**
+ * \\struct %(name)s
+ * \\ingroup struct
+%(headercmd)s\
+ *
+ * Structure type generated from the specification.
+%(docstring)s\
+ */
+struct %(visibility)s %(name)s
+{
+""" % {
+ 'name' : depinfo.binding.val,
+ 'headercmd': get_headerfile_cmd(self.realinclude, self.prettyinclude),
+ 'docstring' : format_docstring(depinfo.el, self.refs),
+ 'visibility': self.visibility,
+ })
+
+ for i in xrange(members):
+ self.decl("""\
+%s\
+ %s %s;
+""" % (docstrings[i], bindings[i].val, names[i]))
+
+ self.decl("""\
+};
+
+""")
+
+ self.both('%s bool operator==(%s v1, %s v2)' %
+ (self.visibility,
+ depinfo.binding.inarg,
+ depinfo.binding.inarg))
+ self.decl(';\n')
+ self.impl("""
+{""")
+ if (bindings[0].val != 'QDBusVariant'):
+ self.impl("""
+ return ((v1.%s == v2.%s)""" % (names[0], names[0]))
+ else:
+ self.impl("""
+ return ((v1.%s.variant() == v2.%s.variant())""" % (names[0], names[0]))
+ for i in xrange(1, members):
+ if (bindings[i].val != 'QDBusVariant'):
+ self.impl("""
+ && (v1.%s == v2.%s)""" % (names[i], names[i]))
+ else:
+ self.impl("""
+ && (v1.%s.variant() == v2.%s.variant())""" % (names[i], names[i]))
+ self.impl("""
+ );
+}
+
+""")
+
+ self.decl('inline bool operator!=(%s v1, %s v2)' %
+ (depinfo.binding.inarg, depinfo.binding.inarg))
+ self.decl("""
+{
+ return !operator==(v1, v2);
+}
+""")
+
+ self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, %s val)' %
+ (self.visibility, depinfo.binding.inarg))
+ self.decl(';\n')
+ self.impl("""
+{
+ arg.beginStructure();
+ arg << %s;
+ arg.endStructure();
+ return arg;
+}
+
+""" % ' << '.join(['val.' + name for name in names]))
+
+ self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s val)' %
+ (self.visibility, depinfo.binding.outarg))
+ self.decl(';\n\n')
+ self.impl("""
+{
+ arg.beginStructure();
+ arg >> %s;
+ arg.endStructure();
+ return arg;
+}
+
+""" % ' >> '.join(['val.' + name for name in names]))
+ elif depinfo.el.localName == 'mapping':
+ if members != 2:
+ raise MalformedMapping(depinfo.binding.val, members)
+
+ realtype = 'QMap<%s, %s>' % (bindings[0].val, (bindings[1].val.endswith('>') and bindings[1].val + ' ') or bindings[1].val)
+ self.decl("""\
+/**
+ * \\struct %s
+ * \\ingroup mapping
+%s\
+ *
+ * Mapping type generated from the specification. Convertible with
+ * %s, but needed to have a discrete type in the Qt4 type system.
+%s\
+ */
+""" % (depinfo.binding.val, get_headerfile_cmd(self.realinclude, self.prettyinclude), realtype, format_docstring(depinfo.el, self.refs)))
+ self.decl(self.faketype(depinfo.binding.val, realtype))
+ else:
+ raise WTF(depinfo.el.localName)
+
+ self.to_declare.append(self.namespace + '::' + depinfo.binding.val)
+
+ if depinfo.binding.array_val:
+ self.to_declare.append('%s::%s' % (self.namespace, depinfo.binding.array_val))
+ self.decl("""\
+/**
+ * \\ingroup list
+%s\
+ *
+ * Array of %s values.
+ */
+typedef %s %s;
+
+""" % (get_headerfile_cmd(self.realinclude, self.prettyinclude), depinfo.binding.val, 'QList<%s>' % depinfo.binding.val, depinfo.binding.array_val))
+
+ i = depinfo.binding.array_depth
+ while i > 1:
+ i -= 1
+ self.to_declare.append('%s::%s%s' % (self.namespace, depinfo.binding.array_val, ('List' * i)))
+ list_of = depinfo.binding.array_val + ('List' * (i-1))
+ self.decl("""\
+/**
+ * \\ingroup list
+%s\
+ *
+ * Array of %s values.
+ */
+typedef QList<%s> %sList;
+
+""" % (get_headerfile_cmd(self.realinclude, self.prettyinclude), list_of, list_of, list_of))
+
+ def faketype(self, fake, real):
+ return """\
+struct %(visibility)s %(fake)s : public %(real)s
+{
+ inline %(fake)s() : %(real)s() {}
+ inline %(fake)s(const %(real)s& a) : %(real)s(a) {}
+
+ inline %(fake)s& operator=(const %(real)s& a)
+ {
+ *(static_cast<%(real)s*>(this)) = a;
+ return *this;
+ }
+};
+
+""" % {'fake' : fake, 'real' : real, 'visibility': self.visibility}
+
+if __name__ == '__main__':
+ options, argv = gnu_getopt(sys.argv[1:], '',
+ ['declfile=',
+ 'implfile=',
+ 'realinclude=',
+ 'prettyinclude=',
+ 'extraincludes=',
+ 'must-define=',
+ 'namespace=',
+ 'specxml=',
+ 'visibility=',
+ ])
+
+ try:
+ Generator(dict(options))()
+ except BrokenSpecException as e:
+ print >> sys.stderr, 'Your spec is broken, dear developer! %s' % e
+ sys.exit(42)