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

KDE's Doxygen guidelines are available online.