KConfig

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

KDE's Doxygen guidelines are available online.