KConfig

KConfigCodeGeneratorBase.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 "KConfigCodeGeneratorBase.h"
15 
16 #include <QFileInfo>
17 #include <QLatin1Char>
18 
19 #include <QDebug>
20 #include <ostream>
21 
22 #include <iostream>
23 
24 KConfigCodeGeneratorBase::KConfigCodeGeneratorBase(const QString &inputFile,
25  const QString &baseDir,
26  const QString &fileName,
27  const KConfigParameters &parameters,
28  ParseResult &parseResult)
29  : parseResult(parseResult)
30  , m_inputFile(inputFile)
31  , m_baseDir(baseDir)
32  , m_fileName(fileName)
33  , m_cfg(parameters)
34 {
35  m_file.setFileName(m_fileName);
36  if (!m_file.open(QIODevice::WriteOnly)) {
37  std::cerr << "Can not open '" << qPrintable(m_fileName) << "for writing." << std::endl;
38  exit(1);
39  }
40  m_stream.setDevice(&m_file);
41 
42  if (m_cfg.staticAccessors) {
43  m_this = QStringLiteral("self()->");
44  } else {
45  m_const = QStringLiteral(" const");
46  }
47 }
48 
49 KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase()
50 {
51  save();
52 }
53 
54 void KConfigCodeGeneratorBase::save()
55 {
56  m_file.close();
57 }
58 
59 // TODO: Remove this weird logic and adapt the testcases
60 void KConfigCodeGeneratorBase::indent()
61 {
62  if (m_indentLevel >= 4) {
63  m_indentLevel += 2;
64  } else {
65  m_indentLevel += 4;
66  }
67 }
68 
69 void KConfigCodeGeneratorBase::unindent()
70 {
71  if (m_indentLevel > 4) {
72  m_indentLevel -= 2;
73  } else {
74  m_indentLevel -= 4;
75  }
76 }
77 
78 QString KConfigCodeGeneratorBase::whitespace() const
79 {
80  QString spaces;
81  for (int i = 0; i < m_indentLevel; i++) {
82  spaces.append(QLatin1Char(' '));
83  }
84  return spaces;
85 }
86 
87 void KConfigCodeGeneratorBase::startScope()
88 {
89  m_stream << whitespace() << QLatin1Char('{');
90  m_stream << '\n';
91  indent();
92 }
93 
94 void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer)
95 {
96  unindent();
97  m_stream << whitespace() << QLatin1Char('}');
98  if (finalizer == ScopeFinalizer::Semicolon) {
99  m_stream << ';';
100  }
101  m_stream << '\n';
102 }
103 
105 {
106  const QString m_fileName = QFileInfo(m_inputFile).fileName();
107  m_stream << "// This file is generated by kconfig_compiler_kf6 from " << m_fileName << ".kcfg"
108  << ".\n";
109  m_stream << "// All changes you do to this file will be lost.\n";
110 }
111 
112 void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList)
113 {
114  for (const auto &include : headerList) {
115  if (include.startsWith(QLatin1Char('"'))) {
116  m_stream << "#include " << include << '\n';
117  } else {
118  m_stream << "#include <" << include << ">\n";
119  }
120  }
121 }
122 
123 // adds as many 'namespace foo {' lines to p_out as
124 // there are namespaces in p_ns
125 void KConfigCodeGeneratorBase::beginNamespaces()
126 {
127  if (!m_cfg.nameSpace.isEmpty()) {
128  const auto nameSpaceList = m_cfg.nameSpace.split(QStringLiteral("::"));
129  for (const QString &ns : nameSpaceList) {
130  m_stream << "namespace " << ns << " {\n";
131  }
132  m_stream << '\n';
133  }
134 }
135 
136 // adds as many '}' lines to p_out as
137 // there are namespaces in p_ns
138 void KConfigCodeGeneratorBase::endNamespaces()
139 {
140  if (!m_cfg.nameSpace.isEmpty()) {
141  m_stream << '\n';
142  const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1;
143  for (int i = 0; i < namespaceCount; ++i) {
144  m_stream << "}\n";
145  }
146  }
147 }
148 
149 // returns the member accessor implementation
150 // which should go in the h file if inline
151 // or the cpp file if not inline
152 QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const
153 {
154  QString result;
155  QTextStream out(&result, QIODevice::WriteOnly);
156  QString n = e->name;
157  QString t = e->type;
158  bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum");
159 
160  out << "return ";
161  if (useEnumType) {
162  out << "static_cast<" << enumType(e, globalEnums) << ">(";
163  }
164  out << m_this << varPath(n, m_cfg);
165  if (!e->param.isEmpty()) {
166  out << "[i]";
167  }
168  if (useEnumType) {
169  out << ")";
170  }
171  out << ";\n";
172 
173  return result;
174 }
175 
176 void KConfigCodeGeneratorBase::memberImmutableBody(const CfgEntry *e, bool globalEnums)
177 {
178  stream() << whitespace() << "return " << m_this << "isImmutable( QStringLiteral( \"";
179  if (!e->param.isEmpty()) {
180  stream() << QString(e->paramName).replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("%1")) << "\" ).arg( ";
181  if (e->paramType == QLatin1String("Enum")) {
182  stream() << "QLatin1String( ";
183 
184  if (globalEnums) {
185  stream() << enumName(e->param) << "ToString[i]";
186  } else {
187  stream() << enumName(e->param) << "::enumToString[i]";
188  }
189 
190  stream() << " )";
191  } else {
192  stream() << "i";
193  }
194  stream() << " )";
195  } else {
196  stream() << e->name << "\" )";
197  }
198  stream() << " );\n";
199 }
200 
201 void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression)
202 {
203  const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
204 
205  m_stream << whitespace() << "if (";
206  if (hasBody) {
207  m_stream << "v != " << varExpression << " && ";
208  }
209 
210  const auto immutablefunction = immutableFunction(e->name, m_cfg.dpointer ? m_cfg.className : QString{});
211  m_stream << "!" << m_this << immutablefunction << "(";
212  if (!e->param.isEmpty()) {
213  m_stream << " i ";
214  }
215  m_stream << "))";
216 }
217 
218 void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e)
219 {
220  // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation.
221  if (!e->min.isEmpty()) {
222  if (e->min != QLatin1String("0") || !isUnsigned(e->type)) { // skip writing "if uint<0" (#187579)
223  m_stream << whitespace() << "if (v < " << e->min << ")\n";
224  m_stream << whitespace() << "{\n";
225  m_stream << whitespace();
226  addDebugMethod(m_stream, m_cfg, e->name);
227  m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n";
228  m_stream << whitespace() << " v = " << e->min << ";\n";
229  m_stream << whitespace() << "}\n";
230  }
231  }
232 
233  if (!e->max.isEmpty()) {
234  m_stream << '\n';
235  m_stream << whitespace() << "if (v > " << e->max << ")\n";
236  m_stream << whitespace() << "{\n";
237  m_stream << whitespace();
238  addDebugMethod(m_stream, m_cfg, e->name);
239  m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n";
240  m_stream << whitespace() << " v = " << e->max << ";\n";
241  m_stream << whitespace() << "}\n\n";
242  }
243 
244  const QString varExpression = m_this + varPath(e->name, m_cfg) + (e->param.isEmpty() ? QString{} : QStringLiteral("[i]"));
245 
246  // TODO: Remove this `hasBody` logic, always use an '{' for the if.
247  const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
248 
249  // m_this call creates an `if (someTest ...) that's just to long to throw over the code.
250  createIfSetLogic(e, varExpression);
251  m_stream << (hasBody ? " {" : "") << '\n';
252  m_stream << whitespace() << " " << varExpression << " = v;\n";
253 
254  const auto listSignal = e->signalList;
255  for (const Signal &signal : std::as_const(listSignal)) {
256  if (signal.modify) {
257  m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();\n";
258  } else {
259  m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) << ".insert(" << signalEnumName(signal.name) << ");\n";
260  }
261  }
262  if (hasBody) {
263  m_stream << whitespace() << "}\n";
264  }
265 }
Configuration Compiler Configuration.
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int count(const T &value) const const
Q_SCRIPTABLE Q_NOREPLY void start()
QString fileName() const const
QString & replace(int position, int n, QChar after)
QString & append(QChar ch)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
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.