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

source code of kconfig/src/kconfig_compiler/KConfigHeaderGenerator.cpp