KConfig

KConfigHeaderGenerator.cpp
1/*
2 This file is part of the KDE libraries.
3
4 SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
6 SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
7 SPDX-FileCopyrightText: 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
8 SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
9 SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
10
11 SPDX-License-Identifier: LGPL-2.0-or-later
12*/
13
14#include "KConfigHeaderGenerator.h"
15
16#include <QDebug>
17#include <QTextStream>
18#include <iostream>
19
20KConfigHeaderGenerator::KConfigHeaderGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result)
21 : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result)
22{
23}
24
25void KConfigHeaderGenerator::start()
26{
27 KConfigCodeGeneratorBase::start();
28 startHeaderGuards();
29 createHeaders();
30
31 beginNamespaces();
32
33 createForwardDeclarations();
34
35 doClassDefinition();
36
37 endNamespaces();
38 endHeaderGuards();
39}
40
41void KConfigHeaderGenerator::doClassDefinition()
42{
43 stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << '\n';
44 startScope();
45
46 // Add Q_OBJECT macro if the config need signals.
47 if (!parseResult.signalList.isEmpty() || cfg().generateProperties) {
48 stream() << " Q_OBJECT\n";
49 }
50
51 if (cfg().qmlRegistration) {
52 stream() << " QML_ELEMENT\n";
53
54 if (cfg().singleton) {
55 stream() << " QML_SINGLETON\n";
56 }
57 }
58 stream() << " public:\n";
59 implementEnums();
60 createConstructor();
61 createDestructor();
62
63 for (const auto *entry : std::as_const(parseResult.entries)) {
64 const QString returnType = (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) ? enumType(entry, cfg().globalEnums) : cppType(entry->type);
65
66 createSetters(entry);
67 createProperties(entry, returnType);
68 createImmutableProperty(entry);
69 createGetters(entry, returnType);
70 createImmutableGetters(entry);
71 createDefaultValueMember(entry);
72 createItemAcessors(entry, returnType);
73 }
74
75 createSignals();
76 stream() << " protected:\n";
77 createSingleton();
78
79 // TODO: Move those to functions too.
80 if (parseResult.hasNonModifySignals) {
81 stream() << whitespace() << "bool usrSave() override;\n";
82 }
83
84 // Member variables
85 if (!cfg().memberVariables.isEmpty() //
86 && cfg().memberVariables != QLatin1String("private") //
87 && cfg().memberVariables != QLatin1String("dpointer")) {
88 stream() << " " << cfg().memberVariables << ":\n";
89 }
90
91 // Class Parameters
92 for (const auto &parameter : std::as_const(parseResult.parameters)) {
93 stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";\n";
94 }
95
96 createNonDPointerHelpers();
97 createDPointer();
98
99 if (cfg().customAddons) {
100 stream() << whitespace() << "// Include custom additions\n";
101 stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << "\"\n";
102 }
103
104 endScope(ScopeFinalizer::Semicolon);
105}
106
107void KConfigHeaderGenerator::createHeaders()
108{
109 addHeaders(cfg().headerIncludes);
110 if (cfg().headerIncludes.size()) {
111 stream() << '\n';
112 }
113
114 if (!cfg().singleton && parseResult.parameters.isEmpty()) {
115 addHeaders({QStringLiteral("qglobal.h")});
116 }
117
118 if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) {
119 addHeaders({QStringLiteral("kcoreconfigskeleton.h")});
120 } else {
121 addHeaders({QStringLiteral("kconfigskeleton.h")});
122 }
123
124 addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")});
125 if (!cfg().dpointer && parseResult.hasNonModifySignals) {
126 addHeaders({QStringLiteral("QSet")});
127 }
128
129 if (cfg().qmlRegistration) {
130 addHeaders({QStringLiteral("qqmlintegration.h")});
131 }
132
133 stream() << '\n';
134
135 addHeaders(parseResult.includes);
136 if (parseResult.includes.size()) {
137 stream() << '\n';
138 }
139}
140
141void KConfigHeaderGenerator::startHeaderGuards()
142{
143 const bool hasNamespace = !cfg().nameSpace.isEmpty();
144 const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper();
145 const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QString{};
146 const QString defineName = namespaceStr + cfg().className.toUpper() + QStringLiteral("_H");
147
148 stream() << "#ifndef " << defineName << '\n';
149 stream() << "#define " << defineName << '\n';
150 stream() << '\n';
151}
152
153void KConfigHeaderGenerator::endHeaderGuards()
154{
155 stream() << '\n';
156 stream() << "#endif";
157 stream() << '\n';
158 // HACK: Original files ended with two last newlines, add them.
159 stream() << '\n';
160}
161
162void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices)
163{
164 const QList<CfgEntry::Choice> chlist = choices.choices;
165
166 if (chlist.isEmpty()) {
167 return;
168 }
169
170 QStringList values;
171 for (const auto &choice : std::as_const(chlist)) {
172 values.append(choices.prefix + choice.name);
173 }
174
175 if (choices.name().isEmpty()) {
176 if (cfg().globalEnums) {
177 stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
178 if (cfg().generateProperties) {
179 stream() << whitespace() << "Q_ENUM(" << enumName(entry->name, entry->choices) << ")\n";
180 }
181 } else {
182 // Create an automatically named enum
183 stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << '\n';
184 stream() << whitespace() << "{\n";
185 stream() << whitespace() << " public:\n";
186 stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
187 stream() << whitespace() << "};\n";
188 }
189 } else if (!choices.external()) {
190 // Create a named enum
191 stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
192 }
193}
194
195void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values)
196{
197 if (values.isEmpty()) {
198 return;
199 }
200
201 if (cfg().globalEnums) {
202 // ### FIXME!!
203 // make the following string table an index-based string search!
204 // ###
205 stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };\n";
206 stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];\n";
207 } else {
208 stream() << whitespace() << "class " << enumName(entry->param) << '\n';
209 stream() << whitespace() << "{\n";
210 stream() << whitespace() << " public:\n";
211 stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
212 stream() << whitespace() << " static const char* const enumToString[];\n";
213 stream() << whitespace() << "};\n";
214 }
215}
216
217void KConfigHeaderGenerator::implementEnums()
218{
219 if (!parseResult.entries.size()) {
220 return;
221 }
222
223 for (const auto *entry : std::as_const(parseResult.entries)) {
224 const CfgEntry::Choices &choices = entry->choices;
225 const QStringList values = entry->paramValues;
226
227 implementChoiceEnums(entry, choices);
228 implementValueEnums(entry, values);
229 }
230 stream() << '\n';
231}
232
233void KConfigHeaderGenerator::createSignals()
234{
235 // Signal definition.
236 if (parseResult.signalList.isEmpty()) {
237 return;
238 }
239
240 stream() << "\n enum {\n";
241
242 // HACK: Use C-Style for add a comma in all but the last element,
243 // just to make the source generated code equal to the old one.
244 // When we are sure, revert this to a range-based-for and just add
245 // a last comma, as it's valid c++.
246 for (int i = 0, end = parseResult.signalList.size(); i < end; i++) {
247 auto signal = parseResult.signalList.at(i);
248 stream() << whitespace() << " " << signalEnumName(signal.name) << " = " << (i + 1);
249 if (i != end - 1) {
250 stream() << ",\n";
251 }
252 }
253 stream() << '\n';
254 stream() << whitespace() << "};\n\n";
255
256 stream() << " Q_SIGNALS:";
257 for (const Signal &signal : std::as_const(parseResult.signalList)) {
258 stream() << '\n';
259 if (!signal.label.isEmpty()) {
260 stream() << whitespace() << "/**\n";
261 stream() << whitespace() << " " << signal.label << '\n';
262 stream() << whitespace() << "*/\n";
263 }
264 stream() << whitespace() << "void " << signal.name << "(";
265
266 auto it = signal.arguments.cbegin();
267 const auto itEnd = signal.arguments.cend();
268 while (it != itEnd) {
269 Param argument = *it;
270 QString type = param(argument.type);
271 if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
272 for (const auto *entry : std::as_const(parseResult.entries)) {
273 if (entry->name == argument.name) {
274 type = enumType(entry, cfg().globalEnums);
275 break;
276 }
277 }
278 }
279 stream() << type << " " << argument.name;
280 if (++it != itEnd) {
281 stream() << ", ";
282 }
283 }
284 stream() << ");\n";
285 }
286 stream() << '\n';
287
288 stream() << " private:\n";
289 stream() << whitespace() << "void itemChanged(quint64 signalFlag);\n";
290 stream() << '\n';
291}
292
293void KConfigHeaderGenerator::createDPointer()
294{
295 if (!cfg().dpointer) {
296 return;
297 }
298
299 // use a private class for both member variables and items
300 stream() << " private:\n";
301 for (const auto *entry : std::as_const(parseResult.entries)) {
302 if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
303 stream() << whitespace() << "";
304 if (cfg().staticAccessors) {
305 stream() << "static ";
306 }
307 stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
308 if (!entry->param.isEmpty()) {
309 stream() << " " << cppType(entry->paramType) << " i ";
310 }
311 stream() << ")" << Const() << ";\n";
312 }
313 }
314 stream() << whitespace() << "" << cfg().className << "Private *d;\n";
315}
316
317void KConfigHeaderGenerator::createConstructor()
318{
319 if (cfg().singleton) {
320 stream() << whitespace() << "static " << cfg().className << " *self();\n";
321
322 if (cfg().qmlRegistration) {
323 stream() << whitespace() << "static " << cfg().className << " *create(QQmlEngine *, QJSEngine *);\n";
324 }
325
326 if (parseResult.cfgFileNameArg) {
327 stream() << whitespace() << "static void instance(const QString& cfgfilename);\n";
328 stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);\n";
329 }
330 return;
331 }
332
333 stream() << whitespace() << "" << cfg().className << "(";
334 if (parseResult.cfgFileNameArg) {
335 if (cfg().forceStringFilename) {
336 stream() << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", ");
337 } else if (parseResult.cfgStateConfig) {
338 stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openStateConfig()" : ", ");
339 } else {
340 stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", ");
341 }
342 }
343 if (cfg().forceStringFilename && parseResult.cfgStateConfig) {
344 std::cerr << "One can not use ForceStringFilename and use the stateConfig attribute, consider "
345 "removing the ForceStringFilename kcfgc option if you want to use state data"
346 << std::endl;
347 }
348
349 bool first = true;
350 for (const auto &parameter : std::as_const(parseResult.parameters)) {
351 if (first) {
352 first = false;
353 } else {
354 stream() << ",";
355 }
356
357 stream() << " " << param(parameter.type) << " " << parameter.name;
358 }
359
360 if (cfg().parentInConstructor) {
361 if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
362 stream() << ",";
363 }
364 stream() << " QObject *parent = nullptr";
365 }
366 stream() << " );\n";
367}
368
369void KConfigHeaderGenerator::createDestructor()
370{
371 stream() << whitespace() << "~" << cfg().className << "() override;\n\n";
372}
373
374void KConfigHeaderGenerator::createForwardDeclarations()
375{
376 // Private class declaration
377 if (cfg().dpointer) {
378 stream() << "class " << cfg().className << "Private;\n\n";
379 }
380
381 if (cfg().qmlRegistration && cfg().singleton) {
382 stream() << "class QQmlEngine;\n";
383 stream() << "class QJSEngine;\n\n";
384 }
385}
386
387void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString &returnType)
388{
389 if (!cfg().generateProperties) {
390 return;
391 }
392 stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name);
393 stream() << " READ " << getFunction(entry->name);
394
395 if (cfg().allMutators || cfg().mutators.contains(entry->name)) {
396 const QString signal = changeSignalName(entry->name);
397 stream() << " WRITE " << setFunction(entry->name);
398 stream() << " NOTIFY " << signal;
399
400 // If we have the modified signal, we'll also need
401 // the changed signal as well
402 Signal s;
403 s.name = signal;
404 s.modify = true;
405 parseResult.signalList.append(s);
406 } else {
407 stream() << " CONSTANT";
408 }
409 stream() << ")\n";
410}
411
412void KConfigHeaderGenerator::createImmutableProperty(const CfgEntry *entry)
413{
414 if (!cfg().generateProperties) {
415 return;
416 }
417 stream() << whitespace();
418 stream() << "Q_PROPERTY(bool " << immutableFunction(entry->name);
419 stream() << " READ " << immutableFunction(entry->name);
420 stream() << " CONSTANT)\n";
421}
422
423void KConfigHeaderGenerator::createSetters(const CfgEntry *entry)
424{
425 // Manipulator
426 if (!cfg().allMutators && !cfg().mutators.contains(entry->name)) {
427 return;
428 }
429
430 stream() << whitespace() << "/**\n";
431 stream() << whitespace() << " Set " << entry->label << '\n';
432 stream() << whitespace() << "*/\n";
433
434 if (cfg().staticAccessors) {
435 stream() << whitespace() << "static\n";
436 }
437
438 stream() << whitespace() << "void " << setFunction(entry->name) << "( ";
439 if (!entry->param.isEmpty()) {
440 stream() << cppType(entry->paramType) << " i, ";
441 }
442
443 stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") ? enumType(entry, cfg().globalEnums) : param(entry->type));
444
445 stream() << " v )";
446
447 // function body inline only if not using dpointer
448 // for BC mode
449 if (!cfg().dpointer) {
450 stream() << '\n';
451 startScope();
452 memberMutatorBody(entry);
453 endScope();
454 stream() << '\n';
455 } else {
456 stream() << ";\n\n";
457 }
458}
459
460void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString &returnType)
461{
462 // Accessor
463 stream() << whitespace() << "/**\n";
464 stream() << whitespace() << " Get " << entry->label << '\n';
465 stream() << whitespace() << "*/\n";
466 if (cfg().staticAccessors) {
467 stream() << whitespace() << "static\n";
468 }
469 stream() << whitespace() << "";
470 stream() << returnType;
471 stream() << " " << getFunction(entry->name) << "(";
472 if (!entry->param.isEmpty()) {
473 stream() << " " << cppType(entry->paramType) << " i ";
474 }
475 stream() << ")" << Const();
476
477 // function body inline only if not using dpointer
478 // for BC mode
479 if (!cfg().dpointer) {
480 stream() << '\n';
481 startScope();
482 stream() << whitespace() << memberAccessorBody(entry, cfg().globalEnums);
483 endScope();
484 stream() << '\n';
485 } else {
486 stream() << ";\n\n";
487 }
488}
489
490void KConfigHeaderGenerator::createImmutableGetters(const CfgEntry *entry)
491{
492 stream() << whitespace() << "/**\n";
493 stream() << whitespace() << " Is " << entry->label << " Immutable\n";
494 stream() << whitespace() << "*/\n";
495 // Immutable
496 if (cfg().staticAccessors) {
497 stream() << whitespace() << "static\n";
498 }
499 stream() << whitespace() << "";
500 stream() << "bool " << immutableFunction(entry->name) << "(";
501 if (!entry->param.isEmpty()) {
502 stream() << " " << cppType(entry->paramType) << " i ";
503 }
504 stream() << ")" << Const();
505 // function body inline only if not using dpointer
506 // for BC mode
507 if (!cfg().dpointer) {
508 stream() << '\n';
509 startScope();
510 memberImmutableBody(entry, cfg().globalEnums);
511 endScope();
512 stream() << '\n';
513 } else {
514 stream() << ";\n\n";
515 }
516}
517
518void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString &returnType)
519{
520 Q_UNUSED(returnType)
521
522 // Item accessor
523 if (!cfg().itemAccessors) {
524 return;
525 }
526
527 const QString declType = entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
528
529 stream() << whitespace() << "/**\n";
530 stream() << whitespace() << " Get Item object corresponding to " << entry->name << "()" << '\n';
531 stream() << whitespace() << "*/\n";
532 stream() << whitespace() << declType << " *" << getFunction(entry->name) << "Item(";
533 if (!entry->param.isEmpty()) {
534 stream() << " " << cppType(entry->paramType) << " i ";
535 }
536 stream() << ")";
537 if (!cfg().dpointer) {
538 stream() << '\n';
539 startScope();
540 stream() << whitespace() << itemAccessorBody(entry, cfg());
541 endScope();
542 } else {
543 stream() << ";\n";
544 }
545
546 stream() << '\n';
547}
548
549void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry)
550{
551 // Default value Accessor
552 if (!((cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) && !entry->defaultValue.isEmpty())) {
553 return;
554 }
555 stream() << whitespace() << "/**\n";
556 stream() << whitespace() << " Get " << entry->label << " default value\n";
557 stream() << whitespace() << "*/\n";
558 if (cfg().staticAccessors) {
559 stream() << whitespace() << "static\n";
560 }
561 stream() << whitespace() << "";
562 if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
563 stream() << enumType(entry, cfg().globalEnums);
564 } else {
565 stream() << cppType(entry->type);
566 }
567 stream() << " " << getDefaultFunction(entry->name) << "(";
568 if (!entry->param.isEmpty()) {
569 stream() << " " << cppType(entry->paramType) << " i ";
570 }
571 stream() << ")" << Const() << '\n';
572 stream() << whitespace() << "{\n";
573 stream() << whitespace() << " return ";
574 if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
575 stream() << "static_cast<" << enumType(entry, cfg().globalEnums) << ">(";
576 }
577 stream() << getDefaultFunction(entry->name) << "_helper(";
578 if (!entry->param.isEmpty()) {
579 stream() << " i ";
580 }
581 stream() << ")";
582 if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
583 stream() << ")";
584 }
585 stream() << ";\n";
586 stream() << whitespace() << "}\n";
587 stream() << '\n';
588}
589
590void KConfigHeaderGenerator::createSingleton()
591{
592 // Private constructor for singleton
593 if (!cfg().singleton) {
594 return;
595 }
596
597 stream() << whitespace() << "" << cfg().className << "(";
598 if (parseResult.cfgFileNameArg) {
599 stream() << "KSharedConfig::Ptr config";
600 }
601 if (cfg().parentInConstructor) {
602 if (parseResult.cfgFileNameArg) {
603 stream() << ", ";
604 }
605 stream() << "QObject *parent = nullptr";
606 }
607 stream() << ");\n";
608 stream() << whitespace() << "friend class " << cfg().className << "Helper;\n\n";
609}
610
611void KConfigHeaderGenerator::createNonDPointerHelpers()
612{
613 if (cfg().memberVariables == QLatin1String("dpointer")) {
614 return;
615 }
616
617 QString group;
618 for (const auto *entry : std::as_const(parseResult.entries)) {
619 if (entry->group != group) {
620 group = entry->group;
621 stream() << '\n';
622 stream() << whitespace() << "// " << group << '\n';
623 }
624 stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg());
625 if (!entry->param.isEmpty()) {
626 stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
627 }
628 stream() << ";\n";
629
630 if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
631 stream() << whitespace() << "";
632 if (cfg().staticAccessors) {
633 stream() << "static ";
634 }
635 stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
636 if (!entry->param.isEmpty()) {
637 stream() << " " << cppType(entry->paramType) << " i ";
638 }
639 stream() << ")" << Const() << ";\n";
640 }
641 }
642
643 stream() << "\n private:\n";
644 if (cfg().itemAccessors) {
645 for (const auto *entry : std::as_const(parseResult.entries)) {
646 const QString declType =
647 entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
648 stream() << whitespace() << declType << " *" << itemVar(entry, cfg());
649 if (!entry->param.isEmpty()) {
650 stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
651 }
652 stream() << ";\n";
653 }
654 }
655
656 if (parseResult.hasNonModifySignals) {
657 stream() << whitespace() << "QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
658 }
659}
Configuration Compiler Configuration.
Type type(const QSqlDatabase &db)
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
const_iterator cbegin() const const
bool isEmpty() const const
QString toUpper() const const
QString join(QChar separator) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:18:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.