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 
20 KConfigHeaderGenerator::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 
26 {
28  startHeaderGuards();
29  createHeaders();
30 
31  beginNamespaces();
32 
33  createForwardDeclarations();
34 
35  doClassDefinition();
36 
37  endNamespaces();
38  endHeaderGuards();
39 }
40 
41 void 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(parseResult.entries)) {
56  const QString returnType = (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) ? enumType(entry, cfg().globalEnums) : cppType(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(parseResult.parameters)) {
85  stream() << whitespace() << "" << cppType(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(ScopeFinalizer::Semicolon);
97 }
98 
99 void KConfigHeaderGenerator::createHeaders()
100 {
101  addHeaders(cfg().headerIncludes);
102  if (cfg().headerIncludes.size()) {
103  stream() << '\n';
104  }
105 
106  if (!cfg().singleton && parseResult.parameters.isEmpty()) {
107  addHeaders({QStringLiteral("qglobal.h")});
108  }
109 
110  if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) {
111  addHeaders({QStringLiteral("kcoreconfigskeleton.h")});
112  } else {
113  addHeaders({QStringLiteral("kconfigskeleton.h")});
114  }
115 
116  addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")});
117  if (!cfg().dpointer && parseResult.hasNonModifySignals) {
118  addHeaders({QStringLiteral("QSet")});
119  }
120  stream() << '\n';
121 
122  addHeaders(parseResult.includes);
123  if (parseResult.includes.size()) {
124  stream() << '\n';
125  }
126 }
127 
128 void KConfigHeaderGenerator::startHeaderGuards()
129 {
130  const bool hasNamespace = !cfg().nameSpace.isEmpty();
131  const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), 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 
140 void 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 
149 void 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(chlist)) {
159  values.append(choices.prefix + choice.name);
160  }
161 
162  if (choices.name().isEmpty()) {
163  if (cfg().globalEnums) {
164  stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
165  if (cfg().generateProperties) {
166  stream() << whitespace() << "Q_ENUM(" << enumName(entry->name, entry->choices) << ")\n";
167  }
168  } else {
169  // Create an automatically named enum
170  stream() << whitespace() << "class " << enumName(entry->name, 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(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
179  }
180 }
181 
182 void 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(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };\n";
193  stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];\n";
194  } else {
195  stream() << whitespace() << "class " << enumName(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 
204 void KConfigHeaderGenerator::implementEnums()
205 {
206  if (!parseResult.entries.size()) {
207  return;
208  }
209 
210  for (const auto *entry : std::as_const(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 
220 void 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(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(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(argument.type);
258  if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
259  for (const auto *entry : std::as_const(parseResult.entries)) {
260  if (entry->name == argument.name) {
261  type = enumType(entry, 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 
280 void 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(parseResult.entries)) {
289  if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
290  stream() << whitespace() << "";
291  if (cfg().staticAccessors) {
292  stream() << "static ";
293  }
294  stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
295  if (!entry->param.isEmpty()) {
296  stream() << " " << cppType(entry->paramType) << " i ";
297  }
298  stream() << ")" << Const() << ";\n";
299  }
300  }
301  stream() << whitespace() << "" << cfg().className << "Private *d;\n";
302 }
303 
304 void 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(parseResult.parameters)) {
333  if (first) {
334  first = false;
335  } else {
336  stream() << ",";
337  }
338 
339  stream() << " " << param(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 
351 void KConfigHeaderGenerator::createDestructor()
352 {
353  stream() << whitespace() << "~" << cfg().className << "() override;\n\n";
354 }
355 
356 void KConfigHeaderGenerator::createForwardDeclarations()
357 {
358  // Private class declaration
359  if (cfg().dpointer) {
360  stream() << "class " << cfg().className << "Private;\n\n";
361  }
362 }
363 
364 void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString &returnType)
365 {
366  if (!cfg().generateProperties) {
367  return;
368  }
369  stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name);
370  stream() << " READ " << getFunction(entry->name);
371 
372  if (cfg().allMutators || cfg().mutators.contains(entry->name)) {
373  const QString signal = changeSignalName(entry->name);
374  stream() << " WRITE " << setFunction(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(s);
383  } else {
384  stream() << " CONSTANT";
385  }
386  stream() << ")\n";
387 }
388 
389 void KConfigHeaderGenerator::createImmutableProperty(const CfgEntry *entry)
390 {
391  if (!cfg().generateProperties) {
392  return;
393  }
394  stream() << whitespace();
395  stream() << "Q_PROPERTY(bool " << immutableFunction(entry->name);
396  stream() << " READ " << immutableFunction(entry->name);
397  stream() << " CONSTANT)\n";
398 }
399 
400 void KConfigHeaderGenerator::createSetters(const CfgEntry *entry)
401 {
402  // Manipulator
403  if (!cfg().allMutators && !cfg().mutators.contains(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(entry->name) << "( ";
416  if (!entry->param.isEmpty()) {
417  stream() << cppType(entry->paramType) << " i, ";
418  }
419 
420  stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") ? enumType(entry, cfg().globalEnums) : param(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(entry);
430  endScope();
431  stream() << '\n';
432  } else {
433  stream() << ";\n\n";
434  }
435 }
436 
437 void 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(entry->name) << "(";
449  if (!entry->param.isEmpty()) {
450  stream() << " " << cppType(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(entry, cfg().globalEnums);
460  endScope();
461  stream() << '\n';
462  } else {
463  stream() << ";\n\n";
464  }
465 }
466 
467 void 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(entry->name) << "(";
478  if (!entry->param.isEmpty()) {
479  stream() << " " << cppType(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(entry, cfg().globalEnums);
488  endScope();
489  stream() << '\n';
490  } else {
491  stream() << ";\n\n";
492  }
493 }
494 
495 void 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(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(entry->name) << "Item(";
510  if (!entry->param.isEmpty()) {
511  stream() << " " << cppType(entry->paramType) << " i ";
512  }
513  stream() << ")";
514  if (!cfg().dpointer) {
515  stream() << '\n';
516  startScope();
517  stream() << whitespace() << itemAccessorBody(entry, cfg());
518  endScope();
519  } else {
520  stream() << ";\n";
521  }
522 
523  stream() << '\n';
524 }
525 
526 void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry)
527 {
528  // Default value Accessor
529  if (!((cfg().allDefaultGetters || cfg().defaultGetters.contains(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(entry, cfg().globalEnums);
541  } else {
542  stream() << cppType(entry->type);
543  }
544  stream() << " " << getDefaultFunction(entry->name) << "(";
545  if (!entry->param.isEmpty()) {
546  stream() << " " << cppType(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(entry, cfg().globalEnums) << ">(";
553  }
554  stream() << getDefaultFunction(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 
567 void 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 
588 void KConfigHeaderGenerator::createNonDPointerHelpers()
589 {
590  if (cfg().memberVariables == QLatin1String("dpointer")) {
591  return;
592  }
593 
594  QString group;
595  for (const auto *entry : std::as_const(parseResult.entries)) {
596  if (entry->group != group) {
597  group = entry->group;
598  stream() << '\n';
599  stream() << whitespace() << "// " << group << '\n';
600  }
601  stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg());
602  if (!entry->param.isEmpty()) {
603  stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
604  }
605  stream() << ";\n";
606 
607  if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
608  stream() << whitespace() << "";
609  if (cfg().staticAccessors) {
610  stream() << "static ";
611  }
612  stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
613  if (!entry->param.isEmpty()) {
614  stream() << " " << cppType(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(parseResult.entries)) {
623  const QString declType =
624  entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
625  stream() << whitespace() << declType << " *" << itemVar(entry, cfg());
626  if (!entry->param.isEmpty()) {
627  stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
628  }
629  stream() << ";\n";
630  }
631  }
632 
633  if (parseResult.hasNonModifySignals) {
634  stream() << whitespace() << "QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
635  }
636 }
void append(const T &value)
QString toUpper() const const
Configuration Compiler Configuration.
Type type(const QSqlDatabase &db)
Q_SCRIPTABLE Q_NOREPLY void start()
bool isEmpty() const const
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
bool isEmpty() const const
QString join(const QString &separator) const const
QString::const_iterator cbegin() const const
QString & append(QChar ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 04:07:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.