You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dbus-1-tqt/tools/dbusxml2qt3/main.cpp

638 lines
22 KiB

/*
* Copyright (C) 2007 Kevin Krammer <kevin.krammer@gmx.at>
*
* 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)
{
qDebug("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