diff options
author | Michele Calgaro <[email protected]> | 2019-04-27 00:21:43 +0900 |
---|---|---|
committer | Michele Calgaro <[email protected]> | 2019-04-27 00:21:43 +0900 |
commit | f9c9e15267bd0319a8190ba146d7be95c37dac2e (patch) | |
tree | 1180022eddef0ffc8f44b030e721b6efef4f8b2a /src/tools/dbusxml2qt3 | |
parent | 7d3c2ed4e8cdf9292059a4fc8f3678ca9d1d7c5c (diff) | |
download | dbus-1-tqt-f9c9e15267bd0319a8190ba146d7be95c37dac2e.tar.gz dbus-1-tqt-f9c9e15267bd0319a8190ba146d7be95c37dac2e.zip |
Moved source files to "src" folder.
Signed-off-by: Michele Calgaro <[email protected]>
Diffstat (limited to 'src/tools/dbusxml2qt3')
-rw-r--r-- | src/tools/dbusxml2qt3/LICENSE | 18 | ||||
-rw-r--r-- | src/tools/dbusxml2qt3/classgen.cpp | 1091 | ||||
-rw-r--r-- | src/tools/dbusxml2qt3/classgen.h | 54 | ||||
-rw-r--r-- | src/tools/dbusxml2qt3/main.cpp | 637 | ||||
-rw-r--r-- | src/tools/dbusxml2qt3/methodgen.cpp | 1793 | ||||
-rw-r--r-- | src/tools/dbusxml2qt3/methodgen.h | 153 |
6 files changed, 3746 insertions, 0 deletions
diff --git a/src/tools/dbusxml2qt3/LICENSE b/src/tools/dbusxml2qt3/LICENSE new file mode 100644 index 0000000..1edf08c --- /dev/null +++ b/src/tools/dbusxml2qt3/LICENSE @@ -0,0 +1,18 @@ +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/tools/dbusxml2qt3/classgen.cpp b/src/tools/dbusxml2qt3/classgen.cpp new file mode 100644 index 0000000..12051c0 --- /dev/null +++ b/src/tools/dbusxml2qt3/classgen.cpp @@ -0,0 +1,1091 @@ +/* +* Copyright (C) 2007 Kevin Krammer <[email protected]> +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +// TQt includes +#include <tqdom.h> +#include <tqfile.h> +#include <tqstringlist.h> +#include <tqtextstream.h> + +// local includes +#include "classgen.h" +#include "methodgen.h" + +class Set : public TQMap<TQString, bool> +{ +public: + void insertString(const TQString& key) + { + insert(key, true); + } + + void removeString(const TQString& key) + { + erase(key); + } + + void insertStringList(const TQStringList& list) + { + TQStringList::const_iterator it = list.begin(); + TQStringList::const_iterator endIt = list.end(); + for (; it != endIt; ++it) + { + insert(*it, true); + } + } +}; + +static void writeFileHeader(TQTextStream& stream) +{ + stream << "// File autogenerated" << endl; + stream << endl; +} + +static void writeFileFooter(TQTextStream& stream) +{ + stream << "// End of File" << endl; + stream << endl; +} + +static void openIncludeGuard(const TQString& className, TQTextStream& stream) +{ + stream << "#if !defined(" << className.upper() << "_H_INCLUDED)" << endl; + stream << "#define " << className.upper() << "_H_INCLUDED" << endl; + stream << endl; +} + +static void closeIncludeGuard(const TQString& className, TQTextStream& stream) +{ + stream << "#endif //" << className.upper() << "_H_INCLUDED" << endl; + stream << endl; +} + +static void openNamespaces(const TQStringList& namespaces, TQTextStream& stream) +{ + TQStringList::const_iterator it = namespaces.begin(); + TQStringList::const_iterator endIt = namespaces.end(); + for (; it != endIt; ++it) + { + stream << "namespace " << *it << endl; + stream << "{" << endl; + } + stream << endl; +} + +static void closeNamespaces(const TQStringList& namespaces, TQTextStream& stream) +{ + TQStringList::const_iterator it = namespaces.end(); + TQStringList::const_iterator endIt = namespaces.end(); + for (--it; it != endIt; --it) + { + stream << "}; // namespace " << *it << endl; + stream << endl; + } +} + +static void writeIncludes(const TQString& description, const TQStringList& includes, + TQTextStream& stream) +{ + if (includes.isEmpty()) return; + + stream << "// " << description << " includes" << endl; + + TQStringList::const_iterator it = includes.begin(); + TQStringList::const_iterator endIt = includes.end(); + for (;it != endIt; ++it) + { + stream << "#include " << *it << endl; + } + + stream << endl; +} + +static void extractHeaderIncludes(const Method& method, + TQMap<TQString, Set>& includes) +{ + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + if ((*it).headerIncludes.isEmpty()) continue; + + TQMap<TQString, TQStringList>::const_iterator mapIt = + (*it).headerIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator mapEndIt = + (*it).headerIncludes.end(); + + for (; mapIt != mapEndIt; ++mapIt) + { + includes[mapIt.key()].insertStringList(mapIt.data()); + } + } +} + +static void extractForwardDeclarations(const Method& method, Set& forwards) +{ + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + if ((*it).forwardDeclarations.isEmpty()) continue; + + forwards.insertStringList((*it).forwardDeclarations); + } +} + +static void writeHeaderIncludes(const Class& classData, Class::Role role, + TQTextStream& stream) +{ + TQMap<TQString, Set> includes; + Set forwards; + + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if ((*it).arguments.isEmpty()) continue; + + extractHeaderIncludes(*it, includes); + extractForwardDeclarations(*it, forwards); + } + + it = classData.msignals.begin(); + endIt = classData.msignals.end(); + for (; it != endIt; ++it) + { + if ((*it).arguments.isEmpty()) continue; + + extractHeaderIncludes(*it, includes); + extractForwardDeclarations(*it, forwards); + } + + + TQValueList<Property>::const_iterator propertyIt = classData.properties.begin(); + TQValueList<Property>::const_iterator propertyEndIt = classData.properties.end(); + for (; propertyIt != propertyEndIt; ++propertyIt) + { + if (!(*propertyIt).headerIncludes.isEmpty()) + { + TQMap<TQString, TQStringList>::const_iterator mapIt = + (*propertyIt).headerIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator mapEndIt = + (*propertyIt).headerIncludes.end(); + + for (; mapIt != mapEndIt; ++mapIt) + { + includes[mapIt.key()].insertStringList(mapIt.data()); + } + } + + if (!(*propertyIt).forwardDeclarations.isEmpty()) + { + forwards.insertStringList((*propertyIt).forwardDeclarations); + } + } + + switch (role) + { + case Class::Interface: + includes["tqdbus"].insertString("<tqdbusobject.h>"); + forwards.insertString("class TQT_DBusError"); + forwards.insertString("class TQDomElement"); + if (!classData.msignals.isEmpty()) + forwards.insertString("class TQString"); + if (!classData.asyncMethods.isEmpty()) + { + includes["TQt"].insertString("<tqmap.h>"); + forwards.erase("template <typename K, typename V> class TQMap"); + + includes["tqdbus"].insertString("<tqdbusmessage.h>"); + forwards.erase("class TQT_DBusMessage"); + } + break; + + case Class::Proxy: + includes["TQt"].insertString("<tqobject.h>"); + forwards.insertString("class TQT_DBusConnection"); + forwards.insertString("class TQT_DBusError"); + forwards.insertString("class TQT_DBusMessage"); + forwards.insertString("class TQT_DBusProxy"); + forwards.insertString("class TQString"); + if (!classData.properties.isEmpty()) + forwards.insertString("class TQT_DBusVariant"); + if (!classData.asyncMethods.isEmpty()) + { + includes["TQt"].insertString("<tqmap.h>"); + forwards.erase("template <typename K, typename V> class TQMap"); + } + break; + + case Class::Node: + includes["tqdbus"].insertString("<tqdbusobject.h>"); + forwards.insertString("class TQT_DBusConnection"); + forwards.insertString("class TQString"); + break; + } + + includes["tqdbus"].insertString("<tqdbuserror.h>"); + + if (!includes["TQt"].isEmpty()) + writeIncludes("TQt", includes["TQt"].keys(), stream); + + if (!includes["tqdbus"].isEmpty()) + writeIncludes("TQt D-Bus", includes["tqdbus"].keys(), stream); + + if (!includes["local"].isEmpty()) + writeIncludes("local", includes["local"].keys(), stream); + + stream << "// forward declarations" << endl; + Set::const_iterator setIt = forwards.begin(); + Set::const_iterator setEndIt = forwards.end(); + for (; setIt != setEndIt; ++setIt) + { + stream << setIt.key() << ";" << endl; + } + stream << endl; +} + +static void extractSourceIncludes(const Method& method, + TQMap<TQString, Set>& includes) +{ + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + if ((*it).sourceIncludes.isEmpty()) continue; + + TQMap<TQString, TQStringList>::const_iterator mapIt = + (*it).sourceIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator mapEndIt = + (*it).sourceIncludes.end(); + + for (; mapIt != mapEndIt; ++mapIt) + { + includes[mapIt.key()].insertStringList(mapIt.data()); + } + } +} + +static void writeSourceIncludes(const Class& classData, Class::Role role, + TQTextStream& stream) +{ + TQMap<TQString, Set> includes; + + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if ((*it).arguments.isEmpty()) continue; + + extractSourceIncludes(*it, includes); + } + + it = classData.msignals.begin(); + endIt = classData.msignals.end(); + for (; it != endIt; ++it) + { + if ((*it).arguments.isEmpty()) continue; + + extractSourceIncludes(*it, includes); + } + + TQValueList<Property>::const_iterator propertyIt = classData.properties.begin(); + TQValueList<Property>::const_iterator propertyEndIt = classData.properties.end(); + for (; propertyIt != propertyEndIt; ++propertyIt) + { + if ((*propertyIt).sourceIncludes.isEmpty()) continue; + + TQMap<TQString, TQStringList>::const_iterator mapIt = + (*propertyIt).sourceIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator mapEndIt = + (*propertyIt).sourceIncludes.end(); + + for (; mapIt != mapEndIt; ++mapIt) + { + includes[mapIt.key()].insertStringList(mapIt.data()); + } + } + + switch (role) + { + case Class::Interface: + includes["TQt"].insertString("<tqdom.h>"); + includes["tqdbus"].insertString("<tqdbuserror.h>"); + includes["tqdbus"].insertString("<tqdbusmessage.h>"); + break; + + case Class::Proxy: + includes["tqdbus"].insertString("<tqdbuserror.h>"); + includes["tqdbus"].insertString("<tqdbusmessage.h>"); + includes["tqdbus"].insertString("<tqdbusproxy.h>"); + if (!classData.properties.isEmpty()) + { + includes["tqdbus"].insertString("<tqdbusconnection.h>"); + includes["tqdbus"].insertString("<tqdbusvariant.h>"); + } + break; + + case Class::Node: + includes["TQt"].insertString("<tqdom.h>"); + includes["TQt"].insertString("<tqmap.h>"); + includes["tqdbus"].insertString("<tqdbusconnection.h>"); + includes["tqdbus"].insertString("<tqdbusmessage.h>"); + break; + } + + if (!includes["TQt"].isEmpty()) + writeIncludes("TQt", includes["TQt"].keys(), stream); + + if (!includes["tqdbus"].isEmpty()) + writeIncludes("TQt D-Bus", includes["tqdbus"].keys(), stream); + + if (!includes["local"].isEmpty()) + writeIncludes("local", includes["local"].keys(), stream); + + stream << endl; +} + +static void writeInterfaceIncludes(const TQValueList<Class> interfaces, + TQTextStream& stream) +{ + stream << "// interface classes includes" << endl; + + TQValueList<Class>::const_iterator it = interfaces.begin(); + TQValueList<Class>::const_iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + stream << "#include \"" << (*it).name.lower() << ".h\"" << endl; + } + + stream << "#include \"introspectableinterface.h\"" << endl; + + stream << endl; +} + +static void openClassDeclaration(const Class& classData, + Class::Role role, TQTextStream& stream) +{ + switch (role) + { + case Class::Interface: + stream << "class " << classData.name << " : public TQT_DBusObjectBase" + << endl; + stream << "{" << endl; + stream << "public:" << endl; + stream << " virtual ~" << classData.name << "() {}" << endl; + stream << endl; + stream << " static void buildIntrospectionData(TQDomElement& interfaceElement);" << endl; + break; + + case Class::Proxy: + stream << "class " << classData.name << " : public TQObject" << endl; + stream << "{" << endl; + stream << " Q_OBJECT" << endl; + stream << " " << endl; + stream << "public:" << endl; + stream << " " << classData.name + << "(const TQString& service, const TQString& path, TQObject* parent = 0, const char* name = 0);" << endl; + stream << endl; + + stream << " virtual ~" << classData.name << "();" << endl; + stream << endl; + + stream << " void setConnection(const TQT_DBusConnection& connection);" + << endl; + break; + + case Class::Node: + stream << "class " << classData.name << " : public TQT_DBusObjectBase" + << endl; + stream << "{" << endl; + stream << "public:" << endl; + stream << " " << classData.name << "();" << endl; + stream << endl; + stream << " virtual ~" << classData.name << "();" << endl; + stream << endl; + stream << " bool registerObject(const TQT_DBusConnection& connection, " + << "const TQString& path);" << endl; + stream << endl; + stream << " void unregisterObject();" << endl; + stream << endl; + stream << "protected:" << endl; + stream << " virtual TQT_DBusObjectBase* createInterface(" + << "const TQString& interfaceName) = 0;" << endl; + stream << endl; + stream << "protected: // usually no need to reimplement" << endl; + stream << " virtual bool handleMethodCall(const TQT_DBusMessage& message);" << endl; + stream << endl; + stream << "private:" << endl; + stream << " class Private;" << endl; + stream << " Private* m_private;" << endl; + break; + } + + stream << endl; +} + +static void closeClassDeclaration(const Class& classData, Class::Role role, + TQTextStream& stream) +{ + switch (role) + { + case Class::Interface: + break; + + case Class::Proxy: + stream << "private: // Hiding copy constructor and assignment operator" << endl; + stream << " " << classData.name << "(const " + << classData.name << "&);" << endl; + stream << " " << classData.name << "& operator=(const " + << classData.name << "&);" << endl; + break; + + case Class::Node: + stream << "private: // Hiding copy constructor and assignment operator" << endl; + stream << " " << classData.name << "(const " + << classData.name << "&);" << endl; + stream << " " << classData.name << "& operator=(const " + << classData.name << "&);" << endl; + break; + } + stream << "}; // class " << classData.name << endl; + stream << endl; +} + +static void writeMethodDeclarations(const Class& classData, Class::Role role, + TQTextStream& stream) +{ + if (role == Class::Interface && !classData.asyncReplyMethods.isEmpty()) + { + stream << "public:" << endl; + + TQValueList<Method>::const_iterator it = + classData.asyncReplyMethods.begin(); + TQValueList<Method>::const_iterator endIt = + classData.asyncReplyMethods.end(); + for (; it != endIt; ++it) + { + Method method = *it; + method.name += "AsyncReply"; + + stream << " virtual void "; + MethodGenerator::writeMethodDeclaration(method, false, false, stream); + + stream << " virtual void " << (*it).name + << "AsyncError(int asyncCallId, const TQT_DBusError& error);" + << endl; + stream << endl; + } + } + + if (!classData.methods.isEmpty() || !classData.asyncMethods.isEmpty()) + { + bool pureVirtual = true; + switch (role) + { + case Class::Interface: + pureVirtual = true; + stream << "protected:" << endl; + break; + + case Class::Proxy: + pureVirtual = false; + stream << "public:" << endl; + break; + + case Class::Node: // no variable methods + break; + } + + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if ((*it).async) continue; + + stream << " virtual bool "; + MethodGenerator::writeMethodDeclaration(*it, pureVirtual, true, stream); + } + + it = classData.asyncMethods.begin(); + endIt = classData.asyncMethods.end(); + for (; it != endIt; ++it) + { + Method method = *it; + method.name += "Async"; + + switch (role) + { + case Class::Interface: + stream << " virtual void "; + MethodGenerator::writeMethodDeclaration(method, pureVirtual, false, stream); + break; + + case Class::Proxy: + stream << " virtual bool "; + MethodGenerator::writeMethodDeclaration(method, pureVirtual, true, stream); + break; + + case Class::Node: // no async methods + break; + } + } + } + + if (!classData.properties.isEmpty()) + { + bool pureVirtual = true; + bool skip = false; + switch (role) + { + case Class::Interface: + tqWarning("Properties not yet supported for interfaces"); + skip = true; + pureVirtual = true; + break; + + case Class::Proxy: + pureVirtual = false; + stream << "public:" << endl; + stream << " virtual void setDBusProperty(const TQString& name," + << " const TQT_DBusVariant& variant, TQT_DBusError& error);" + << endl; + stream << " virtual TQT_DBusVariant getDBusProperty(const TQString& name, TQT_DBusError& error) const;" << endl; + stream << endl; + break; + + case Class::Node: // no node properties + skip = true; + break; + } + + if (!skip) + { + TQValueList<Property>::const_iterator it = classData.properties.begin(); + TQValueList<Property>::const_iterator endIt = classData.properties.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writePropertyDeclaration(*it, pureVirtual, stream); + } + } + } + + switch (role) + { + case Class::Interface: + if (!classData.methods.isEmpty() || !classData.asyncMethods.isEmpty()) + { + stream << "protected: // implement sending replies" << endl; + stream << " virtual void handleMethodReply(const TQT_DBusMessage& reply) = 0;" << endl; + stream << endl; + stream << "protected: // usually no need to reimplement" << endl; + stream << " virtual bool handleMethodCall(const TQT_DBusMessage& message);" << endl; + } + else + { + stream << "protected: // no methods to handle" << endl; + stream << " virtual bool handleMethodCall(const TQT_DBusMessage&) { return false; }" << endl; + } + break; + + case Class::Proxy: + { + if (!classData.msignals.isEmpty()) + { + stream << "protected slots: // usually no need to reimplement" << endl; + stream << " virtual void slotHandleDBusSignal(const TQT_DBusMessage& message);" << endl; + stream << endl; + } + + if (!classData.asyncReplySignals.isEmpty()) + { + if (classData.msignals.isEmpty()) + { + stream << "protected slots: // usually no need to reimplement" << endl; + } + stream << " virtual void slotHandleAsyncReply(int id, const TQT_DBusMessage& message);" << endl; + stream << endl; + } + + stream << "protected:" << endl; + stream << " TQT_DBusProxy* m_baseProxy;" << endl; + + if (!classData.asyncMethods.isEmpty()) + { + stream << endl; + stream << " TQMap<int, TQString> m_asyncCalls;" << endl; + } + + break; + } + + case Class::Node: // not variable methods + break; + } + + stream << endl; +} + +static void writeSignalDeclarations(const Class& classData, Class::Role role, + TQTextStream& stream) +{ + if (classData.msignals.isEmpty() && classData.asyncReplySignals.isEmpty()) + return; + + TQString prefix; + switch (role) + { + case Class::Interface: + stream << "protected: // implement sending signals" << endl; + stream << " virtual bool handleSignalSend(const TQT_DBusMessage& reply) = 0;" << endl; + stream << " virtual TQString objectPath() const = 0;" << endl; + stream << endl; + stream << "protected: // for sending D-Bus signals" << endl; + prefix = " virtual bool emit"; + break; + + case Class::Proxy: + stream << "signals:" << endl; + stream << " void AsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError error);" << endl << endl; + prefix = " void "; + break; + + case Class::Node: // no signals + break; + } + + TQValueList<Method>::const_iterator it = classData.msignals.begin(); + TQValueList<Method>::const_iterator endIt = classData.msignals.end(); + for (; it != endIt; ++it) + { + stream << prefix; + MethodGenerator::writeMethodDeclaration(*it, false, false, stream); + } + + it = classData.asyncReplySignals.begin(); + endIt = classData.asyncReplySignals.end(); + for (; it != endIt; ++it) + { + stream << prefix; + + Method signal = *it; + signal.name += "AsyncReply"; + + MethodGenerator::writeMethodDeclaration(signal, false, false, stream); + } + + stream << endl; +} + +static void writeSignalEmitters(const Class& classData, TQTextStream& stream) +{ + if (classData.msignals.isEmpty()) return; + + TQValueList<Method>::const_iterator it = classData.msignals.begin(); + TQValueList<Method>::const_iterator endIt = classData.msignals.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writeSignalEmitter(classData, *it, stream); + } + + stream << endl; +} + +static void writeMethodCallDeclarations(const Class& classData, + TQTextStream& stream) +{ + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + stream << " "; + MethodGenerator::writeMethodCallDeclaration(*it, stream); + } + + if (!classData.asyncReplyMethods.isEmpty()) + { + stream << "protected:" << endl; + stream << " TQMap<int, TQT_DBusMessage> m_asyncCalls;" << endl; + stream << endl; + } +} + +static void writeInterfaceAsyncReplyHandlers(const Class& classData, + TQTextStream& stream) +{ + if (classData.asyncReplyMethods.isEmpty()) return; + + TQValueList<Method>::const_iterator it = classData.asyncReplyMethods.begin(); + TQValueList<Method>::const_iterator endIt = classData.asyncReplyMethods.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writeInterfaceAsyncReplyHandler(classData, *it, stream); + } +} + +static void writeMethodCalls(const Class& classData, TQTextStream& stream) +{ + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if ((*it).async) continue; + + MethodGenerator::writeMethodCall(classData, *it, stream); + } + + it = classData.asyncMethods.begin(); + endIt = classData.asyncMethods.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writeMethodCall(classData, *it, stream); + } +} + +static void writeProxyMethods(const Class& classData, TQTextStream& stream) +{ + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if ((*it).async) continue; + + MethodGenerator::writeProxyMethod(classData.name, *it, stream); + } + + it = classData.asyncMethods.begin(); + endIt = classData.asyncMethods.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writeProxyMethod(classData.name, *it, stream); + } +} + +static void writeProxyProperties(const Class& classData, TQTextStream& stream) +{ + if (classData.properties.isEmpty()) return; + + MethodGenerator::writeProxyGenericProperty(classData, stream); + + TQValueList<Property>::const_iterator it = classData.properties.begin(); + TQValueList<Property>::const_iterator endIt = classData.properties.end(); + for (; it != endIt; ++it) + { + MethodGenerator::writeProxyProperty(classData, *it, stream); + } +} + +static void splitAsyncProxyMethods(Class& classData) +{ + // create the async identifier + Argument idArgMethod; + idArgMethod.name = "asyncCallId"; + idArgMethod.signature = "int"; + idArgMethod.isPrimitive = true; + idArgMethod.direction = Argument::Out; + + Argument idArgSignal = idArgMethod; + idArgSignal.direction = Argument::In; + + TQValueList<Method>::iterator it = classData.methods.begin(); + TQValueList<Method>::iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if (!(*it).async) continue; + + Method method = *it; + + TQValueList<Argument> methodArgs; + TQValueList<Argument> signalArgs; + + // add id argument + methodArgs << idArgMethod; + signalArgs << idArgSignal; + + // split in/out arguments: "in" belong to the method, "out" to the new signal + TQValueList<Argument>::const_iterator argIt = method.arguments.begin(); + TQValueList<Argument>::const_iterator argEndIt = method.arguments.end(); + for (; argIt != argEndIt; ++argIt) + { + if ((*argIt).direction == Argument::Out) + { + // signal parameters are "out" but have "in" signature, + // e.g. "const T&" + Argument arg = *argIt; + arg.direction = Argument::In; + + signalArgs << arg; + } + else + methodArgs << *argIt; + } + + // change method + method.arguments = methodArgs; + + classData.asyncMethods << method; + + // create "callback" signal + Method signal = method; + signal.arguments = signalArgs; + + classData.asyncReplySignals << signal; + } +} + +static void splitAsyncInterfaceMethods(Class& classData) +{ + // create the async identifier + Argument idArgMethod; + idArgMethod.name = "asyncCallId"; + idArgMethod.signature = "int"; + idArgMethod.isPrimitive = true; + idArgMethod.direction = Argument::In; + + Argument idArgReply = idArgMethod; + + TQValueList<Method>::iterator it = classData.methods.begin(); + TQValueList<Method>::iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if (!(*it).async) continue; + + Method method = *it; + + TQValueList<Argument> methodArgs; + TQValueList<Argument> replyArgs; + + // add id argument + methodArgs << idArgMethod; + replyArgs << idArgReply; + + // split in/out arguments: "in" belong to the call, "out" to the reply + TQValueList<Argument>::const_iterator argIt = method.arguments.begin(); + TQValueList<Argument>::const_iterator argEndIt = method.arguments.end(); + for (; argIt != argEndIt; ++argIt) + { + if ((*argIt).direction == Argument::Out) + { + // reply parameters are "out" for the service but "in" for + // the reply handler + Argument arg = *argIt; + arg.direction = Argument::In; + + replyArgs << arg; + } + else + methodArgs << *argIt; + } + + // change method + method.arguments = methodArgs; + + classData.asyncMethods << method; + + // create reply handler + Method reply = method; + reply.arguments = replyArgs; + + classData.asyncReplyMethods << reply; + } +} + +bool ClassGenerator::initStreams(const TQString& baseName, + TQTextStream& headerStream, + TQTextStream& sourceStream) +{ + TQFile* headerFile = new TQFile(baseName + ".h"); + TQFile* sourceFile = new TQFile(baseName + ".cpp"); + + if (!headerFile->open(IO_WriteOnly) || !sourceFile->open(IO_WriteOnly)) + { + delete headerFile; + delete sourceFile; + + return false; + } + + headerStream.setDevice(TQT_TQIODEVICE(headerFile)); + sourceStream.setDevice(TQT_TQIODEVICE(sourceFile)); + + // create header + writeFileHeader(headerStream); + openIncludeGuard(baseName, headerStream); + + // create source + writeFileHeader(sourceStream); + sourceStream << "// declaration include" << endl; + sourceStream << "#include \"" << baseName << ".h\"" << endl; + sourceStream << endl; + + return true; +} + +bool ClassGenerator::finishStreams(const TQString& baseName, + TQTextStream& headerStream, + TQTextStream& sourceStream) +{ + closeIncludeGuard(baseName, headerStream); + writeFileFooter(headerStream); + writeFileFooter(sourceStream); + + TQIODevice* device = headerStream.device(); + headerStream.unsetDevice(); + delete device; + + device = sourceStream.device(); + sourceStream.unsetDevice(); + delete device; + + return true; +} + +bool ClassGenerator::extractClass(const TQDomElement& interfaceElement, + Class& classData) +{ + tqDebug("ClassGenerator: processing interface '%s'", + interfaceElement.attribute("name").latin1()); + + classData.dbusName = interfaceElement.attribute("name"); + + TQStringList nameParts = TQStringList::split('.', classData.dbusName); + + if (nameParts.count() < 2) return false; + + classData.name = nameParts.back(); + nameParts.pop_back(); + classData.namespaces = nameParts; + + return MethodGenerator::extractMethods(interfaceElement, classData); +} + +bool ClassGenerator::generateInterface(const Class& classData, + TQTextStream& headerStream, + TQTextStream& sourceStream) +{ + Class classDataCopy = classData; + splitAsyncInterfaceMethods(classDataCopy); + + // create header + writeHeaderIncludes(classDataCopy, Class::Interface, headerStream); + + openNamespaces(classDataCopy.namespaces, headerStream); + openClassDeclaration(classDataCopy, Class::Interface, headerStream); + + writeSignalDeclarations(classDataCopy, Class::Interface, headerStream); + writeMethodDeclarations(classDataCopy, Class::Interface, headerStream); + writeMethodCallDeclarations(classDataCopy, headerStream); + + closeClassDeclaration(classDataCopy, Class::Interface, headerStream); + closeNamespaces(classDataCopy.namespaces, headerStream); + + // create source + writeSourceIncludes(classDataCopy, Class::Interface, sourceStream); + + openNamespaces(classDataCopy.namespaces, sourceStream); + + MethodGenerator::writeIntrospectionDataMethod(classDataCopy, sourceStream); + + writeSignalEmitters(classDataCopy, sourceStream); + writeInterfaceAsyncReplyHandlers(classDataCopy, sourceStream); + writeMethodCalls(classDataCopy, sourceStream); + + MethodGenerator::writeInterfaceMainMethod(classDataCopy, sourceStream); + + closeNamespaces(classDataCopy.namespaces, sourceStream); + + return true; +} + +bool ClassGenerator::generateProxy(const Class& classData, + TQTextStream& headerStream, + TQTextStream& sourceStream) +{ + Class classDataCopy = classData; + splitAsyncProxyMethods(classDataCopy); + + // create header + writeHeaderIncludes(classDataCopy, Class::Proxy, headerStream); + + openNamespaces(classDataCopy.namespaces, headerStream); + openClassDeclaration(classDataCopy, Class::Proxy, headerStream); + + writeSignalDeclarations(classDataCopy, Class::Proxy, headerStream); + writeMethodDeclarations(classDataCopy, Class::Proxy, headerStream); + + closeClassDeclaration(classDataCopy, Class::Proxy, headerStream); + closeNamespaces(classDataCopy.namespaces, headerStream); + + // create source + writeSourceIncludes(classDataCopy, Class::Proxy, sourceStream); + + openNamespaces(classDataCopy.namespaces, sourceStream); + + MethodGenerator::writeProxyBegin(classDataCopy, sourceStream); + + writeProxyMethods(classDataCopy, sourceStream); + + writeProxyProperties(classDataCopy, sourceStream); + + if (!classDataCopy.msignals.isEmpty()) + MethodGenerator::writeSignalHandler(classDataCopy, sourceStream); + + if (!classDataCopy.asyncReplySignals.isEmpty()) + MethodGenerator::writeProxyAsyncReplyHandler(classDataCopy, sourceStream); + + closeNamespaces(classDataCopy.namespaces, sourceStream); + + return true; +} + +bool ClassGenerator::generateNode(const Class& classData, + const TQValueList<Class>& interfaces, + TQTextStream& headerStream, + TQTextStream& sourceStream) +{ + // create header + writeHeaderIncludes(classData, Class::Node, headerStream); + + openNamespaces(classData.namespaces, headerStream); + openClassDeclaration(classData, Class::Node, headerStream); + + closeClassDeclaration(classData, Class::Node, headerStream); + closeNamespaces(classData.namespaces, headerStream); + + // create source + writeSourceIncludes(classData, Class::Node, sourceStream); + writeInterfaceIncludes(interfaces, sourceStream); + + openNamespaces(classData.namespaces, sourceStream); + + MethodGenerator::writeNodePrivate(classData, sourceStream); + + MethodGenerator::writeNodeBegin(classData, sourceStream); + + MethodGenerator::writeNodeMethods(classData, interfaces, sourceStream); + + closeNamespaces(classData.namespaces, sourceStream); + + return true; +} + +// End of File diff --git a/src/tools/dbusxml2qt3/classgen.h b/src/tools/dbusxml2qt3/classgen.h new file mode 100644 index 0000000..3597890 --- /dev/null +++ b/src/tools/dbusxml2qt3/classgen.h @@ -0,0 +1,54 @@ +/* +* Copyright (C) 2007 Kevin Krammer <[email protected]> +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +#if !defined(CLASSGEN_H_INCLUDED) +#define CLASSGEN_H_INCLUDED + +// forward declarations +class Class; +class TQDomElement; +class TQTextStream; +template<typename T> class TQValueList; + +class ClassGenerator +{ +public: + static bool initStreams(const TQString& baseName, + TQTextStream& headerStream, TQTextStream& sourceStream); + + static bool finishStreams(const TQString& baseName, + TQTextStream& headerStream, TQTextStream& sourceStream); + + static bool extractClass(const TQDomElement& interfaceElement, Class& classData); + static bool generateInterface(const Class& classData, + TQTextStream& headerStream, + TQTextStream& sourceStream); + static bool generateProxy(const Class& classData, + TQTextStream& headerStream, TQTextStream& sourceStream); + static bool generateNode(const Class& classData, + const TQValueList<Class>& interfaces, + TQTextStream& headerStream, TQTextStream& sourceStream); +}; + +#endif + +// End of File diff --git a/src/tools/dbusxml2qt3/main.cpp b/src/tools/dbusxml2qt3/main.cpp new file mode 100644 index 0000000..0208072 --- /dev/null +++ b/src/tools/dbusxml2qt3/main.cpp @@ -0,0 +1,637 @@ +/* +* Copyright (C) 2007 Kevin Krammer <[email protected]> +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +// standard includes +#include <iostream> +#include <cstdlib> + +// TQt includes +#include <tqdom.h> +#include <tqfile.h> +#include <tqmap.h> +#include <tqtextstream.h> + +// local includes +#include "classgen.h" +#include "methodgen.h" + +typedef TQMap<TQString, TQString> OptionMap; + +void usage(); + +OptionMap parseOptions(int argc, char** argv); + +bool checkForOption(const OptionMap& options, const TQString& option) +{ + return options.find(option) != options.end(); +} + +int main(int argc, char** argv) +{ + const OptionMap options = parseOptions(argc, argv); + + if (!checkForOption(options, "filename")) + { + std::cerr << "dbusxml2qt3: introspection data file missing" << std::endl; + usage(); + exit(1); + } + + TQString fileName = options["filename"]; + TQFile file(fileName); + if (!file.exists()) + { + std::cerr << "dbusxml2qt3: introspection data file '" + << fileName.local8Bit().data() + << "' does not exist" << std::endl; + exit(2); + } + + if (!file.open(IO_ReadOnly)) + { + std::cerr << "dbusxml2qt3: introspection data file '" + << fileName.local8Bit().data() + << "' cannot be read" << std::endl; + exit(2); + } + + TQDomDocument document; + + if (!document.setContent(&file)) + { + file.close(); + + std::cerr << "dbusxml2qt3: introspection data file '" + << fileName.local8Bit().data() + << "' cannot be parsed" << std::endl; + exit(2); + } + + file.close(); + + TQDomElement rootElement = document.documentElement(); + if (rootElement.isNull() || rootElement.tagName() != "node") + { + std::cerr << "dbusxml2qt3: introspection data file '" + << fileName.local8Bit().data() + << "' does not have a 'node' element as its root node" + << std::endl; + exit(2); + } + + TQValueList<Class> interfaces; + bool hasIntrospectable = false; + + TQDomNode child = rootElement.firstChild(); + for (; !child.isNull(); child = child.nextSibling()) + { + if (!child.isElement()) continue; + + TQDomElement element = child.toElement(); + + if (element.tagName() == "interface") + { + if (!element.attribute("name").isEmpty()) + { + Class classData; + if (ClassGenerator::extractClass(element, classData)) + { + if (classData.dbusName == "org.freedesktop.DBus.Introspectable") + hasIntrospectable = true; + else + interfaces << classData; + } + } + } + } + + if (interfaces.isEmpty()) + { + std::cerr << "dbusxml2qt3: introspection data file '" + << fileName.local8Bit().data() + << "' does not contain any valid interface descriptions" + << std::endl; + exit(3); + } + + bool generateProxies = checkForOption(options, "proxy"); + bool generateInterfaces = checkForOption(options, "interface"); + bool generateNode = checkForOption(options, "node"); + + // if no specific option is selected, we generate everything + bool generateAll = !(generateProxies || generateInterfaces || generateNode); + + if (checkForOption(options, "classname")) + { + // class name only useful for single interfaces or just node + if (interfaces.count() > 1 && (generateAll || generateInterfaces || generateProxies)) + { + std::cerr << "dbusxml2qt3: class name option specified but " + << "introspection data file '" + << fileName.local8Bit().data() + << "' contains more than one interface description" + << std::endl; + exit(3); + } + + // class name for node is handled differently later on + if (!generateNode) + { + TQStringList nameParts = TQStringList::split("::", options["classname"]); + + interfaces[0].name = nameParts.back(); + + nameParts.pop_back(); + interfaces[0].namespaces = nameParts; + } + } + + if (checkForOption(options, "namespace")) + { + TQStringList nameParts = TQStringList::split("::", options["namespace"]); + + TQValueList<Class>::iterator it = interfaces.begin(); + TQValueList<Class>::iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + (*it).namespaces = nameParts; + } + } + + if (generateInterfaces || generateAll) + { + TQTextStream headerStream; + TQTextStream sourceStream; + + TQString baseName = options["interface"]; + if (!baseName.isEmpty()) + { + if (!ClassGenerator::initStreams(baseName, headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: proxy files, using base name '" + << baseName.local8Bit().data() + << "', could not be opened for writing" + << std::endl; + exit(4); + } + } + + TQValueList<Class>::const_iterator it = interfaces.begin(); + TQValueList<Class>::const_iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + if (baseName.isEmpty()) + { + if (!ClassGenerator::initStreams((*it).name.lower() + "interface", + headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: interface files, using base name '" + << baseName.local8Bit().data() + << "', could not be opened for writing" + << std::endl; + exit(4); + } + } + + ClassGenerator::generateInterface(*it, headerStream, sourceStream); + + if (baseName.isEmpty()) + { + ClassGenerator::finishStreams((*it).name.lower() + "interface", + headerStream, sourceStream); + } + } + + if (!baseName.isEmpty()) + ClassGenerator::finishStreams(baseName, headerStream, sourceStream); + } + + if (generateProxies || generateAll) + { + TQTextStream headerStream; + TQTextStream sourceStream; + + TQString baseName = options["proxy"]; + if (!baseName.isEmpty()) + { + if (!ClassGenerator::initStreams(baseName, headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: proxy files, using base name '" + << baseName.local8Bit().data() + << "', could not be opened for writing" + << std::endl; + exit(4); + } + } + + TQValueList<Class>::const_iterator it = interfaces.begin(); + TQValueList<Class>::const_iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + if (baseName.isEmpty()) + { + if (!ClassGenerator::initStreams((*it).name.lower() + "proxy", + headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: proxy files, using base name '" + << baseName.local8Bit().data() + << "', could not be opened for writing" + << std::endl; + exit(4); + } + } + + ClassGenerator::generateProxy(*it, headerStream, sourceStream); + + if (baseName.isEmpty()) + { + ClassGenerator::finishStreams((*it).name.lower() + "proxy", + headerStream, sourceStream); + } + } + + if (!baseName.isEmpty()) + ClassGenerator::finishStreams(baseName, headerStream, sourceStream); + } + + if (generateNode || generateAll) + { + if (!hasIntrospectable) + { + tqDebug("Generating org.freedesktop.DBus.Introspectable on demand"); + + Class classData; + classData.name = "Introspectable"; + classData.dbusName = "org.freedesktop.DBus.Introspectable"; + + classData.namespaces << "org" << "freedesktop" << "DBus"; + + Method method; + method.name = "Introspect"; + method.noReply = false; + method.async = false; + + Argument argument; + argument.name = "data"; + argument.direction = Argument::Out; + argument.signature = "TQString"; + argument.accessor = "String"; + argument.isPrimitive = false; + argument.dbusSignature = "s"; + + argument.forwardDeclarations << "class TQString"; + argument.sourceIncludes["TQt"].append("<tqstring.h>"); + + method.arguments << argument; + classData.methods << method; + + TQTextStream headerStream; + TQTextStream sourceStream; + + if (!ClassGenerator::initStreams(classData.name.lower() + "interface", + headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: interface files, using base name '" + << classData.name.lower().local8Bit().data() << "interface" + << "', could not be opened for writing" + << std::endl; + exit(4); + } + + ClassGenerator::generateInterface(classData, + headerStream, sourceStream); + + ClassGenerator::finishStreams(classData.name.lower() + "interface", + headerStream, sourceStream); + } + + TQString nodeClassName = options["classname"]; + if (nodeClassName.isEmpty()) + { + nodeClassName = rootElement.attribute("name"); + if (nodeClassName.startsWith("/")) nodeClassName = nodeClassName.mid(1); + if (nodeClassName.isEmpty()) + { + std::cerr << "dbusxml2qt3: cannot generate node without class name." + << std::endl; + exit(3); + } + + nodeClassName.replace('/', "_"); + } + + TQStringList nameParts = TQStringList::split("::", nodeClassName); + + Class classData; + classData.name = nameParts.back(); + + nameParts.pop_back(); + classData.namespaces = nameParts; + + if (checkForOption(options, "namespace")) + { + nameParts = TQStringList::split("::", options["namespace"]); + + classData.namespaces = nameParts; + } + + TQTextStream headerStream; + TQTextStream sourceStream; + + TQString baseName = options["node"]; + if (baseName.isEmpty()) baseName = classData.name.lower() + "node"; + + if (!ClassGenerator::initStreams(baseName, headerStream, sourceStream)) + { + std::cerr << "dbusxml2qt3: interface files, using base name '" + << baseName.local8Bit().data() + << "', could not be opened for writing" + << std::endl; + exit(4); + } + + ClassGenerator::generateNode(classData, interfaces, + headerStream, sourceStream); + + ClassGenerator::finishStreams(baseName, headerStream, sourceStream); + } + + return 0; +} + +void usage() +{ + std::cout << "usage: dbusxml2qt3 [options] <introspectionfile>" << std::endl; + std::cout << std::endl; + + std::cout << "Options:" << std::endl; + std::cout << "-h, --help" << std::endl; + std::cout << "\tDisplay this help" << std::endl; + std::cout << std::endl; + + std::cout << "-c <classname>, --class <classname>" << std::endl; + std::cout << "\tUse 'classname' instead of last string in interface name" + << std::endl; + std::cout << std::endl; + + std::cout << "-N [namespace], --namespace [namespace]" << std::endl; + std::cout << "\tOverride namespaces. If provided, use 'namespace' instead, otherwise ignore namespaces" + << std::endl; + std::cout << std::endl; + + std::cout << "-i [basename], --interface [basename]" << std::endl; + std::cout << "\tGenerate interface files. If provided, use 'basename' for filenames" + << std::endl; + std::cout << std::endl; + + std::cout << "-p [basename], --proxy [basename]" << std::endl; + std::cout << "\tGenerate proxy files. If provided, use 'basename' for filenames" + << std::endl; + std::cout << std::endl; + + std::cout << "-n [basename], --node [basename]" << std::endl; + std::cout << "\tGenerate node files. If provided, use 'basename' for filenames" + << std::endl; + std::cout << std::endl; + + std::cout << "Examples:" << std::endl; + std::cout << "dbusxml2qt3 myinterface.xml" << std::endl; + std::cout << "\tGenerates as much as possible, i.e. interfaces, proxies and, " + << "if a node name is specified in 'myinterface.xml', the node files" + << std::endl; + std::cout << "\tUses lowercased interface names as plus type specific suffix " + << "for the file names" << std::endl; + std::cout << std::endl; + + std::cout << "dbusxml2qt3 myinterface.xml -N" << std::endl; + std::cout << "\tSame as first example but does not use namespaces" + << std::endl; + std::cout << std::endl; + + std::cout << "dbusxml2qt3 myinterface.xml -N org::myorg" << std::endl; + std::cout << "\tSame as first example but overrides namespaces with 'org::myorg'" + << std::endl; + std::cout << std::endl; + + std::cout << "dbusxml2qt3 myinterface.xml -n mynode -c MyNode" << std::endl; + std::cout << "\tGenerate only node files, use 'mynode' as the file basename " + << "and classname 'MyClass'" + << std::endl; + std::cout << std::endl; + + std::cout << "dbusxml2qt3 myinterface.xml -p" << std::endl; + std::cout << "\tGenerate only proxy files, use default file basename" + << std::endl; + std::cout << std::endl; + + std::cout << "dbusxml2qt3 myinterface.xml -p myproxy" << std::endl; + std::cout << "\tGenerate only proxy files, use 'myproxy' as the file basename" + << std::endl; + std::cout << std::endl; +} + +bool testAndSetOption(OptionMap& options, const TQString& option, const TQString& value) +{ + OptionMap::iterator it = options.find(option); + if (it == options.end()) + { + options.insert(option, value); + return true; + } + + return false; +} + +OptionMap parseOptions(int argc, char** argv) +{ + TQStringList args; + for (int i = 1; i < argc; ++i) + { + args << TQString::fromLocal8Bit(argv[i]); + } + + OptionMap options; + + while (!args.isEmpty()) + { + TQString arg = args.front(); + args.pop_front(); + + if (arg.startsWith("-")) + { + if (arg.endsWith("help")) + { + usage(); + exit(0); + } + else if (arg == "-p" || arg == "--proxy") + { + // test for optional argument + TQString value; + if (!args.isEmpty() > 0 && !args[0].startsWith("-")) + { + value = args.front(); + args.pop_front(); + } + + if (!testAndSetOption(options, "proxy", value)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() << "'"; + + if (!value.isEmpty()) + std::cerr << ", value '" << value.local8Bit().data() << "':"; + else + std::cerr << ":"; + + std::cerr << " already set to '" + << options["proxy"].local8Bit().data() << std::endl; + } + } + else if (arg == "-i" || arg == "--interface") + { + // test for optional argument + TQString value; + if (!args.isEmpty() > 0 && !args[0].startsWith("-")) + { + value = args.front(); + args.pop_front(); + } + + if (!testAndSetOption(options, "interface", value)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() << "'"; + + if (!value.isEmpty()) + std::cerr << ", value '" << value.local8Bit().data() << "':"; + else + std::cerr << ":"; + + std::cerr << " already set to '" + << options["interface"].local8Bit().data() << std::endl; + } + } + else if (arg == "-n" || arg == "--node") + { + // test for optional argument + TQString value; + if (!args.isEmpty() > 0 && !args[0].startsWith("-")) + { + value = args.front(); + args.pop_front(); + } + + if (!testAndSetOption(options, "node", value)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() << "'"; + + if (!value.isEmpty()) + std::cerr << ", value '" << value.local8Bit().data() << "':"; + else + std::cerr << ":"; + + std::cerr << " already set to '" + << options["node"].local8Bit().data() << std::endl; + } + } + else if (arg == "-N" || arg == "--namespace") + { + // test for optional argument + TQString value; + if (!args.isEmpty() > 0 && !args[0].startsWith("-")) + { + value = args.front(); + args.pop_front(); + } + + if (!testAndSetOption(options, "namespace", value)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() << "'"; + + if (!value.isEmpty()) + std::cerr << ", value '" << value.local8Bit().data() << "':"; + else + std::cerr << ":"; + + std::cerr << " already set to '" + << options["namespace"].local8Bit().data() << std::endl; + } + } + else if (arg == "-c" || arg == "--class") + { + // test for mandatory argument + if (args.isEmpty() || args[0].startsWith("-")) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() + << "': mandatory parameter missing" << std::endl; + usage(); + exit(1); + } + + TQString value = args.front(); + args.pop_front(); + + if (!testAndSetOption(options, "classname", value)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() << "'"; + + if (!value.isEmpty()) + std::cerr << ", value '" << value.local8Bit().data() << "':"; + else + std::cerr << ":"; + + std::cerr << " already set to '" + << options["classname"].local8Bit().data() << std::endl; + } + } + else + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() + << "': unknown option" << std::endl; + usage(); + exit(1); + } + } + else + { + if (!testAndSetOption(options, "filename", arg)) + { + std::cerr << "Error while parsing command line argument '" + << arg.local8Bit().data() + << "': introspection file already given as '" + << options["filename"].local8Bit().data() << std::endl; + usage(); + exit(1); + } + } + } + + return options; +} + +// End of File diff --git a/src/tools/dbusxml2qt3/methodgen.cpp b/src/tools/dbusxml2qt3/methodgen.cpp new file mode 100644 index 0000000..4e407d5 --- /dev/null +++ b/src/tools/dbusxml2qt3/methodgen.cpp @@ -0,0 +1,1793 @@ +/* +* Copyright (C) 2007 Kevin Krammer <[email protected]> +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +// TQt includes +#include <tqdom.h> +#include <tqtextstream.h> + +// local includes +#include "methodgen.h" + +static bool parseDBusSignature(const TQString& signature, Argument& argument) +{ + argument.dbusSignature = signature; + + if (signature.length() == 1) + { + if (signature == "b") + { + argument.signature = "bool"; + argument.accessor = "Bool"; + argument.isPrimitive = true; + } + else if (signature == "y") + { + argument.signature = "TQ_UINT8"; + argument.accessor = "Byte"; + argument.isPrimitive = true; + } + else if (signature == "n") + { + argument.signature = "TQ_INT16"; + argument.accessor = "Int16"; + argument.isPrimitive = true; + } + else if (signature == "q") + { + argument.signature = "TQ_UINT16"; + argument.accessor = "UInt16"; + argument.isPrimitive = true; + } + else if (signature == "i") + { + argument.signature = "TQ_INT32"; + argument.accessor = "Int32"; + argument.isPrimitive = true; + } + else if (signature == "u") + { + argument.signature = "TQ_UINT32"; + argument.accessor = "UInt32"; + argument.isPrimitive = true; + } + else if (signature == "x") + { + argument.signature = "TQ_INT64"; + argument.accessor = "Int64"; + argument.isPrimitive = true; + } + else if (signature == "t") + { + argument.signature = "TQ_UINT64"; + argument.accessor = "UInt64"; + argument.isPrimitive = true; + } + else if (signature == "d") + { + argument.signature = "double"; + argument.accessor = "Double"; + argument.isPrimitive = true; + } + else if (signature == "s") + { + argument.signature = "TQString"; + argument.accessor = "String"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQString"); + argument.sourceIncludes["TQt"].append("<tqstring.h>"); + } + else if (signature == "o") + { + argument.signature = "TQT_DBusObjectPath"; + argument.accessor = "ObjectPath"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQT_DBusObjectPath"); + argument.sourceIncludes["tqdbus"].append("<tqdbusobjectpath.h>"); + } + else if (signature == "h") + { + argument.signature = "TQT_DBusUnixFd"; + argument.accessor = "UnixFd"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQT_DBusUnixFd"); + argument.sourceIncludes["tqdbus"].append("<tqdbusunixfd.h>"); + } + else if (signature == "v") + { + argument.signature = "TQT_DBusVariant"; + argument.accessor = "Variant"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQT_DBusVariant"); + argument.sourceIncludes["tqdbus"].append("<tqdbusvariant.h>"); + } + else + return false; + } + else if (signature.startsWith("a")) + { + if (signature == "as") + { + argument.signature = "TQStringList"; + argument.accessor = "List"; + argument.subAccessor = "TQStringList"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQStringList"); + + argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>"); + argument.sourceIncludes["TQt"].append("<tqstringlist.h>"); + } + else if (signature.startsWith("a{")) + { + int from = signature.find("{"); + int to = signature.findRev("}"); + if (from == -1 || to == -1 || (to - from - 1) < 2) return false; + + TQString dictSignature = signature.mid(from + 1, (to - from - 1)); + + Argument key; + if (!parseDBusSignature(dictSignature.left(1), key)) return false; + + Argument value; + if (parseDBusSignature(dictSignature.mid(1), value)) + { + if (!value.subAccessor.isEmpty()) + { + argument.isPrimitive = false; + argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >"; + argument.signature = "TQT_DBusDataMap< " + key.signature + " >"; + argument.accessor = key.accessor + "KeyMap"; + + argument.forwardDeclarations.append("template <typename K> class TQT_DBusDataMap"); + argument.forwardDeclarations += key.forwardDeclarations; + + argument.sourceIncludes = key.sourceIncludes; + argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>"); + } + else + { + argument.isPrimitive = false; + argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >"; + argument.signature = "TQMap< " + key.signature + + ", " + value.signature + " >"; + argument.accessor = key.accessor + "KeyMap"; + argument.subAccessor = value.accessor + "Map"; + + argument.forwardDeclarations.append("template <typename K, typename V> class TQMap"); + argument.forwardDeclarations += key.forwardDeclarations; + argument.forwardDeclarations += value.forwardDeclarations; + + argument.sourceIncludes = key.sourceIncludes; + argument.sourceIncludes["TQt"].append("<tqmap.h>"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>"); + + TQMap<TQString, TQStringList>::const_iterator it = + value.sourceIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator endIt = + value.sourceIncludes.end(); + for (; it != endIt; ++it) + { + argument.sourceIncludes[it.key()] += it.data(); + } + } + } + else + { + argument.isPrimitive = false; + argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >"; + argument.signature = "TQT_DBusDataMap< " + key.signature + " >"; + argument.accessor = key.accessor + "KeyMap"; + + argument.forwardDeclarations.append("template <typename K> class TQT_DBusDataMap"); + argument.forwardDeclarations += key.forwardDeclarations; + + argument.sourceIncludes = key.sourceIncludes; + argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>"); + } + } + else + { + TQString itemSignature = signature.mid(1); + + Argument item; + if (parseDBusSignature(itemSignature, item) && !itemSignature.startsWith("a")) + { + argument.isPrimitive = false; + argument.signature = "TQValueList< " + item.signature + " >"; + argument.accessor = "List"; + argument.subAccessor = item.accessor + "List"; + argument.containerClass = "TQT_DBusDataList"; + + argument.forwardDeclarations.append("class TQT_DBusDataList"); + argument.forwardDeclarations.append("template <typename T> class TQValueList"); + argument.forwardDeclarations += item.forwardDeclarations; + + argument.sourceIncludes["TQt"].append("<tqvaluelist.h>"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>"); + + TQMap<TQString, TQStringList>::const_iterator it = + item.sourceIncludes.begin(); + TQMap<TQString, TQStringList>::const_iterator endIt = + item.sourceIncludes.end(); + for (; it != endIt; ++it) + { + argument.sourceIncludes[it.key()] += it.data(); + } + } + else + { + argument.signature = "TQT_DBusDataList"; + argument.accessor = "List"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQT_DBusDataList"); + + argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>"); + } + } + } + else + return false; + + return true; +} + +static TQMap<TQString, TQString> extractTypeAnnotations(const TQDomElement& element) +{ + const TQString annotationPrefix = "org.freedesktop.DBus.TQt3.Type."; + + TQMap<TQString, TQString> annotations; + + TQDomNode node = element.firstChild(); + for (uint count = 1; !node.isNull(); node = node.nextSibling(), ++count) + { + if (!node.isElement()) continue; + + TQDomElement element = node.toElement(); + if (element.tagName() != "annotation") continue; + + TQString name = element.attribute("name"); + if (name.isEmpty()) continue; + + TQString value = element.attribute("value").stripWhiteSpace(); + if (value.isEmpty()) continue; + + if (!name.startsWith(annotationPrefix)) continue; + + TQString arg = name.mid(annotationPrefix.length()); + + annotations.insert(arg, value); + } + + return annotations; +} + +static bool hasAnnotation(const TQDomElement& element, const TQString& annotation, TQString* value = 0) +{ + for (TQDomNode node = element.firstChild(); !node.isNull(); + node = node.nextSibling()) + { + if (!node.isElement()) continue; + + TQDomElement childElement = node.toElement(); + if (childElement.tagName() != "annotation") continue; + if (childElement.attribute("name") != annotation) continue; + + if (value != 0) *value = childElement.attribute("value"); + return true; + } + + return false; +} + +static TQValueList<Argument> extractArguments(const TQDomElement& methodElement, + Class& classData) +{ + TQMap<TQString, TQString> argAnnotations = extractTypeAnnotations(methodElement); + + TQValueList<Argument> arguments; + + bool isSignal = methodElement.tagName() == "signal"; + + uint inCount = 0; + uint outCount = 0; + for (TQDomNode node = methodElement.firstChild(); !node.isNull(); + node = node.nextSibling()) + { + if (!node.isElement()) continue; + + TQDomElement element = node.toElement(); + if (element.tagName() != "arg") continue; + if (element.attribute("type").isEmpty()) continue; + + Argument argument; + argument.name = element.attribute("name"); + if (argument.name.isEmpty()) + argument.name = TQString("arg%1").arg(inCount + outCount); + + argument.direction = Argument::In; + if (!isSignal && element.attribute("direction", "in") == "out") + argument.direction = Argument::Out; + + TQString annotation; + if (!isSignal && argument.direction == Argument::In) + { + annotation = argAnnotations[TQString("In%1").arg(inCount)]; + ++inCount; + } + else + { + annotation = argAnnotations[TQString("Out%1").arg(outCount)]; + ++outCount; + } + + if (!annotation.isEmpty()) + { + // just assume nobody uses annotations for primitives + argument.annotatedType = annotation; + argument.signature = annotation; + argument.isPrimitive = false; + argument.dbusSignature = element.attribute("type"); + + TQString includeBase = + TQString("\"%1type%2.h\"").arg(classData.name.lower()); + + argument.headerIncludes["local"].append(includeBase.arg("declarations")); + argument.sourceIncludes["local"].append(includeBase.arg("includes")); + argument.sourceIncludes["tqdbus"].append("<tqdbusdataconverter.h>"); + } + else if (!parseDBusSignature(element.attribute("type"), argument)) + { + argument.signature = "TQT_DBusData"; + argument.isPrimitive = false; + + argument.forwardDeclarations.append("class TQT_DBusData"); + argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>"); + } + + arguments.append(argument); + } + + return arguments; +} + +static void writeVariable(const Argument& argument, uint index, + const TQString& prefix, TQTextStream& stream) +{ + stream << prefix << argument.signature << " _" << argument.name; + if (argument.direction == Argument::In) + { + if (!argument.annotatedType.isEmpty()) + { + stream << ";" << endl; + + // TODO: error handling? + stream << prefix << "TQT_DBusDataConverter::convertFromTQT_DBusData<" + << argument.annotatedType + << TQString(">(message[%1], _").arg(index) + << argument.name << ")"; + } + else if (!argument.accessor.isEmpty()) + { + stream << TQString::fromUtf8(" = message[%1].to").arg(index); + stream << argument.accessor; + + if (!argument.subAccessor.isEmpty()) + { + stream << TQString("().to%1").arg(argument.subAccessor); + } + + stream << "()"; + } + else + stream << TQString::fromUtf8(" = message[%1]").arg(index); + } + + stream << ";" << endl; +} + +static void writeVariables(const TQString& prefix, const Method& method, + TQTextStream& stream) +{ + uint count = 0; + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + writeVariable(*it, count, prefix, stream); + + if ((*it).direction == Argument::In) ++count; + } +} + +static void writeSignalEmit(const Method& signal, TQTextStream& stream) +{ + stream << " emit " << signal.name << "("; + + TQValueList<Argument>::const_iterator it = signal.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = signal.arguments.end(); + for (; it != endIt;) + { + stream << "_" << (*it).name; + + ++it; + if (it != endIt) stream << ", "; + } + + stream << ");" << endl; +} + +static void writeMethodIntrospection(const Method& method, bool& firstArgument, + TQTextStream& stream) +{ + stream << " methodElement.setAttribute(\"name\", \"" + << method.name << "\");" << endl; + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + stream << endl; + if (firstArgument) + { + firstArgument = false; + + stream << " TQDomElement argumentElement = document.createElement(" + << "\"arg\");" << endl; + } + else + { + stream << " argumentElement = document.createElement(" + << "\"arg\");" << endl; + } + + stream << " argumentElement.setAttribute(\"name\", \"" + << (*it).name << "\");" << endl; + + stream << " argumentElement.setAttribute(\"type\", \"" + << (*it).dbusSignature << "\");" << endl; + + stream << " argumentElement.setAttribute(\"direction\", \"" + << ((*it).direction == Argument::In ? "in" : "out") << "\");" + << endl; + + stream << " methodElement.appendChild(argumentElement);" << endl; + } + stream << endl; +} + +static void writeNodeInitialization(const Class& classData, + const TQValueList<Class>& interfaces, TQTextStream& stream) +{ + stream << "bool " << classData.name + << "::registerObject(const TQT_DBusConnection& connection, " + << "const TQString& path)" << endl; + stream << "{" << endl; + stream << " if (path.isEmpty()) return false;" << endl; + stream << endl; + + stream << " if (!m_private->objectPath.isEmpty()) unregisterObject();" + << endl; + stream << endl; + + stream << " m_private->connection = connection;" << endl; + stream << " m_private->objectPath = path;" << endl; + stream << endl; + stream << " if (!m_private->connection.registerObject(path, this))" << endl; + stream << " {" << endl; + stream << " m_private->connection = TQT_DBusConnection();" << endl; + stream << " m_private->objectPath = TQString();" << endl; + stream << endl; + stream << " return false;" << endl; + stream << " }" << endl; + stream << endl; + + stream << " if (m_private->interfaces.isEmpty())" << endl; + stream << " {" << endl; + stream << " TQString name = \"org.freedesktop.DBus.Introspectable\";" + << endl; + stream << " TQT_DBusObjectBase* interface = m_private;" << endl; + stream << " m_private->interfaces.insert(name, interface);" << endl; + + TQValueList<Class>::const_iterator it = interfaces.begin(); + TQValueList<Class>::const_iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + stream << endl; + stream << " name = \"" << (*it).dbusName << "\";" << endl; + stream << " interface = createInterface(name);" << endl; + stream << " Q_ASSERT(interface != 0);" << endl; + stream << " m_private->interfaces.insert(name, interface);" << endl; + } + + stream << " }" << endl; + stream << endl; + stream << " return true;" << endl; + stream << "}" << endl; + stream << endl; +} +static void writeNodeIntrospection(const Class& classData, + const TQValueList<Class>& interfaces, TQTextStream& stream) +{ + stream << "void " << classData.name << "::Private" + << "::cacheIntrospectionData()" << endl; + stream << "{" << endl; + + stream << " TQDomDocument doc;" << endl; + stream << " TQDomElement nodeElement = doc.createElement(\"node\");" << endl; + stream << " TQDomElement interfaceElement = doc.createElement(\"interface\");" + << endl; + stream << " org::freedesktop::DBus::Introspectable" + << "::buildIntrospectionData(interfaceElement);" << endl; + stream << " nodeElement.appendChild(interfaceElement);" << endl; + + TQValueList<Class>::const_iterator it = interfaces.begin(); + TQValueList<Class>::const_iterator endIt = interfaces.end(); + for (; it != endIt; ++it) + { + if ((*it).dbusName == "org.freedesktop.DBus.Introspectable") continue; + + stream << endl; + stream << " interfaceElement = doc.createElement(\"interface\");" + << endl; + stream << " " << (*it).namespaces.join("::") + "::" + (*it).name + << "::buildIntrospectionData(interfaceElement);" << endl; + stream << " nodeElement.appendChild(interfaceElement);" << endl; + } + + stream << endl; + stream << " doc.appendChild(nodeElement);" << endl; + stream << endl; + + stream << " introspectionData = \"<!DOCTYPE node PUBLIC \\\"" + << "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\\\"\\n" + << "\\\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" + << "\\\">\\n\";" << endl; + stream << " introspectionData += doc.toString();" << endl; + stream << "}" << endl; + stream << endl; +} + +bool MethodGenerator::extractMethods(const TQDomElement& interfaceElement, + Class& classData) +{ + TQMap<TQString, TQString> propertyAnnotations = + extractTypeAnnotations(interfaceElement); + + uint propertyCount = 0; + for (TQDomNode node = interfaceElement.firstChild(); !node.isNull(); + node = node.nextSibling()) + { + if (!node.isElement()) continue; + + TQDomElement element = node.toElement(); + if (element.attribute("name").isEmpty()) continue; + + if (element.tagName() == "method" || element.tagName() == "signal") + { + Method method; + method.name = element.attribute("name"); + method.arguments = extractArguments(element, classData); + method.noReply = false; + method.async = false; + + if (element.tagName() == "method") + { + method.async = hasAnnotation(element, "org.freedesktop.DBus.GLib.Async"); + classData.methods.append(method); + if (method.async) { + method.async = false; + classData.methods.append(method); + } + } + else + classData.msignals.append(method); + } + else if (element.tagName() == "property") + { + Property property; + property.name = element.attribute("name"); + property.read = element.attribute("access").find("read") != -1; + property.write = element.attribute("access").find("write") != -1; + + TQString annotation = + propertyAnnotations[TQString("Property%1").arg(propertyCount)]; + + if (!annotation.isEmpty()) + { + property.annotatedType = annotation; + property.signature = annotation; + property.dbusSignature = element.attribute("type"); + property.isPrimitive = false; + + TQString includeBase = + TQString("\"%1type%2.h\"").arg(classData.name.lower()); + + property.headerIncludes["local"].append(includeBase.arg("declarations")); + property.sourceIncludes["local"].append(includeBase.arg("includes")); + property.sourceIncludes["tqdbus"].append("<tqdbusdataconverter.h>"); + } + else if (!parseDBusSignature(element.attribute("type"), property)) + { + property.signature = "TQT_DBusData"; + property.isPrimitive = false; + + property.forwardDeclarations.append("class TQT_DBusData"); + property.sourceIncludes["tqdbus"].append("<tqdbusdata.h>"); + } + + classData.properties.append(property); + ++propertyCount; + } + } + + return !classData.methods.isEmpty() || !classData.msignals.isEmpty() || + !classData.properties.isEmpty(); +} + +void MethodGenerator::writeMethodDeclaration(const Method& method, bool pureVirtual, + bool withError, TQTextStream& stream) +{ + stream << method.name << "("; + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt;) + { + if (!(*it).isPrimitive && (*it).direction == Argument::In) + stream << "const "; + + stream << (*it).signature; + + if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&"; + + stream << " " << (*it).name; + + ++it; + if (it != endIt || withError) stream << ", "; + } + + if (withError) + stream << "TQT_DBusError& error)"; + else + stream << ")"; + + if (pureVirtual) + stream << " = 0;" << endl; + else + stream << ";" << endl; + + stream << endl; +} + +void MethodGenerator::writePropertyDeclaration(const Property& property, + bool pureVirtual, TQTextStream& stream) +{ + if (property.write) + { + stream << " virtual void set" << property.name << "("; + + if (!property.isPrimitive) stream << "const "; + + stream << property.signature; + + if (!property.isPrimitive) stream << "&"; + + stream << " value, TQT_DBusError& error)"; + + if (pureVirtual) + stream << " = 0;" << endl; + else + stream << ";" << endl; + } + + if (property.read) + { + stream << " virtual " << property.signature << " get" + << property.name << "(TQT_DBusError& error) const"; + + if (pureVirtual) + stream << " = 0;" << endl; + else + stream << ";" << endl; + } + + if (property.read || property.write) stream << endl; +} + +void MethodGenerator::writeMethodCallDeclaration(const Method& method, + TQTextStream& stream) +{ + if (method.async) + stream << "void call" << method.name << "Async"; + else + stream << "TQT_DBusMessage call" << method.name; + + stream << "(const TQT_DBusMessage& message);" << endl; + stream << endl; +} + +void MethodGenerator::writeMethodCall(const Class& classData, + const Method& method, TQTextStream& stream) +{ + if (method.async) + stream << "void " << classData.name << "::call" << method.name << "Async"; + else + stream << "TQT_DBusMessage " << classData.name << "::call" << method.name; + + stream << "(const TQT_DBusMessage& message)" << endl; + + stream << "{" << endl; + + if (method.async) + { + // FIXME: using writeVariables by removing asyncCallId argument + Method reducedMethod = method; + reducedMethod.arguments.pop_front(); + + writeVariables(" ", reducedMethod, stream); + } + else + { + stream << " TQT_DBusError error;" << endl; + stream << " TQT_DBusMessage reply;" << endl; + stream << endl; + + writeVariables(" ", method, stream); + } + + stream << endl; + + if (method.async) + { + stream << " int _asyncCallId = 0;" << endl; + stream << " while (m_asyncCalls.find(_asyncCallId) != m_asyncCalls.end())" + << endl; + stream << " {" << endl; + stream << " ++_asyncCallId;" << endl; + stream << " }" << endl; + stream << " m_asyncCalls.insert(_asyncCallId, message);" << endl; + stream << endl; + + stream << " " << method.name << "Async("; + } + else + stream << " if (" << method.name << "("; + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + while (it != endIt) + { + stream << "_" << (*it).name; + + ++it; + if (it != endIt) stream << ", "; + } + + if (method.async) + { + stream << ");" << endl; + stream << endl; + + stream << " return;" << endl; + stream << "}" << endl; + stream << endl; + return; + } + + if (method.arguments.count() > 0) stream << ", "; + stream << "error))" << endl; + + stream << " {" << endl; + stream << " reply = TQT_DBusMessage::methodReply(message);" << endl; + + it = method.arguments.begin(); + for (; it != endIt; ++it) + { + if ((*it).direction == Argument::Out) + { + if (!(*it).annotatedType.isEmpty()) + { + stream << " TQT_DBusData " << (*it).name << "Data;" << endl; + stream << " TQT_DBusDataConverter::convertToTQT_DBusData<" + << (*it).annotatedType << ">(_" + << (*it).name << ", " << (*it).name << "Data);" + << endl; + stream << " reply << " << (*it).name << "Data"; + } + else if (!(*it).accessor.isEmpty()) + { + stream << " reply << TQT_DBusData::from" << (*it).accessor; + if (!(*it).subAccessor.isEmpty()) + { + stream << "(" << (*it).containerClass; + } + + stream << "(_" << (*it).name << ")"; + + if (!(*it).subAccessor.isEmpty()) + { + stream << ")"; + } + } + else + stream << " reply << _" << (*it).name; + + stream << ";" << endl; + } + } + stream << " }" << endl; + stream << " else" << endl; + stream << " {" << endl; + + stream << " if (!error.isValid())" << endl; + stream << " {" << endl; + stream << " tqWarning(\"Call to implementation of "; + + TQStringList::const_iterator nsIt = classData.namespaces.begin(); + TQStringList::const_iterator nsEndIt = classData.namespaces.end(); + for (; nsIt != nsEndIt; ++nsIt) + { + stream << *nsIt << "::"; + } + + stream << classData.name << "::" << method.name; + stream << " returned 'false' but error object is not valid!\");" << endl; + stream << endl; + stream << " error = TQT_DBusError::stdFailed(\""; + + nsIt = classData.namespaces.begin(); + for (; nsIt != nsEndIt; ++nsIt) + { + stream << *nsIt << "."; + } + + stream << classData.name << "." << method.name << " execution failed\");" + << endl; + stream << " }" << endl; + stream << endl; + + stream << " reply = TQT_DBusMessage::methodError(message, error);" << endl; + + stream << " }" << endl; + stream << endl; + stream << " return reply;" << endl; + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeSignalEmitter(const Class& classData, + const Method& method, TQTextStream& stream) +{ + stream << "bool " << classData.name << "::emit" << method.name << "("; + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt;) + { + if (!(*it).isPrimitive && (*it).direction == Argument::In) + stream << "const "; + + stream << (*it).signature; + + if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&"; + + stream << " " << (*it).name; + + ++it; + if (it != endIt) stream << ", "; + } + + stream << ")" << endl; + + stream << "{" << endl; + + // TODO: create error or use enum for return + stream << " TQString path = objectPath();" << endl; + stream << " Q_ASSERT(!path.isEmpty());" << endl; + stream << endl; + + stream << " TQT_DBusMessage message = TQT_DBusMessage::signal(path, \""; + stream << classData.dbusName << "\", \"" << method.name << "\");" << endl; + stream << endl; + + it = method.arguments.begin(); + for (; it != endIt; ++it) + { + if ((*it).direction == Argument::In) + { + if (!(*it).annotatedType.isEmpty()) + { + // TODO: error handling + stream << " TQT_DBusData " << (*it).name << "Data;" << endl; + stream << " if (TQT_DBusDataConverter:convertToTQT_DBusData<" + << (*it).annotatedType << ">(" + << (*it).name << ", " << (*it).name << "Data" + << ") != TQT_DBusDataConverter::Success) return false;" + << endl; + stream << " message << " << (*it).name << "Data"; + } + else if (!(*it).accessor.isEmpty()) + { + stream << " message << TQT_DBusData::from" << (*it).accessor; + if (!(*it).subAccessor.isEmpty()) + { + stream << "(" << (*it).containerClass; + } + + stream << "(" << (*it).name << ")"; + + if (!(*it).subAccessor.isEmpty()) + { + stream << ")"; + } + } + else + stream << " message << " << (*it).name; + + stream << ";" << endl; + } + } + stream << endl; + + stream << " return handleSignalSend(message);" << endl; + + stream << "}" << endl; + stream << endl; +} + + +void MethodGenerator::writeInterfaceAsyncReplyHandler(const Class& classData, + const Method& method, TQTextStream& stream) +{ + stream << "void " << classData.name << "::" << method.name + << "AsyncReply("; + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + while (it != endIt) + { + if (!(*it).isPrimitive && (*it).direction == Argument::In) + stream << "const "; + + stream << (*it).signature; + + if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&"; + + stream << " " << (*it).name; + + ++it; + if (it != endIt) stream << ", "; + } + stream << ")" << endl; + stream << endl; + stream << "{" << endl; + + stream << " TQMap<int, TQT_DBusMessage>::iterator findIt = m_asyncCalls.find(asyncCallId);" << endl; + stream << " if (findIt == m_asyncCalls.end()) return;" << endl; + stream << endl; + + stream << " TQT_DBusMessage call = findIt.data();" << endl; + stream << " m_asyncCalls.erase(findIt);" << endl; + stream << endl; + + stream << " TQT_DBusMessage reply = TQT_DBusMessage::methodReply(call);" + << endl; + + it = method.arguments.begin(); + for (++it; it != endIt; ++it) // skip asyncCallId at beginning + { + if (!(*it).annotatedType.isEmpty()) + { + stream << " TQT_DBusData " << (*it).name << "Data;" << endl; + + // TODO error handling + stream << " if (TQT_DBusDataConverter::convertToTQT_DBusData<" + << (*it).annotatedType << ">(" << (*it).name << ", " + << (*it).name << "Data" + << ") != TQT_DBusDataConverter::Success) return false;" + << endl; + stream << " reply << " << (*it).name << "Data;" << endl; + } + else if (!(*it).accessor.isEmpty()) + { + stream << " reply << TQT_DBusData::from" << (*it).accessor << "("; + + if ((*it).subAccessor.isEmpty()) + stream << (*it).name; + else + stream << (*it).containerClass << "(" << (*it).name << ")"; + + stream << ");" << endl; + } + else + stream << " reply << " << (*it).name << ";" << endl; + } + stream << endl; + + stream << " handleMethodReply(reply);" << endl; + + stream << "}" << endl; + stream << endl; + + stream << "void " << classData.name << "::" << method.name + << "AsyncError(int asyncCallId, const TQT_DBusError& error)"; + stream << endl; + + stream << "{" << endl; + + stream << " TQMap<int, TQT_DBusMessage>::iterator findIt = m_asyncCalls.find(asyncCallId);" << endl; + stream << " if (findIt == m_asyncCalls.end()) return;" << endl; + stream << endl; + + stream << " TQT_DBusMessage call = findIt.data();" << endl; + stream << " m_asyncCalls.erase(findIt);" << endl; + stream << endl; + + stream << " TQT_DBusMessage reply = TQT_DBusMessage::methodError(call, error);" + << endl; + stream << " handleMethodReply(reply);" << endl; + + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeInterfaceMainMethod(const Class& classData, + TQTextStream& stream) +{ + if (classData.methods.isEmpty()) return; + + stream << "bool " << classData.name + << "::handleMethodCall(const TQT_DBusMessage& message)" << endl; + stream << "{" << endl; + + stream << " if (message.interface() != \"" << classData.dbusName + << "\") return false;" << endl; + stream << endl; + + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + stream << " if (message.member() == \"" << (*it).name << "\")" << endl; + stream << " {" << endl; + + if ((*it).async) + { + stream << " call" << (*it).name << "Async(message);" << endl; + stream << endl; + } + else + { + stream << " TQT_DBusMessage reply = call" << (*it).name + << "(message);" << endl; + stream << " handleMethodReply(reply);" << endl; + stream << endl; + } + stream << " return true;" << endl; + stream << " }" << endl; + stream << endl; + } + + stream << " return false; " << endl; + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeSignalHandler(const Class& classData, + TQTextStream& stream) +{ + stream << "void " << classData.name + << "::slotHandleDBusSignal(const TQT_DBusMessage& message)" << endl; + stream << "{" << endl; + + TQValueList<Method>::const_iterator it = classData.msignals.begin(); + TQValueList<Method>::const_iterator endIt = classData.msignals.end(); + bool first = true; + for (; it != endIt; ++it) + { + stream << " "; + + if (!first) + stream << "else "; + else + first = false; + + stream << "if (message.member() == \"" << (*it).name << "\")" << endl; + stream << " {" << endl; + + writeVariables(" ", *it, stream); + stream << endl; + + writeSignalEmit(*it, stream); + + stream << " }" << endl; + } + + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeProxyBegin(const Class& classData, TQTextStream& stream) +{ + stream << classData.name << "::" << classData.name + << "(const TQString& service, const TQString& path, TQObject* parent, const char* name)" << endl; + stream << " : TQObject(parent, name)," << endl; + stream << " m_baseProxy(new TQT_DBusProxy())" << endl; + stream << "{" << endl; + stream << " m_baseProxy->setInterface(\"" + << classData.dbusName << "\");" << endl; + stream << " m_baseProxy->setPath(path);" << endl; + stream << " m_baseProxy->setService(service);" << endl; + stream << endl; + + if (!classData.msignals.isEmpty()) + { + stream << " TQObject::connect(m_baseProxy, " + << "TQT_SIGNAL(dbusSignal(const TQT_DBusMessage&))," << endl; + stream << " this, " + << " TQT_SLOT(slotHandleDBusSignal(const TQT_DBusMessage&)));" + << endl; + } + + if (!classData.asyncReplySignals.isEmpty()) + { + stream << " TQObject::connect(m_baseProxy, " + << "TQT_SIGNAL(asyncReply(int, const TQT_DBusMessage&))," << endl; + stream << " this, " + << " TQT_SLOT(slotHandleAsyncReply(int, const TQT_DBusMessage&)));" + << endl; + } + + stream << "}" << endl; + + stream << endl; + + stream << classData.name << "::~" << classData.name << "()" << endl; + stream << "{" << endl; + stream << " delete m_baseProxy;" << endl; + stream << "}" << endl; + stream << endl; + + stream << "void " << classData.name + << "::setConnection(const TQT_DBusConnection& connection)" << endl; + stream << "{" << endl; + stream << " m_baseProxy->setConnection(connection);" << endl; + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeProxyMethod(const TQString& className, + const Method& method, TQTextStream& stream) +{ + stream << "bool " << className << "::" << method.name + << (method.async ? "Async(" : "("); + + TQValueList<Argument>::const_iterator it = method.arguments.begin(); + TQValueList<Argument>::const_iterator endIt = method.arguments.end(); + for (; it != endIt; ++it) + { + if (!(*it).isPrimitive && (*it).direction == Argument::In) + stream << "const "; + + stream << (*it).signature; + + if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&"; + + stream << " " << (*it).name << ", "; + } + + stream << "TQT_DBusError& error)" << endl; + + stream << "{" << endl; + stream << " TQValueList<TQT_DBusData> parameters;" << endl; + stream << endl; + + uint outCount = 0; + + it = method.arguments.begin(); + for (; it != endIt; ++it) + { + if ((*it).direction == Argument::Out) + { + ++outCount; + continue; + } + + if (!(*it).annotatedType.isEmpty()) + { + stream << " TQT_DBusData " << (*it).name << "Data;" << endl; + + // TODO error handling + stream << " if (TQT_DBusDataConverter::convertToTQT_DBusData<" + << (*it).annotatedType << ">(" << (*it).name << ", " + << (*it).name << "Data" + << ") != TQT_DBusDataConverter::Success) return false;" + << endl; + stream << " parameters << " << (*it).name << "Data;" << endl; + } + else if (!(*it).accessor.isEmpty()) + { + stream << " parameters << TQT_DBusData::from" << (*it).accessor << "("; + + if ((*it).subAccessor.isEmpty()) + stream << (*it).name; + else + stream << (*it).containerClass << "(" << (*it).name << ")"; + + stream << ");" << endl; + } + else + stream << " parameters << " << (*it).name << ";" << endl; + } + + stream << endl; + + if (outCount == 0 && method.noReply) + { + stream << " if (!m_baseProxy->send(\"" << method.name + << "\", parameters))" << endl; + stream << " {" << endl; + stream << " error = m_baseProxy->lastError();" << endl; + stream << " return false;" << endl; + stream << " }" << endl; + stream << " return true;" << endl; + stream << "}" << endl; + stream << endl; + return; + } + + if (method.async) + { + stream << " asyncCallId = m_baseProxy->sendWithAsyncReply(\""; + stream << method.name << "\", parameters);" << endl; + stream << endl; + + stream << " if (asyncCallId != 0) m_asyncCalls[asyncCallId] = \"" + << method.name << "\";" << endl; + stream << endl; + + stream << " error = TQT_DBusError();"; + stream << endl; + + stream << " return (asyncCallId != 0);" << endl; + stream << "}" << endl; + stream << endl; + return; + } + + stream << " TQT_DBusMessage reply = m_baseProxy->sendWithReply(\""; + stream << method.name << "\", parameters, &error);" << endl; + stream << endl; + + stream << " if (reply.type() != TQT_DBusMessage::ReplyMessage) return false;" + << endl; + + if (outCount == 0) + { + stream << " return true;" << endl; + stream << "}" << endl; + stream << endl; + return; + } + + stream << endl; + + // TODO: create error or use enum for return + stream << " if (reply.count() != " << outCount << ") return false;" << endl; + stream << endl; + + bool firstAccessor = true; + bool firstSubAccessor = true; + + it = method.arguments.begin(); + for (; it != endIt; ++it) + { + if ((*it).direction == Argument::In) continue; + + --outCount; + + if (!(*it).annotatedType.isEmpty()) + { + // TODO error handling + stream << " if (TQT_DBusDataConverter::convertFromTQT_DBusData<" + << (*it).annotatedType << ">(reply.front(), " + << (*it).name + << ") != TQT_DBusDataConverter::Success) return false;" + << endl; + } + else if (!(*it).accessor.isEmpty()) + { + if (firstAccessor) + { + stream << " bool ok = false;" << endl; + stream << endl; + firstAccessor = false; + } + + if ((*it).subAccessor.isEmpty()) + { + stream << " " << (*it).name << " = reply.front().to" + << (*it).accessor << "(&ok);" << endl; + } + else + { + if (firstSubAccessor) + { + stream << " bool subOK = false;" << endl; + stream << endl; + firstSubAccessor = false; + } + + stream << " " << (*it).name << " = reply.front().to" + << (*it).accessor << "(&ok).to" << (*it).subAccessor + << "(&subOK);" << endl; + + // TODO: create error or use enum for return + stream << " if (!subOK) return false;" << endl; + } + + // TODO: create error or use enum for return + stream << " if (!ok) return false;" << endl; + } + else + stream << " " << (*it).name << " = reply.front();" << endl; + stream << endl; + + if (outCount > 0) + { + stream << " reply.pop_front();" << endl; + stream << endl; + } + } + + stream << " return true;" << endl; + + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeProxyGenericProperty(const Class& classData, + TQTextStream& stream) +{ + stream << "void " << classData.name + << "::setDBusProperty(const TQString& name, " + << "const TQT_DBusVariant& value, TQT_DBusError& error)" + << endl; + stream << "{" << endl; + + stream << " TQT_DBusConnection connection = m_baseProxy->connection();" << endl; + stream << endl; + stream << " TQT_DBusMessage message = TQT_DBusMessage::methodCall(" + << "m_baseProxy->service(), m_baseProxy->path(), " + << "\"org.freedesktop.DBus.Properties\", \"Set\");" << endl; + stream << endl; + + stream << " message << TQT_DBusData::fromString(m_baseProxy->interface());" + << endl; + stream << " message << TQT_DBusData::fromString(name);" << endl; + stream << " message << TQT_DBusData::fromVariant(value);" << endl; + stream << endl; + + stream << " connection.sendWithReply(message, &error);" << endl; + + stream << "}" << endl; + + stream << endl; + + stream << "TQT_DBusVariant " << classData.name + << "::getDBusProperty(const TQString& name, TQT_DBusError& error) const" + << endl; + stream << "{" << endl; + + stream << " TQT_DBusConnection connection = m_baseProxy->connection();" << endl; + stream << endl; + + stream << " TQT_DBusMessage message = TQT_DBusMessage::methodCall(" + << "m_baseProxy->service(), m_baseProxy->path(), " + << "\"org.freedesktop.DBus.Properties\", \"Get\");" << endl; + stream << endl; + + stream << " message << TQT_DBusData::fromString(m_baseProxy->interface());" + << endl; + stream << " message << TQT_DBusData::fromString(name);" << endl; + stream << endl; + + stream << " TQT_DBusMessage reply = connection.sendWithReply(message, &error);" + << endl; + stream << endl; + + stream << " if (reply.type() != TQT_DBusMessage::ReplyMessage)" + << " return TQT_DBusVariant();" << endl; + stream << " if (reply.count() != 1) return TQT_DBusVariant();" << endl; + + stream << endl; + + stream << " bool ok = false;" << endl; + stream << " TQT_DBusVariant value = reply.front().toVariant(&ok);" << endl; + + // TODO generate error + stream << " if (!ok) return TQT_DBusVariant();" << endl; + stream << endl; + + stream << " return value;" << endl; + + stream << "}" << endl; + + stream << endl; +} + +void MethodGenerator::writeProxyProperty(const Class& classData, + const Property& property, TQTextStream& stream) +{ + if (property.write) + { + stream << "void " << classData.name << "::set" << property.name << "("; + + if (!property.isPrimitive) stream << "const "; + + stream << property.signature; + + if (!property.isPrimitive) stream << "&"; + + stream << " value, TQT_DBusError& error)" << endl; + stream << "{" << endl; + stream << " TQT_DBusVariant variant;" << endl; + + if (!property.annotatedType.isEmpty()) + { + // TODO: error handling + stream << " TQT_DBusDataConverter::convertToTQT_DBusData<" + << property.annotatedType << ">(value, variant.value);" + << endl; + } + else if (!property.accessor.isEmpty()) + { + stream << " variant.value = TQT_DBusData::from" + << property.accessor << "("; + + if (property.subAccessor.isEmpty()) + stream << "value"; + else + stream << property.containerClass << "(value)"; + + stream << ");" << endl; + } + else + stream << " variant.value = TQT_DBusData(value);" << endl; + + stream << " variant.signature = \"" << property.dbusSignature << "\";" + << endl; + + stream << endl; + stream << " setDBusProperty(\"" << property.name + << "\", variant, error);" << endl; + + stream << "}" << endl; + stream << endl; + } + + if (property.read) + { + stream << property.signature << " " << classData.name + << "::get" << property.name << "(TQT_DBusError& error) const" << endl; + stream << "{" << endl; + + stream << " TQT_DBusVariant variant = getDBusProperty(\"" + << property.name << "\", error);" << endl; + stream << endl; + stream << " if (error.isValid()) return " + << property.signature << "();" << endl; + stream << endl; + + if (!property.annotatedType.isEmpty()) + { + stream << " " << property.signature << " result;" << endl; + + // TODO error handling + stream << " TQT_DBusDataConverter::convertFromTQT_DBusData<" + << property.annotatedType << ">(variant.value, result);" + << endl; + } + else if (!property.accessor.isEmpty()) + { + stream << " bool ok = false;" << endl; + stream << endl; + + if (property.subAccessor.isEmpty()) + { + stream << " " << property.signature << " result = "; + stream << " variant.value.to" << property.accessor + << "(&ok);" << endl; + } + else + { + stream << " bool subOK = false;" << endl; + stream << endl; + + stream << " " << property.signature << " result = "; + stream << " variant.value.to" + << property.accessor << "(&ok).to" << property.subAccessor + << "(&subOK);" << endl; + + // TODO: create error + stream << " if (!subOK) {}" << endl; + } + + // TODO: create error + stream << " if (!ok) {}" << endl; + } + else + stream << " " << property.signature << " result = variant.value;"; + stream << endl; + + stream << " return result;" << endl; + stream << "}" << endl; + stream << endl; + } +} + +void MethodGenerator::writeProxyAsyncReplyHandler(const Class& classData, + TQTextStream& stream) +{ + stream << "void " << classData.name + << "::slotHandleAsyncReply(int asyncCallId, const TQT_DBusMessage& message)" << endl; + stream << "{" << endl; + + stream << " TQMap<int, TQString>::iterator findIt = " + << "m_asyncCalls.find(asyncCallId);" << endl; + stream << " if (findIt == m_asyncCalls.end()) return;" << endl; + stream << endl; + stream << " const TQString signalName = findIt.data();" << endl; + stream << " m_asyncCalls.erase(findIt);" << endl; + stream << endl; + + TQValueList<Method>::const_iterator it = classData.asyncReplySignals.begin(); + TQValueList<Method>::const_iterator endIt = classData.asyncReplySignals.end(); + bool first = true; + for (; it != endIt; ++it) + { + stream << " "; + + if (!first) + stream << "else "; + else + first = false; + + stream << "if (signalName == \"" << (*it).name << "\")" << endl; + stream << " {" << endl; + + // FIXME tricking writeVariables and writeSignalEmit into writing + // the reply emit code by manipulating arguments and name + stream << " int _asyncCallId = asyncCallId;" << endl << endl; + + stream << " if (message.type() == TQT_DBusMessage::ErrorMessage) {" << endl; + stream << " emit AsyncErrorResponseDetected(_asyncCallId, message.error());" << endl; + stream << " }" << endl << endl; + + Method signal = *it; + signal.arguments.pop_front(); + + writeVariables(" ", signal, stream); + stream << endl; + + signal = *it; + signal.name += "AsyncReply"; + + writeSignalEmit(signal, stream); + + stream << " }" << endl; + } + + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeIntrospectionDataMethod(const Class& classData, + TQTextStream& stream) +{ + stream << "void " << classData.name + << "::buildIntrospectionData(TQDomElement& interfaceElement)" << endl; + stream << "{" << endl; + + stream << " interfaceElement.setAttribute(\"name\", \"" + << classData.dbusName << "\");" << endl; + stream << endl; + + bool firstMethod = true; + bool firstArgument = true; + + TQValueList<Method>::const_iterator it = classData.methods.begin(); + TQValueList<Method>::const_iterator endIt = classData.methods.end(); + for (; it != endIt; ++it) + { + if (firstMethod) + { + firstMethod = false; + stream << " TQDomDocument document = interfaceElement.ownerDocument();" << endl; + stream << " TQDomElement methodElement = document.createElement(" + << "\"method\");" << endl; + } + else + { + stream << endl; + stream << " methodElement = document.createElement(" + << "\"method\");" << endl; + } + + writeMethodIntrospection(*it, firstArgument, stream); + + stream << " interfaceElement.appendChild(methodElement);" << endl; + } + + it = classData.msignals.begin(); + endIt = classData.msignals.end(); + for (; it != endIt; ++it) + { + if (firstMethod) + { + firstMethod = false; + stream << " TQDomDocument document = interfaceElement.ownerDocument();" << endl; + stream << endl; + stream << " TQDomElement methodElement = document.createElement(" + << "\"signal\");" << endl; + } + else + { + stream << endl; + stream << " methodElement = document.createElement(" + << "\"signal\");" << endl; + } + + writeMethodIntrospection(*it, firstArgument, stream); + + stream << " interfaceElement.appendChild(methodElement);" << endl; + } + + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeNodePrivate(const Class& classData, TQTextStream& stream) +{ + stream << "class " << classData.name + << "::Private : public org::freedesktop::DBus::Introspectable" << endl; + stream << "{" << endl; + stream << "public:" << endl; + stream << " virtual ~Private();" << endl; + stream << endl; + + stream << "public:" << endl; + stream << " TQMap<TQString, TQT_DBusObjectBase*> interfaces;" << endl; + stream << " TQString introspectionData;" << endl; + stream << endl; + stream << " TQT_DBusConnection connection;" << endl; + stream << " TQString objectPath;" << endl; + stream << endl; + + stream << "protected:" << endl; + stream << " virtual bool Introspect(TQString& data, TQT_DBusError& error);" + << endl; + stream << endl; + stream << " virtual void handleMethodReply(const TQT_DBusMessage& reply);" + << endl; + + stream << "private:" << endl; + stream << " void cacheIntrospectionData();" << endl; + + stream << "};" << endl; + stream << endl; +} + +void MethodGenerator::writeNodeBegin(const Class& classData, TQTextStream& stream) +{ + stream << classData.name << "::" << classData.name + << "() : TQT_DBusObjectBase()," << endl; + stream << " m_private(new Private())" << endl; + stream << "{" << endl; + stream << "}" << endl; + stream << endl; + + stream << classData.name << "::~" << classData.name << "()" << endl; + stream << "{" << endl; + stream << " unregisterObject();" << endl; + stream << endl; + stream << " delete m_private;" << endl; + stream << "}" << endl; + stream << endl; +} + +void MethodGenerator::writeNodeMethods(const Class& classData, + const TQValueList<Class>& interfaces, TQTextStream& stream) +{ + writeNodeInitialization(classData, interfaces, stream); + + stream << "void " << classData.name << "::unregisterObject()" << endl; + stream << "{" << endl; + stream << " if (m_private->objectPath.isEmpty()) return;" << endl; + stream << endl; + stream << " m_private->connection.unregisterObject(m_private->objectPath);" << endl; + stream << endl; + stream << " m_private->connection = TQT_DBusConnection();" << endl; + stream << " m_private->objectPath = TQString();" << endl; + stream << "}" << endl; + stream << endl; + + stream << "bool " << classData.name + << "::handleMethodCall(const TQT_DBusMessage& message)" << endl; + stream << "{" << endl; + stream << " TQMap<TQString, TQT_DBusObjectBase*>::iterator findIt = " + << "m_private->interfaces.find(message.interface());" << endl; + stream << " if (findIt == m_private->interfaces.end()) return false;" + << endl; + stream << endl; + stream << " return delegateMethodCall(message, findIt.data());" << endl; + stream << "}" << endl; + stream << endl; + + stream << classData.name << "::Private::~Private()" << endl; + stream << "{" << endl; + stream << " TQMap<TQString, TQT_DBusObjectBase*>::const_iterator it = " + << "interfaces.begin();" << endl; + stream << " TQMap<TQString, TQT_DBusObjectBase*>::const_iterator endIt = " + << "interfaces.end();" << endl; + stream << " for (; it != endIt; ++it)" << endl; + stream << " {" << endl; + stream << " TQT_DBusObjectBase* interface = it.data();" << endl; + stream << " if (interface != this)" << endl; + stream << " delete interface;" << endl; + stream << " }" << endl; + stream << " interfaces.clear();" << endl; + stream << "}" << endl; + stream << endl; + + stream << "bool " << classData.name << "::Private" + << "::Introspect(TQString& data, TQT_DBusError& error)" << endl; + stream << "{" << endl; + stream << " Q_UNUSED(error);" << endl; + stream << " if (introspectionData.isEmpty()) cacheIntrospectionData();" + << endl; + stream << endl; + stream << " data = introspectionData;" << endl; + stream << endl; + stream << " return true;" << endl; + stream << "}" << endl; + stream << endl; + + stream << "void " << classData.name << "::Private" + << "::handleMethodReply(const TQT_DBusMessage& reply)" << endl; + stream << "{" << endl; + stream << " connection.send(reply);" << endl; + stream << "}" << endl; + stream << endl; + + writeNodeIntrospection(classData, interfaces, stream); +} + +// End of File diff --git a/src/tools/dbusxml2qt3/methodgen.h b/src/tools/dbusxml2qt3/methodgen.h new file mode 100644 index 0000000..9954fb2 --- /dev/null +++ b/src/tools/dbusxml2qt3/methodgen.h @@ -0,0 +1,153 @@ +/* +* Copyright (C) 2007 Kevin Krammer <[email protected]> +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +#if !defined(METHODGEN_H_INCLUDED) +#define METHODGEN_H_INCLUDED + +// TQt includes +#include <tqmap.h> +#include <tqstringlist.h> + +// forward declarations +class TQTextStream; + +class Argument +{ +public: + enum Direction + { + In, + Out + }; + + TQString name; + TQString annotatedType; + TQString signature; + TQString accessor; + TQString subAccessor; + TQString containerClass; + Direction direction; + bool isPrimitive; + + TQStringList forwardDeclarations; + TQMap<TQString, TQStringList> headerIncludes; + TQMap<TQString, TQStringList> sourceIncludes; + + TQString dbusSignature; +}; + +class Method +{ +public: + TQString name; + TQValueList<Argument> arguments; + bool noReply; + bool async; +}; + +class Property : public Argument +{ +public: + bool read; + bool write; +}; + +class Class +{ +public: + enum Role + { + Interface, + Proxy, + Node + }; + + TQString name; + TQString dbusName; + TQStringList namespaces; + TQValueList<Method> methods; + TQValueList<Method> msignals; + TQValueList<Property> properties; + + TQValueList<Method> asyncMethods; + TQValueList<Method> asyncReplySignals; + TQValueList<Method> asyncReplyMethods; +}; + +class MethodGenerator +{ +public: + static bool extractMethods(const TQDomElement& interfaceElement, + Class& classData); + + static void writeMethodDeclaration(const Method& method, bool pureVirtual, + bool withError, TQTextStream& stream); + + static void writePropertyDeclaration(const Property& property, bool pureVirtual, + TQTextStream& stream); + + static void writeMethodCallDeclaration(const Method& method, + TQTextStream& stream); + + static void writeMethodCall(const Class& classData, const Method& method, + TQTextStream& stream); + + static void writeSignalEmitter(const Class& classData, const Method& method, + TQTextStream& stream); + + static void writeInterfaceAsyncReplyHandler(const Class& classData, + const Method& method, + TQTextStream& stream); + + static void writeInterfaceMainMethod(const Class& classData, + TQTextStream& stream); + + static void writeSignalHandler(const Class& classData, TQTextStream& stream); + + static void writeProxyBegin(const Class& classData, TQTextStream& stream); + + static void writeProxyMethod(const TQString& className, const Method& method, + TQTextStream& stream); + + static void writeProxyGenericProperty(const Class& classData, + TQTextStream& stream); + + static void writeProxyProperty(const Class& classData, const Property& property, + TQTextStream& stream); + + static void writeProxyAsyncReplyHandler(const Class& classData, + TQTextStream& stream); + + static void writeIntrospectionDataMethod(const Class& classData, + TQTextStream& stream); + + static void writeNodePrivate(const Class& classData, TQTextStream& stream); + + static void writeNodeBegin(const Class& classData, TQTextStream& stream); + + static void writeNodeMethods(const Class& classData, + const TQValueList<Class>& interfaces, TQTextStream& stream); +}; + +#endif + +// End of File |