File indexing completed on 2024-05-12 03:54:29
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 ¶meters, 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 0042 if (m_cfg.staticAccessors) { 0043 m_this = QStringLiteral("self()->"); 0044 } else { 0045 m_const = QStringLiteral(" const"); 0046 } 0047 } 0048 0049 KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() 0050 { 0051 save(); 0052 } 0053 0054 void KConfigCodeGeneratorBase::save() 0055 { 0056 m_file.close(); 0057 } 0058 0059 // TODO: Remove this weird logic and adapt the testcases 0060 void KConfigCodeGeneratorBase::indent() 0061 { 0062 if (m_indentLevel >= 4) { 0063 m_indentLevel += 2; 0064 } else { 0065 m_indentLevel += 4; 0066 } 0067 } 0068 0069 void KConfigCodeGeneratorBase::unindent() 0070 { 0071 if (m_indentLevel > 4) { 0072 m_indentLevel -= 2; 0073 } else { 0074 m_indentLevel -= 4; 0075 } 0076 } 0077 0078 QString KConfigCodeGeneratorBase::whitespace() const 0079 { 0080 QString spaces; 0081 for (int i = 0; i < m_indentLevel; i++) { 0082 spaces.append(QLatin1Char(' ')); 0083 } 0084 return spaces; 0085 } 0086 0087 void KConfigCodeGeneratorBase::startScope() 0088 { 0089 m_stream << whitespace() << QLatin1Char('{'); 0090 m_stream << '\n'; 0091 indent(); 0092 } 0093 0094 void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) 0095 { 0096 unindent(); 0097 m_stream << whitespace() << QLatin1Char('}'); 0098 if (finalizer == ScopeFinalizer::Semicolon) { 0099 m_stream << ';'; 0100 } 0101 m_stream << '\n'; 0102 } 0103 0104 void KConfigCodeGeneratorBase::start() 0105 { 0106 const QString m_fileName = QFileInfo(m_inputFile).fileName(); 0107 m_stream << "// This file is generated by kconfig_compiler_kf6 from " << m_fileName << ".kcfg" 0108 << ".\n"; 0109 m_stream << "// All changes you do to this file will be lost.\n"; 0110 } 0111 0112 void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList) 0113 { 0114 for (const auto &include : headerList) { 0115 if (include.startsWith(QLatin1Char('"'))) { 0116 m_stream << "#include " << include << '\n'; 0117 } else { 0118 m_stream << "#include <" << include << ">\n"; 0119 } 0120 } 0121 } 0122 0123 // adds as many 'namespace foo {' lines to p_out as 0124 // there are namespaces in p_ns 0125 void KConfigCodeGeneratorBase::beginNamespaces() 0126 { 0127 if (!m_cfg.nameSpace.isEmpty()) { 0128 const auto nameSpaceList = m_cfg.nameSpace.split(QStringLiteral("::")); 0129 for (const QString &ns : nameSpaceList) { 0130 m_stream << "namespace " << ns << " {\n"; 0131 } 0132 m_stream << '\n'; 0133 } 0134 } 0135 0136 // adds as many '}' lines to p_out as 0137 // there are namespaces in p_ns 0138 void KConfigCodeGeneratorBase::endNamespaces() 0139 { 0140 if (!m_cfg.nameSpace.isEmpty()) { 0141 m_stream << '\n'; 0142 const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1; 0143 for (int i = 0; i < namespaceCount; ++i) { 0144 m_stream << "}\n"; 0145 } 0146 } 0147 } 0148 0149 // returns the member accessor implementation 0150 // which should go in the h file if inline 0151 // or the cpp file if not inline 0152 QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const 0153 { 0154 QString result; 0155 QTextStream out(&result, QIODevice::WriteOnly); 0156 QString n = e->name; 0157 QString t = e->type; 0158 bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum"); 0159 0160 out << "return "; 0161 if (useEnumType) { 0162 out << "static_cast<" << enumType(e, globalEnums) << ">("; 0163 } 0164 out << m_this << varPath(n, m_cfg); 0165 if (!e->param.isEmpty()) { 0166 out << "[i]"; 0167 } 0168 if (useEnumType) { 0169 out << ")"; 0170 } 0171 out << ";\n"; 0172 0173 return result; 0174 } 0175 0176 void KConfigCodeGeneratorBase::memberImmutableBody(const CfgEntry *e, bool globalEnums) 0177 { 0178 stream() << whitespace() << "return " << m_this << "isImmutable( QStringLiteral( \""; 0179 if (!e->param.isEmpty()) { 0180 stream() << QString(e->paramName).replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("%1")) << "\" ).arg( "; 0181 if (e->paramType == QLatin1String("Enum")) { 0182 stream() << "QLatin1String( "; 0183 0184 if (globalEnums) { 0185 stream() << enumName(e->param) << "ToString[i]"; 0186 } else { 0187 stream() << enumName(e->param) << "::enumToString[i]"; 0188 } 0189 0190 stream() << " )"; 0191 } else { 0192 stream() << "i"; 0193 } 0194 stream() << " )"; 0195 } else { 0196 stream() << e->name << "\" )"; 0197 } 0198 stream() << " );\n"; 0199 } 0200 0201 void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) 0202 { 0203 const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; 0204 0205 m_stream << whitespace() << "if ("; 0206 if (hasBody) { 0207 m_stream << "v != " << varExpression << " && "; 0208 } 0209 0210 const auto immutablefunction = immutableFunction(e->name, m_cfg.dpointer ? m_cfg.className : QString{}); 0211 m_stream << "!" << m_this << immutablefunction << "("; 0212 if (!e->param.isEmpty()) { 0213 m_stream << " i "; 0214 } 0215 m_stream << "))"; 0216 } 0217 0218 void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) 0219 { 0220 // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. 0221 if (!e->min.isEmpty()) { 0222 if (e->min != QLatin1String("0") || !isUnsigned(e->type)) { // skip writing "if uint<0" (#187579) 0223 m_stream << whitespace() << "if (v < " << e->min << ")\n"; 0224 m_stream << whitespace() << "{\n"; 0225 m_stream << whitespace(); 0226 addDebugMethod(m_stream, m_cfg, e->name); 0227 m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n"; 0228 m_stream << whitespace() << " v = " << e->min << ";\n"; 0229 m_stream << whitespace() << "}\n"; 0230 } 0231 } 0232 0233 if (!e->max.isEmpty()) { 0234 m_stream << '\n'; 0235 m_stream << whitespace() << "if (v > " << e->max << ")\n"; 0236 m_stream << whitespace() << "{\n"; 0237 m_stream << whitespace(); 0238 addDebugMethod(m_stream, m_cfg, e->name); 0239 m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n"; 0240 m_stream << whitespace() << " v = " << e->max << ";\n"; 0241 m_stream << whitespace() << "}\n\n"; 0242 } 0243 0244 const QString varExpression = m_this + varPath(e->name, m_cfg) + (e->param.isEmpty() ? QString{} : QStringLiteral("[i]")); 0245 0246 // TODO: Remove this `hasBody` logic, always use an '{' for the if. 0247 const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; 0248 0249 // m_this call creates an `if (someTest ...) that's just to long to throw over the code. 0250 createIfSetLogic(e, varExpression); 0251 m_stream << (hasBody ? " {" : "") << '\n'; 0252 m_stream << whitespace() << " " << varExpression << " = v;\n"; 0253 0254 const auto listSignal = e->signalList; 0255 for (const Signal &signal : std::as_const(listSignal)) { 0256 if (signal.modify) { 0257 m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();\n"; 0258 } else { 0259 m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) << ".insert(" << signalEnumName(signal.name) << ");\n"; 0260 } 0261 } 0262 if (hasBody) { 0263 m_stream << whitespace() << "}\n"; 0264 } 0265 }