/* * Copyright (C) 2007 Kevin Krammer * * 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 #include // TQt includes #include #include #include #include // local includes #include "classgen.h" #include "methodgen.h" typedef TQMap OptionMap; void usage(); OptionMap parseOptions(int argc, char** argv); bool checkForOption(const OptionMap& options, const TQString& option) { return options.tqfind(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 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::iterator it = interfaces.begin(); TQValueList::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::const_iterator it = interfaces.begin(); TQValueList::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::const_iterator it = interfaces.begin(); TQValueList::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(""); 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.tqreplace('/', "_"); } 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] " << 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 , --class " << 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.tqfind(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