diff --git a/tools/bluezapi2qt/CppGenerator.cpp b/tools/bluezapi2qt/CppGenerator.cpp index 897fe3d..fb10f73 100644 --- a/tools/bluezapi2qt/CppGenerator.cpp +++ b/tools/bluezapi2qt/CppGenerator.cpp @@ -1,256 +1,259 @@ /* * BluezQt - Asynchronous BlueZ wrapper library * * Copyright (C) 2019 Manuel Weichselbaumer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ -#include "CppGenerator.h" - #include #include +#include + +#include "CppGenerator.h" #include "BluezApiParser.h" #include "TypeAnnotation.h" CppGenerator::CppGenerator(const Config &config) : m_config(config) { } bool CppGenerator::generate(const BluezApiParser &parser) { writeAdaptorHeader(parser); writeAdaptorSource(parser); return true; } void CppGenerator::writeAdaptorHeader(const BluezApiParser &parser) { // Iterate interfaces for (const auto &interface : parser.interfaces()) { auto className = interfaceToClassName(interface.name()); // Create file QFile file(className.toLower() + QStringLiteral("adaptor.h")); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Error opening file for writing:" << file.fileName(); return; } // Write content QTextStream stream(&file); writeCopyrightHeader(stream); stream << "#pragma once" << endl << endl; stream << "#include " << endl << endl; stream << "class QDBusObjectPath;" << endl << endl; stream << "namespace BluezQt" << endl << "{" << endl << endl; stream << "class " << className << ";" << endl << endl; stream << "class " << className << "Adaptor : public QDBusAbstractAdaptor" << endl << "{" << endl; stream << " Q_OBJECT " << endl; stream << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface.name() << "\")" << endl; // Write properties for (const auto &property : interface.properties().properties()) { // Respect config if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) { continue; } stream << " Q_PROPERTY(" << bluezToQt(property.type()) << " " << property.name() << " READ " << lowerFirstChars(property.name()); if (!property.tags().isReadOnly) { stream << " WRITE set" << property.name(); } stream << ")" << endl; } stream << endl << "public:" << endl; stream << " explicit " << className << "Adaptor(" << className << "* parent);" << endl << endl; // Write property accessors for (const auto &property : interface.properties().properties()) { // Respect config if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) { continue; } stream << " " << bluezToQt(property.type()) << " " << lowerFirstChars(property.name()) << "() const;" << endl; if (!property.tags().isReadOnly) { stream << " void set" << property.name() << "(const " << bluezToQt(property.type()) << " &" << lowerFirstChars(property.name()) << ");" << endl; } stream << endl; } stream << "public Q_SLOTS:" << endl; // write Methods for (const auto &method : interface.methods().methods()) { // Respect config if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) { continue; } stream << " " << bluezToQt(method.outParameter().type()) << " " << method.name() << "("; for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) { stream << "const " << bluezToQt(it->type()) << " &" << it->name(); if (it != std::prev(method.inParameters().end())) { stream << ", "; } } stream << ");" << endl; } // write private members stream << endl << "private:" << endl; stream << " " << className << " *m_" << lowerFirstChars(className) << ";" << endl; stream << "};" << endl << endl << "} // namespace BluezQt" << endl; file.close(); } } void CppGenerator::writeAdaptorSource(const BluezApiParser &parser) { // Iterate interfaces for (const auto &interface : parser.interfaces()) { auto className = interfaceToClassName(interface.name()); // Create file QFile file(className.toLower() + QStringLiteral("adaptor.cpp")); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Error opening file for writing:" << file.fileName(); return; } // Write content QTextStream stream(&file); writeCopyrightHeader(stream); stream << "#include \"" << className << "Adaptor.h\"" << endl << endl; stream << "#include \"" << className << ".h\"" << endl << endl; stream << "namespace BluezQt" << endl << "{" << endl << endl; stream << className << "Adaptor::" << className << "Adaptor(" << className << " *parent)" << endl; stream << " : QDBusAbstractAdaptor(parent)" << endl; stream << " , m_" << lowerFirstChars(className) << "(parent)" << endl; stream << "{" << endl << "}" << endl << endl; // Write property accessors for (const auto &property : interface.properties().properties()) { // Respect config if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) { continue; } stream << bluezToQt(property.type()) << " " << className << "Adaptor::" << lowerFirstChars(property.name()) << "() const" << endl; stream << "{" << endl; stream << " return m_" << lowerFirstChars(className) << "->" << lowerFirstChars(property.name()) << "();" << endl; stream << "}" << endl << endl; if (!property.tags().isReadOnly) { stream << "void " << className << "Adaptor::set" << property.name() << "(const " << bluezToQt(property.type()) << " &" << lowerFirstChars(property.name()) << ");" << endl; stream << "{" << endl; stream << " m_" << lowerFirstChars(className) << "->set" << property.name() << "(" << lowerFirstChars(property.name()) << ");" << endl; stream << "}" << endl << endl; } } // write Methods for (const auto &method : interface.methods().methods()) { // Respect config if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) { continue; } stream << bluezToQt(method.outParameter().type()) << " " << className << "Adaptor::" << method.name() << "("; for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) { stream << "const " << bluezToQt(it->type()) << " &" << it->name(); if (it != std::prev(method.inParameters().end())) { stream << ", "; } } stream << ")" << endl << "{" << endl; stream << " return m_" << lowerFirstChars(className) << "->" << lowerFirstChars(method.name()) << "("; for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) { stream << it->name(); if (it != std::prev(method.inParameters().end())) { stream << ", "; } } stream << ");" << endl << "}" << endl << endl; } stream << "} // namespace BluezQt" << endl; file.close(); } } QString CppGenerator::interfaceToClassName(const QString &interface) { - auto className = interface.mid(interface.lastIndexOf(QRegExp(QStringLiteral("\\.[A-Z]\\w+")))+1); + const int index = interface.lastIndexOf(QRegularExpression(QStringLiteral("\\.[A-Z]\\w+"))) + 1; + auto className = interface.mid(index); while (className.back() > L'0' && className.back() <= L'9') { className.remove(className.size()-1, 1); } return className; } QString CppGenerator::lowerFirstChars(const QString &string) { QString str(string); //str.replace(0, 1, string.at(0).toLower()); - QRegExp rx(QStringLiteral("^([A-Z]+)"), Qt::CaseSensitive, QRegExp::RegExp2); - if (rx.indexIn(string) != -1) { - QString caps = rx.capturedTexts().last(); - for (int i = 0; i < caps.size()-1; ++i) { + const QRegularExpression rx(QStringLiteral("^([A-Z]+)")); + QRegularExpressionMatch match = rx.match(string); + if (match.hasMatch()) { + QString matchedStr = match.captured(); + for (int i = 0; i < matchedStr.size() - 1; ++i) { str.replace(i, 1, str.at(i).toLower()); } } str.replace(0, 1, string.at(0).toLower()); str.replace(string.size()-1, 1, string.at(string.size()-1).toLower()); return str; } void CppGenerator::writeCopyrightHeader(QTextStream &stream) { stream << "/*" << endl; stream << " * BluezQt - Asynchronous Bluez wrapper library" << endl; stream << " *" << endl; stream << " * Copyright (C) " << m_config.year << " " << m_config.author << endl; stream << " *" << endl; stream << " * This library is free software; you can redistribute it and/or" << endl; stream << " * modify it under the terms of the GNU Lesser General Public" << endl; stream << " * License as published by the Free Software Foundation; either" << endl; stream << " * version 2.1 of the License, or (at your option) version 3, or any" << endl; stream << " * later version accepted by the membership of KDE e.V. (or its" << endl; stream << " * successor approved by the membership of KDE e.V.), which shall" << endl; stream << " * act as a proxy defined in Section 6 of version 3 of the license." << endl; stream << " *" << endl; stream << " * This library is distributed in the hope that it will be useful," << endl; stream << " * but WITHOUT ANY WARRANTY; without even the implied warranty of" << endl; stream << " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU" << endl; stream << " * Lesser General Public License for more details." << endl; stream << " *" << endl; stream << " * You should have received a copy of the GNU Lesser General Public" << endl; stream << " * License along with this library. If not, see ." << endl; stream << " */" << endl << endl; } diff --git a/tools/bluezapi2qt/Interface.cpp b/tools/bluezapi2qt/Interface.cpp index 35e6738..c8d0168 100644 --- a/tools/bluezapi2qt/Interface.cpp +++ b/tools/bluezapi2qt/Interface.cpp @@ -1,147 +1,152 @@ /* * BluezQt - Asynchronous BlueZ wrapper library * * Copyright (C) 2019 Manuel Weichselbaumer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "Interface.h" +#include + Interface::Interface() { } bool Interface::parse(const QString &line) { if (line.startsWith(QLatin1String("Service\t"))) { m_state = State::Service; } else if (line.startsWith(QLatin1String("Interface\t"))) { m_state = State::Interface; } else if (line.startsWith(QLatin1String("Object path\t"))) { m_state = State::ObjectPath; } else if (line.startsWith(QLatin1String("Methods\t")) || Methods::isMethod(line)) { // Argh! AgentManager is missing the Methods keyword m_state = State::Methods; } else if (line.startsWith(QLatin1String("Properties\t"))) { m_state = State::Properties; } else if (m_state != State::Comment && !line.isEmpty() && !line.startsWith(QLatin1String("\t"))) { // If we do not parse comment, but line starts with characters, we are done. return false; } switch (m_state) { case State::Comment: parseComment(line); break; case State::Service: parseService(line); break; case State::Interface: parseInterface(line); break; case State::ObjectPath: parseObjectPath(line); break; case State::Methods: m_methods.parse(line); break; case State::Properties: m_properties.parse(line); break; } return true; } bool Interface::finalize() { bool success = true; success &= m_methods.finalize(); success &= m_properties.finalize(); return success; } QStringList Interface::comment() const { return m_comment; } QString Interface::service() const { return m_service; } QString Interface::name() const { return m_name; } QString Interface::objectPath() const { return m_objectPath; } Methods Interface::methods() const { return m_methods; } Properties Interface::properties() const { return m_properties; } void Interface::parseComment(const QString &line) { if (line.isEmpty()) { m_comment.append(QString()); return; } else if (line.startsWith(QLatin1Char(' ')) || line.startsWith(QStringLiteral("\t"))) { m_comment.append(QString()); } if (!m_comment.last().isEmpty()) { m_comment.last() += QLatin1Char(' '); } m_comment.last() += line; } void Interface::parseService(const QString &line) { - QRegExp rx(QStringLiteral("Service\\t+(.+)"), Qt::CaseSensitive, QRegExp::RegExp2); - if (rx.indexIn(line) != -1) { - m_service = rx.capturedTexts().last(); + const QRegularExpression rx(QStringLiteral("Service\\t+(.+)")); + QRegularExpressionMatch match = rx.match(line); + if (match.hasMatch()) { + m_service = match.captured(); } } void Interface::parseInterface(const QString &line) { - QRegExp rx(QStringLiteral("Interface\\t+(.+)"), Qt::CaseSensitive, QRegExp::RegExp2); - if (rx.indexIn(line) != -1) { - m_name = rx.capturedTexts().last(); + const QRegularExpression rx(QStringLiteral("Interface\\t+(.+)")); + QRegularExpressionMatch match = rx.match(line); + if (match.hasMatch()) { + m_name = match.captured(); } } void Interface::parseObjectPath(const QString &line) { - QRegExp rx(QStringLiteral("Object path\\t+(.+)"), Qt::CaseSensitive, QRegExp::RegExp2); - if (rx.indexIn(line) != -1) { - m_objectPath = rx.capturedTexts().last(); + const QRegularExpression rx(QStringLiteral("Object path\\t+(.+)")); + QRegularExpressionMatch match = rx.match(line); + if (match.hasMatch()) { + m_objectPath = match.captured(); } } diff --git a/tools/bluezapi2qt/Method.cpp b/tools/bluezapi2qt/Method.cpp index bd0beff..55784e2 100644 --- a/tools/bluezapi2qt/Method.cpp +++ b/tools/bluezapi2qt/Method.cpp @@ -1,111 +1,113 @@ /* * BluezQt - Asynchronous BlueZ wrapper library * * Copyright (C) 2019 Manuel Weichselbaumer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ +#include + #include "Method.h" Method::Method() { } bool Method::finalize() { for (const auto &tag : m_stringTags) { m_tags.isOptional |= tag.contains(QLatin1String("optional"), Qt::CaseInsensitive); m_tags.isDeprecated |= tag.contains(QLatin1String("deprecated"), Qt::CaseInsensitive); m_tags.isExperimental |= tag.contains(QLatin1String("experimental"), Qt::CaseInsensitive); } bool success = true; success &= m_comment.finalize(); for (const auto &inParam : m_inParameterStrings) { m_inParameters.push_back(Parameter::fromString(inParam)); } m_outParameterStrings.removeOne(QStringLiteral("void")); if (m_outParameterStrings.isEmpty()) { m_outParameter = Parameter::fromString(QStringLiteral("void unnamend")); return true; } // Guess out parameter name from method name QString paramName = guessOutParameterName(); if (!paramName.isEmpty()) { m_outParameterStrings.front() += QLatin1Char(' ') + paramName; m_outParameter = Parameter::fromString(m_outParameterStrings.front()); } else { for (int i = 0; i < m_outParameterStrings.size(); ++i) { m_outParameterStrings[i] += QLatin1String(" value") + QString::number(i); } } for (const auto &outParam : m_outParameterStrings) { m_outParameters.push_back(Parameter::fromString(outParam)); } return success; } QString Method::name() const { return m_name; } QList Method::inParameters() const { return m_inParameters; } QList Method::outParameters() const { return m_outParameters; } Parameter Method::outParameter() const { return m_outParameter; } Method::Tags Method::tags() const { return m_tags; } QStringList Method::comment() const { return m_comment; } QString Method::guessOutParameterName() const { if (m_outParameterStrings.size() != 1) { return QString(); } - QRegExp rx(QStringLiteral("([A-Z][a-z0-9]+)+")); - if (rx.indexIn(m_name, 1) == -1) { + const QRegularExpression rx(QStringLiteral("([A-Z][a-z0-9]+)+")); + QRegularExpressionMatch match = rx.match(m_name, 1); + if (!match.hasMatch()) { return QStringLiteral("value"); } - QStringList list = rx.capturedTexts(); - return list.last().toLower(); + return match.captured().toLower(); } diff --git a/tools/bluezapi2qt/Methods.cpp b/tools/bluezapi2qt/Methods.cpp index d9d96bd..de146f8 100644 --- a/tools/bluezapi2qt/Methods.cpp +++ b/tools/bluezapi2qt/Methods.cpp @@ -1,91 +1,83 @@ /* * BluezQt - Asynchronous BlueZ wrapper library * * Copyright (C) 2019 Manuel Weichselbaumer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "Methods.h" #include +#include + +static const QRegularExpression rx(QStringLiteral( + "\\t+" // preceding tabs + "(?:(.+) )?" // return types - Argh! LE Advertising Manager does not specify return type + "([A-Z]\\w+)" // method name + "\\(([^\\)]*)\\)" // parameters + "(?: \\[(.*)\\])?" // tags + "(?: \\((.*)\\))?" // limitations + ), QRegularExpression::CaseInsensitiveOption); Methods::Methods() { } bool Methods::isMethod(const QString &line) { - QRegExp rx(QStringLiteral( - "\\t+" // preceding tabs - "(?:(.+) )?" // return types - Argh! LE Advertising Manager does not specify return type - "([A-Z]\\w+)" // method name - "\\(([^\\)]*)\\)" // parameters - "(?: \\[(.*)\\])?" // tags - "(?: \\((.*)\\))?" // limitations - ), Qt::CaseInsensitive, QRegExp::RegExp2); - // Check if we match a method - return (rx.indexIn(line) != -1); + return (rx.match(line).hasMatch()); } void Methods::parse(const QString &line) { - QRegExp rx(QStringLiteral( - "\\t+" // preceding tabs - "(?:(.+) )?" // return types - Argh! LE Advertising Manager does not specify return type - "([A-Z]\\w+)" // method name - "\\(([^\\)]*)\\)" // parameters - "(?: \\[(.*)\\])?" // tags - "(?: \\((.*)\\))?" // limitations - ), Qt::CaseInsensitive, QRegExp::RegExp2); - // Check if we match a method - if (rx.indexIn(line) != -1) { - QStringList list = rx.capturedTexts(); + QRegularExpressionMatch match = rx.match(line); + if (match.hasMatch()) { m_methods.emplace_back(Method()); m_currentMethod = &m_methods.back(); - m_currentMethod->m_outParameterStrings = list.at(1).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); - m_currentMethod->m_name = list.at(2); - m_currentMethod->m_inParameterStrings = list.at(3).split(QStringLiteral(", "), QString::SkipEmptyParts); - m_currentMethod->m_stringTags = list.at(4).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); - m_currentMethod->m_limitation = list.at(5).toLower(); + m_currentMethod->m_outParameterStrings = match.captured(1).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); + m_currentMethod->m_name = match.captured(2); + m_currentMethod->m_inParameterStrings = match.captured(3).split(QStringLiteral(", "), QString::SkipEmptyParts); + m_currentMethod->m_stringTags = match.captured(4).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); + m_currentMethod->m_limitation = match.captured(5).toLower(); } else if (m_currentMethod) { // Skip first empty line if (line.isEmpty() && m_currentMethod->m_comment.isEmpty()) { return; } m_currentMethod->m_comment.append(line); } } bool Methods::finalize() { bool success = true; for (auto &method : m_methods) { success &= method.finalize(); } return success; } std::list Methods::methods() const { return m_methods; } diff --git a/tools/bluezapi2qt/Properties.cpp b/tools/bluezapi2qt/Properties.cpp index 2500be3..783766b 100644 --- a/tools/bluezapi2qt/Properties.cpp +++ b/tools/bluezapi2qt/Properties.cpp @@ -1,77 +1,77 @@ /* * BluezQt - Asynchronous BlueZ wrapper library * * Copyright (C) 2019 Manuel Weichselbaumer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "Properties.h" -#include +#include #include Properties::Properties() { } void Properties::parse(const QString &line) { - QRegExp rx(QStringLiteral( - "(?:Properties|^)" // Properties keyword or start of line - "\\t{1,2}" // preceding tabs (max 2) - "([a-z1-6{}_]+)" // type name - " " // space - "([A-Z]\\w+)" // method name - "(?: \\[(.*)\\])?" // tags - "(?: \\((.*)\\))?" // limitations - ), Qt::CaseSensitive, QRegExp::RegExp2); + const QRegularExpression rx(QStringLiteral( + "(?:Properties|^)" // Properties keyword or start of line + "\\t{1,2}" // preceding tabs (max 2) + "([a-z1-6{}_]+)" // type name + " " // space + "([A-Z]\\w+)" // method name + "(?: \\[(.*)\\])?" // tags + "(?: \\((.*)\\))?" // limitations + ), QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = rx.match(line); // Check if we match a property - if (rx.indexIn(line) != -1) { - QStringList list = rx.capturedTexts(); + if (match.hasMatch()) { m_properties.emplace_back(Property()); m_currentProperty = &m_properties.back(); - m_currentProperty->m_type = list.at(1).toLower(); - m_currentProperty->m_name = list.at(2); - m_currentProperty->m_stringTags = list.at(3).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); - m_currentProperty->m_limitation = list.at(4).toLower(); + m_currentProperty->m_type = match.captured(1).toLower(); + m_currentProperty->m_name = match.captured(2); + m_currentProperty->m_stringTags = match.captured(3).toLower().split(QStringLiteral(", "), QString::SkipEmptyParts); + m_currentProperty->m_limitation = match.captured(4).toLower(); } else if (m_currentProperty) { // Skip first empty line if (line.isEmpty() && m_currentProperty->m_comment.isEmpty()) { return; } m_currentProperty->m_comment.append(line); } } bool Properties::finalize() { bool success = true; for (auto &property : m_properties) { success &= property.finalize(); } return success; } std::list Properties::properties() const { return m_properties; }