Warning, file /frameworks/kconfig/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     This file is part of the KDE libraries.
0003 
0004     SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
0005     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0006     SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
0007     SPDX-FileCopyrightText: 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
0008     SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
0009     SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "KConfigCodeGeneratorBase.h"
0015 
0016 #include <QFileInfo>
0017 #include <QLatin1Char>
0018 
0019 #include <QDebug>
0020 #include <ostream>
0021 
0022 #include <iostream>
0023 
0024 KConfigCodeGeneratorBase::KConfigCodeGeneratorBase(const QString &inputFile,
0025                                                    const QString &baseDir,
0026                                                    const QString &fileName,
0027                                                    const KConfigParameters &parameters,
0028                                                    ParseResult &parseResult)
0029     : parseResult(parseResult)
0030     , m_inputFile(inputFile)
0031     , m_baseDir(baseDir)
0032     , m_fileName(fileName)
0033     , m_cfg(parameters)
0034 {
0035     m_file.setFileName(m_fileName);
0036     if (!m_file.open(QIODevice::WriteOnly)) {
0037         std::cerr << "Can not open '" << qPrintable(m_fileName) << "for writing." << std::endl;
0038         exit(1);
0039     }
0040     m_stream.setDevice(&m_file);
0041 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0042     m_stream.setCodec("utf-8");
0043 #endif
0044 
0045     if (m_cfg.staticAccessors) {
0046         m_this = QStringLiteral("self()->");
0047     } else {
0048         m_const = QStringLiteral(" const");
0049     }
0050 }
0051 
0052 KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase()
0053 {
0054     save();
0055 }
0056 
0057 void KConfigCodeGeneratorBase::save()
0058 {
0059     m_file.close();
0060 }
0061 
0062 // TODO: Remove this weird logic and adapt the testcases
0063 void KConfigCodeGeneratorBase::indent()
0064 {
0065     if (m_indentLevel >= 4) {
0066         m_indentLevel += 2;
0067     } else {
0068         m_indentLevel += 4;
0069     }
0070 }
0071 
0072 void KConfigCodeGeneratorBase::unindent()
0073 {
0074     if (m_indentLevel > 4) {
0075         m_indentLevel -= 2;
0076     } else {
0077         m_indentLevel -= 4;
0078     }
0079 }
0080 
0081 QString KConfigCodeGeneratorBase::whitespace() const
0082 {
0083     QString spaces;
0084     for (int i = 0; i < m_indentLevel; i++) {
0085         spaces.append(QLatin1Char(' '));
0086     }
0087     return spaces;
0088 }
0089 
0090 void KConfigCodeGeneratorBase::startScope()
0091 {
0092     m_stream << whitespace() << QLatin1Char('{');
0093     m_stream << '\n';
0094     indent();
0095 }
0096 
0097 void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer)
0098 {
0099     unindent();
0100     m_stream << whitespace() << QLatin1Char('}');
0101     if (finalizer == ScopeFinalizer::Semicolon) {
0102         m_stream << ';';
0103     }
0104     m_stream << '\n';
0105 }
0106 
0107 void KConfigCodeGeneratorBase::start()
0108 {
0109     const QString m_fileName = QFileInfo(m_inputFile).fileName();
0110     m_stream << "// This file is generated by kconfig_compiler_kf5 from " << m_fileName << ".kcfg"
0111              << ".\n";
0112     m_stream << "// All changes you do to this file will be lost.\n";
0113 }
0114 
0115 void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList)
0116 {
0117     for (const auto &include : headerList) {
0118         if (include.startsWith(QLatin1Char('"'))) {
0119             m_stream << "#include " << include << '\n';
0120         } else {
0121             m_stream << "#include <" << include << ">\n";
0122         }
0123     }
0124 }
0125 
0126 // adds as many 'namespace foo {' lines to p_out as
0127 // there are namespaces in p_ns
0128 void KConfigCodeGeneratorBase::beginNamespaces()
0129 {
0130     if (!m_cfg.nameSpace.isEmpty()) {
0131         const auto nameSpaceList = m_cfg.nameSpace.split(QStringLiteral("::"));
0132         for (const QString &ns : nameSpaceList) {
0133             m_stream << "namespace " << ns << " {\n";
0134         }
0135         m_stream << '\n';
0136     }
0137 }
0138 
0139 // adds as many '}' lines to p_out as
0140 // there are namespaces in p_ns
0141 void KConfigCodeGeneratorBase::endNamespaces()
0142 {
0143     if (!m_cfg.nameSpace.isEmpty()) {
0144         m_stream << '\n';
0145         const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1;
0146         for (int i = 0; i < namespaceCount; ++i) {
0147             m_stream << "}\n";
0148         }
0149     }
0150 }
0151 
0152 // returns the member accessor implementation
0153 // which should go in the h file if inline
0154 // or the cpp file if not inline
0155 QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const
0156 {
0157     QString result;
0158     QTextStream out(&result, QIODevice::WriteOnly);
0159     QString n = e->name;
0160     QString t = e->type;
0161     bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum");
0162 
0163     out << "return ";
0164     if (useEnumType) {
0165         out << "static_cast<" << enumType(e, globalEnums) << ">(";
0166     }
0167     out << m_this << varPath(n, m_cfg);
0168     if (!e->param.isEmpty()) {
0169         out << "[i]";
0170     }
0171     if (useEnumType) {
0172         out << ")";
0173     }
0174     out << ";\n";
0175 
0176     return result;
0177 }
0178 
0179 void KConfigCodeGeneratorBase::memberImmutableBody(const CfgEntry *e, bool globalEnums)
0180 {
0181     stream() << whitespace() << "return " << m_this << "isImmutable( QStringLiteral( \"";
0182     if (!e->param.isEmpty()) {
0183         stream() << QString(e->paramName).replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("%1")) << "\" ).arg( ";
0184         if (e->paramType == QLatin1String("Enum")) {
0185             stream() << "QLatin1String( ";
0186 
0187             if (globalEnums) {
0188                 stream() << enumName(e->param) << "ToString[i]";
0189             } else {
0190                 stream() << enumName(e->param) << "::enumToString[i]";
0191             }
0192 
0193             stream() << " )";
0194         } else {
0195             stream() << "i";
0196         }
0197         stream() << " )";
0198     } else {
0199         stream() << e->name << "\" )";
0200     }
0201     stream() << " );\n";
0202 }
0203 
0204 void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression)
0205 {
0206     const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
0207 
0208     m_stream << whitespace() << "if (";
0209     if (hasBody) {
0210         m_stream << "v != " << varExpression << " && ";
0211     }
0212 
0213     const auto immutablefunction = immutableFunction(e->name, m_cfg.dpointer ? m_cfg.className : QString{});
0214     m_stream << "!" << m_this << immutablefunction << "(";
0215     if (!e->param.isEmpty()) {
0216         m_stream << " i ";
0217     }
0218     m_stream << "))";
0219 }
0220 
0221 void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e)
0222 {
0223     // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation.
0224     if (!e->min.isEmpty()) {
0225         if (e->min != QLatin1String("0") || !isUnsigned(e->type)) { // skip writing "if uint<0" (#187579)
0226             m_stream << whitespace() << "if (v < " << e->min << ")\n";
0227             m_stream << whitespace() << "{\n";
0228             m_stream << whitespace();
0229             addDebugMethod(m_stream, m_cfg, e->name);
0230             m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n";
0231             m_stream << whitespace() << "  v = " << e->min << ";\n";
0232             m_stream << whitespace() << "}\n";
0233         }
0234     }
0235 
0236     if (!e->max.isEmpty()) {
0237         m_stream << '\n';
0238         m_stream << whitespace() << "if (v > " << e->max << ")\n";
0239         m_stream << whitespace() << "{\n";
0240         m_stream << whitespace();
0241         addDebugMethod(m_stream, m_cfg, e->name);
0242         m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n";
0243         m_stream << whitespace() << "  v = " << e->max << ";\n";
0244         m_stream << whitespace() << "}\n\n";
0245     }
0246 
0247     const QString varExpression = m_this + varPath(e->name, m_cfg) + (e->param.isEmpty() ? QString{} : QStringLiteral("[i]"));
0248 
0249     // TODO: Remove this `hasBody` logic, always use an '{' for the if.
0250     const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
0251 
0252     // m_this call creates an `if (someTest ...) that's just to long to throw over the code.
0253     createIfSetLogic(e, varExpression);
0254     m_stream << (hasBody ? " {" : "") << '\n';
0255     m_stream << whitespace() << "  " << varExpression << " = v;\n";
0256 
0257     const auto listSignal = e->signalList;
0258     for (const Signal &signal : std::as_const(listSignal)) {
0259         if (signal.modify) {
0260             m_stream << whitespace() << "  Q_EMIT " << m_this << signal.name << "();\n";
0261         } else {
0262             m_stream << whitespace() << "  " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) << ".insert(" << signalEnumName(signal.name) << ");\n";
0263         }
0264     }
0265     if (hasBody) {
0266         m_stream << whitespace() << "}\n";
0267     }
0268 }