File indexing completed on 2024-04-28 03:52:07

0001 /*
0002  * BluezQt - Asynchronous BlueZ wrapper library
0003  *
0004  * SPDX-FileCopyrightText: 2019 Manuel Weichselbaumer <mincequi@web.de>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007  */
0008 
0009 #include <QDebug>
0010 #include <QFile>
0011 #include <QRegularExpression>
0012 
0013 #include "CppGenerator.h"
0014 
0015 #include "BluezApiParser.h"
0016 #include "TypeAnnotation.h"
0017 
0018 CppGenerator::CppGenerator(const Config &config)
0019     : m_config(config)
0020 {
0021 }
0022 
0023 bool CppGenerator::generate(const BluezApiParser &parser)
0024 {
0025     writeAdaptorHeader(parser);
0026     writeAdaptorSource(parser);
0027 
0028     return true;
0029 }
0030 
0031 void CppGenerator::writeAdaptorHeader(const BluezApiParser &parser)
0032 {
0033     // Iterate interfaces
0034     for (const auto &interface : parser.interfaces()) {
0035         auto className = interfaceToClassName(interface.name());
0036         const QString includeGuard = QLatin1String("BLUEZQT_") + className.toUpper() + QLatin1String("ADAPTOR_H");
0037 
0038         // Create file
0039         QFile file(className.toLower() + QStringLiteral("adaptor.h"));
0040         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0041             qWarning() << "Error opening file for writing:" << file.fileName();
0042             return;
0043         }
0044 
0045         // Write content
0046         QTextStream stream(&file);
0047         writeCopyrightHeader(stream);
0048         stream << "#ifndef " << includeGuard << "\n";
0049         stream << "#define " << includeGuard << "\n\n";
0050         stream << "#include <QDBusAbstractAdaptor>\n\n";
0051         stream << "class QDBusObjectPath;\n\n";
0052         stream << "namespace BluezQt\n{\n\n";
0053         stream << "class " << className << ";\n\n";
0054         stream << "class " << className << "Adaptor : public QDBusAbstractAdaptor\n{\n";
0055         stream << "    Q_OBJECT \n";
0056         stream << "    Q_CLASSINFO(\"D-Bus Interface\", \"" << interface.name() << "\")\n";
0057 
0058         // Write properties
0059         for (const auto &property : interface.properties().properties()) {
0060             // Respect config
0061             if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
0062                 continue;
0063             }
0064             stream << "    Q_PROPERTY(" << bluezToQt(property.type()) << " " << property.name() << " READ " << lowerFirstChars(property.name());
0065             if (!property.tags().isReadOnly) {
0066                 stream << " WRITE set" << property.name();
0067             }
0068             stream << ")\n";
0069         }
0070 
0071         stream << "\npublic:\n";
0072         stream << "    explicit " << className << "Adaptor(" << className << "* parent);\n\n";
0073 
0074         // Write property accessors
0075         for (const auto &property : interface.properties().properties()) {
0076             // Respect config
0077             if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
0078                 continue;
0079             }
0080             stream << "    " << bluezToQt(property.type()) << " " << lowerFirstChars(property.name()) << "() const;\n";
0081             if (!property.tags().isReadOnly) {
0082                 stream << "    void set" << property.name() << "(const " << bluezToQt(property.type()) << " &" << lowerFirstChars(property.name()) << ");\n";
0083             }
0084             stream << "\n";
0085         }
0086 
0087         stream << "public Q_SLOTS:\n";
0088 
0089         // write Methods
0090         for (const auto &method : interface.methods().methods()) {
0091             // Respect config
0092             if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) {
0093                 continue;
0094             }
0095             stream << "    " << bluezToQt(method.outParameter().type()) << " " << method.name() << "(";
0096             for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
0097                 stream << "const " << bluezToQt(it->type()) << " &" << it->name();
0098                 if (it != std::prev(method.inParameters().end())) {
0099                     stream << ", ";
0100                 }
0101             }
0102             stream << ");\n";
0103         }
0104 
0105         // write private members
0106         stream << "\nprivate:\n";
0107         stream << "    " << className << " *m_" << lowerFirstChars(className) << ";\n";
0108         stream << "};\n\n} // namespace BluezQt\n\n";
0109 
0110         // include guard
0111         stream << "#endif\n";
0112 
0113         file.close();
0114     }
0115 }
0116 
0117 void CppGenerator::writeAdaptorSource(const BluezApiParser &parser)
0118 {
0119     // Iterate interfaces
0120     for (const auto &interface : parser.interfaces()) {
0121         auto className = interfaceToClassName(interface.name());
0122 
0123         // Create file
0124         QFile file(className.toLower() + QStringLiteral("adaptor.cpp"));
0125         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0126             qWarning() << "Error opening file for writing:" << file.fileName();
0127             return;
0128         }
0129 
0130         // Write content
0131         QTextStream stream(&file);
0132         writeCopyrightHeader(stream);
0133         stream << "#include \"" << className << "Adaptor.h\"\n\n";
0134         stream << "#include \"" << className << ".h\"\n\n";
0135         stream << "namespace BluezQt\n{\n\n";
0136         stream << className << "Adaptor::" << className << "Adaptor(" << className << " *parent)\n";
0137         stream << "    : QDBusAbstractAdaptor(parent)\n";
0138         stream << "    , m_" << lowerFirstChars(className) << "(parent)\n";
0139         stream << "{\n}\n\n";
0140 
0141         // Write property accessors
0142         for (const auto &property : interface.properties().properties()) {
0143             // Respect config
0144             if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
0145                 continue;
0146             }
0147             stream << bluezToQt(property.type()) << " " << className << "Adaptor::" << lowerFirstChars(property.name()) << "() const\n";
0148             stream << "{\n";
0149             stream << "    return m_" << lowerFirstChars(className) << "->" << lowerFirstChars(property.name()) << "();\n";
0150             stream << "}\n\n";
0151             if (!property.tags().isReadOnly) {
0152                 stream << "void " << className << "Adaptor::set" << property.name() << "(const " << bluezToQt(property.type()) << " &"
0153                        << lowerFirstChars(property.name()) << ");\n";
0154                 stream << "{\n";
0155                 stream << "    m_" << lowerFirstChars(className) << "->set" << property.name() << "(" << lowerFirstChars(property.name()) << ");\n";
0156                 stream << "}\n\n";
0157             }
0158         }
0159 
0160         // write Methods
0161         for (const auto &method : interface.methods().methods()) {
0162             // Respect config
0163             if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) {
0164                 continue;
0165             }
0166             stream << bluezToQt(method.outParameter().type()) << " " << className << "Adaptor::" << method.name() << "(";
0167             for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
0168                 stream << "const " << bluezToQt(it->type()) << " &" << it->name();
0169                 if (it != std::prev(method.inParameters().end())) {
0170                     stream << ", ";
0171                 }
0172             }
0173             stream << ")\n{\n";
0174             stream << "    return m_" << lowerFirstChars(className) << "->" << lowerFirstChars(method.name()) << "(";
0175             for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
0176                 stream << it->name();
0177                 if (it != std::prev(method.inParameters().end())) {
0178                     stream << ", ";
0179                 }
0180             }
0181             stream << ");\n}\n\n";
0182         }
0183 
0184         stream << "} // namespace BluezQt\n";
0185 
0186         file.close();
0187     }
0188 }
0189 
0190 QString CppGenerator::interfaceToClassName(const QString &interface)
0191 {
0192     const int index = interface.lastIndexOf(QRegularExpression(QStringLiteral("\\.[A-Z]\\w+"))) + 1;
0193     auto className = interface.mid(index);
0194     while (className.back() > QLatin1Char('0') && className.back() <= QLatin1Char('9')) {
0195         className.remove(className.size() - 1, 1);
0196     }
0197 
0198     return className;
0199 }
0200 
0201 QString CppGenerator::lowerFirstChars(const QString &string)
0202 {
0203     QString str(string);
0204     // str.replace(0, 1, string.at(0).toLower());
0205 
0206     const QRegularExpression rx(QStringLiteral("^([A-Z]+)"));
0207     QRegularExpressionMatch match = rx.match(string);
0208     if (match.hasMatch()) {
0209         QString matchedStr = match.captured(1);
0210         for (int i = 0; i < matchedStr.size() - 1; ++i) {
0211             str.replace(i, 1, str.at(i).toLower());
0212         }
0213     }
0214     str.replace(0, 1, string.at(0).toLower());
0215     str.replace(string.size() - 1, 1, string.at(string.size() - 1).toLower());
0216 
0217     return str;
0218 }
0219 
0220 void CppGenerator::writeCopyrightHeader(QTextStream &stream)
0221 {
0222     stream << "/*\n";
0223     stream << " * BluezQt - Asynchronous Bluez wrapper library\n";
0224     stream << " *\n";
0225     stream << " * Copyright (C) " << m_config.year << " " << m_config.author << "\n";
0226     stream << " *\n";
0227     stream << " * This library is free software; you can redistribute it and/or\n";
0228     stream << " * modify it under the terms of the GNU Lesser General Public\n";
0229     stream << " * License as published by the Free Software Foundation; either\n";
0230     stream << " * version 2.1 of the License, or (at your option) version 3, or any\n";
0231     stream << " * later version accepted by the membership of KDE e.V. (or its\n";
0232     stream << " * successor approved by the membership of KDE e.V.), which shall\n";
0233     stream << " * act as a proxy defined in Section 6 of version 3 of the license.\n";
0234     stream << " *\n";
0235     stream << " * This library is distributed in the hope that it will be useful,\n";
0236     stream << " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n";
0237     stream << " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n";
0238     stream << " * Lesser General Public License for more details.\n";
0239     stream << " *\n";
0240     stream << " * You should have received a copy of the GNU Lesser General Public\n";
0241     stream << " * License along with this library. If not, see <http://www.gnu.org/licenses/>.\n";
0242     stream << " */\n\n";
0243 }