1/*
2 * BluezQt - Asynchronous BlueZ wrapper library
3 *
4 * SPDX-FileCopyrightText: 2019 Manuel Weichselbaumer <mincequi@web.de>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include <QDebug>
10#include <QFile>
11#include <QRegularExpression>
12
13#include "CppGenerator.h"
14
15#include "BluezApiParser.h"
16#include "TypeAnnotation.h"
17
18CppGenerator::CppGenerator(const Config &config)
19 : m_config(config)
20{
21}
22
23bool CppGenerator::generate(const BluezApiParser &parser)
24{
25 writeAdaptorHeader(parser);
26 writeAdaptorSource(parser);
27
28 return true;
29}
30
31void CppGenerator::writeAdaptorHeader(const BluezApiParser &parser)
32{
33 // Iterate interfaces
34 for (const auto &interface : parser.interfaces()) {
35 auto className = interfaceToClassName(interface: interface.name());
36 const QString includeGuard = QLatin1String("BLUEZQT_") + className.toUpper() + QLatin1String("ADAPTOR_H");
37
38 // Create file
39 QFile file(className.toLower() + QStringLiteral("adaptor.h"));
40 if (!file.open(flags: QIODevice::WriteOnly | QIODevice::Text)) {
41 qWarning() << "Error opening file for writing:" << file.fileName();
42 return;
43 }
44
45 // Write content
46 QTextStream stream(&file);
47 writeCopyrightHeader(stream);
48 stream << "#ifndef " << includeGuard << "\n";
49 stream << "#define " << includeGuard << "\n\n";
50 stream << "#include <QDBusAbstractAdaptor>\n\n";
51 stream << "class QDBusObjectPath;\n\n";
52 stream << "namespace BluezQt\n{\n\n";
53 stream << "class " << className << ";\n\n";
54 stream << "class " << className << "Adaptor : public QDBusAbstractAdaptor\n{\n";
55 stream << " Q_OBJECT \n";
56 stream << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface.name() << "\")\n";
57
58 // Write properties
59 for (const auto &property : interface.properties().properties()) {
60 // Respect config
61 if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
62 continue;
63 }
64 stream << " Q_PROPERTY(" << bluezToQt(type: property.type()) << " " << property.name() << " READ " << lowerFirstChars(string: property.name());
65 if (!property.tags().isReadOnly) {
66 stream << " WRITE set" << property.name();
67 }
68 stream << ")\n";
69 }
70
71 stream << "\npublic:\n";
72 stream << " explicit " << className << "Adaptor(" << className << "* parent);\n\n";
73
74 // Write property accessors
75 for (const auto &property : interface.properties().properties()) {
76 // Respect config
77 if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
78 continue;
79 }
80 stream << " " << bluezToQt(type: property.type()) << " " << lowerFirstChars(string: property.name()) << "() const;\n";
81 if (!property.tags().isReadOnly) {
82 stream << " void set" << property.name() << "(const " << bluezToQt(type: property.type()) << " &" << lowerFirstChars(string: property.name()) << ");\n";
83 }
84 stream << "\n";
85 }
86
87 stream << "public Q_SLOTS:\n";
88
89 // write Methods
90 for (const auto &method : interface.methods().methods()) {
91 // Respect config
92 if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) {
93 continue;
94 }
95 stream << " " << bluezToQt(type: method.outParameter().type()) << " " << method.name() << "(";
96 for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
97 stream << "const " << bluezToQt(type: it->type()) << " &" << it->name();
98 if (it != std::prev(x: method.inParameters().end())) {
99 stream << ", ";
100 }
101 }
102 stream << ");\n";
103 }
104
105 // write private members
106 stream << "\nprivate:\n";
107 stream << " " << className << " *m_" << lowerFirstChars(string: className) << ";\n";
108 stream << "};\n\n} // namespace BluezQt\n\n";
109
110 // include guard
111 stream << "#endif\n";
112
113 file.close();
114 }
115}
116
117void CppGenerator::writeAdaptorSource(const BluezApiParser &parser)
118{
119 // Iterate interfaces
120 for (const auto &interface : parser.interfaces()) {
121 auto className = interfaceToClassName(interface: interface.name());
122
123 // Create file
124 QFile file(className.toLower() + QStringLiteral("adaptor.cpp"));
125 if (!file.open(flags: QIODevice::WriteOnly | QIODevice::Text)) {
126 qWarning() << "Error opening file for writing:" << file.fileName();
127 return;
128 }
129
130 // Write content
131 QTextStream stream(&file);
132 writeCopyrightHeader(stream);
133 stream << "#include \"" << className << "Adaptor.h\"\n\n";
134 stream << "#include \"" << className << ".h\"\n\n";
135 stream << "namespace BluezQt\n{\n\n";
136 stream << className << "Adaptor::" << className << "Adaptor(" << className << " *parent)\n";
137 stream << " : QDBusAbstractAdaptor(parent)\n";
138 stream << " , m_" << lowerFirstChars(string: className) << "(parent)\n";
139 stream << "{\n}\n\n";
140
141 // Write property accessors
142 for (const auto &property : interface.properties().properties()) {
143 // Respect config
144 if ((property.tags().isOptional && !m_config.useOptional) || (property.tags().isExperimental && !m_config.useExperimental)) {
145 continue;
146 }
147 stream << bluezToQt(type: property.type()) << " " << className << "Adaptor::" << lowerFirstChars(string: property.name()) << "() const\n";
148 stream << "{\n";
149 stream << " return m_" << lowerFirstChars(string: className) << "->" << lowerFirstChars(string: property.name()) << "();\n";
150 stream << "}\n\n";
151 if (!property.tags().isReadOnly) {
152 stream << "void " << className << "Adaptor::set" << property.name() << "(const " << bluezToQt(type: property.type()) << " &"
153 << lowerFirstChars(string: property.name()) << ");\n";
154 stream << "{\n";
155 stream << " m_" << lowerFirstChars(string: className) << "->set" << property.name() << "(" << lowerFirstChars(string: property.name()) << ");\n";
156 stream << "}\n\n";
157 }
158 }
159
160 // write Methods
161 for (const auto &method : interface.methods().methods()) {
162 // Respect config
163 if ((method.tags().isOptional && !m_config.useOptional) || (method.tags().isExperimental && !m_config.useExperimental)) {
164 continue;
165 }
166 stream << bluezToQt(type: method.outParameter().type()) << " " << className << "Adaptor::" << method.name() << "(";
167 for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
168 stream << "const " << bluezToQt(type: it->type()) << " &" << it->name();
169 if (it != std::prev(x: method.inParameters().end())) {
170 stream << ", ";
171 }
172 }
173 stream << ")\n{\n";
174 stream << " return m_" << lowerFirstChars(string: className) << "->" << lowerFirstChars(string: method.name()) << "(";
175 for (auto it = method.inParameters().begin(); it != method.inParameters().end(); ++it) {
176 stream << it->name();
177 if (it != std::prev(x: method.inParameters().end())) {
178 stream << ", ";
179 }
180 }
181 stream << ");\n}\n\n";
182 }
183
184 stream << "} // namespace BluezQt\n";
185
186 file.close();
187 }
188}
189
190QString CppGenerator::interfaceToClassName(const QString &interface)
191{
192 const int index = interface.lastIndexOf(re: QRegularExpression(QStringLiteral("\\.[A-Z]\\w+"))) + 1;
193 auto className = interface.mid(position: index);
194 while (className.back() > QLatin1Char('0') && className.back() <= QLatin1Char('9')) {
195 className.remove(i: className.size() - 1, len: 1);
196 }
197
198 return className;
199}
200
201QString CppGenerator::lowerFirstChars(const QString &string)
202{
203 QString str(string);
204 // str.replace(0, 1, string.at(0).toLower());
205
206 const QRegularExpression rx(QStringLiteral("^([A-Z]+)"));
207 QRegularExpressionMatch match = rx.match(subject: string);
208 if (match.hasMatch()) {
209 QString matchedStr = match.captured(nth: 1);
210 for (int i = 0; i < matchedStr.size() - 1; ++i) {
211 str.replace(i, len: 1, after: str.at(i).toLower());
212 }
213 }
214 str.replace(i: 0, len: 1, after: string.at(i: 0).toLower());
215 str.replace(i: string.size() - 1, len: 1, after: string.at(i: string.size() - 1).toLower());
216
217 return str;
218}
219
220void CppGenerator::writeCopyrightHeader(QTextStream &stream)
221{
222 stream << "/*\n";
223 stream << " * BluezQt - Asynchronous Bluez wrapper library\n";
224 stream << " *\n";
225 stream << " * Copyright (C) " << m_config.year << " " << m_config.author << "\n";
226 stream << " *\n";
227 stream << " * This library is free software; you can redistribute it and/or\n";
228 stream << " * modify it under the terms of the GNU Lesser General Public\n";
229 stream << " * License as published by the Free Software Foundation; either\n";
230 stream << " * version 2.1 of the License, or (at your option) version 3, or any\n";
231 stream << " * later version accepted by the membership of KDE e.V. (or its\n";
232 stream << " * successor approved by the membership of KDE e.V.), which shall\n";
233 stream << " * act as a proxy defined in Section 6 of version 3 of the license.\n";
234 stream << " *\n";
235 stream << " * This library is distributed in the hope that it will be useful,\n";
236 stream << " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n";
237 stream << " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n";
238 stream << " * Lesser General Public License for more details.\n";
239 stream << " *\n";
240 stream << " * You should have received a copy of the GNU Lesser General Public\n";
241 stream << " * License along with this library. If not, see <http://www.gnu.org/licenses/>.\n";
242 stream << " */\n\n";
243}
244

source code of bluez-qt/tools/bluezapi2qt/CppGenerator.cpp