diff options
32 files changed, 3193 insertions, 2129 deletions
| diff --git a/autotests/kconfig_compiler/CMakeLists.txt b/autotests/kconfig_compiler/CMakeLists.txt index 8a6289f9..c459243d 100644 --- a/autotests/kconfig_compiler/CMakeLists.txt +++ b/autotests/kconfig_compiler/CMakeLists.txt @@ -161,7 +161,6 @@ gen_kcfg_test_source(test12 test12_SRCS)  ecm_add_test(TEST_NAME test12 ${test12_SRCS})  target_link_libraries(test12 KF5::ConfigGui) -  ########### next target ###############  set(test13_SRCS test13main.cpp ) @@ -171,6 +170,14 @@ gen_kcfg_test_source(test13 test13_SRCS GENERATE_MOC)  ecm_add_test(TEST_NAME test13 ${test13_SRCS})  target_link_libraries(test13 KF5::ConfigGui) +########### next target ############### + +set(test_emptyentries_SRCS test_emptyentries_main.cpp ) + +gen_kcfg_test_source(test_emptyentries test_emptyentries_SRCS GENERATE_MOC) + +ecm_add_test(TEST_NAME test_emptyentries ${test_emptyentries_SRCS}) +target_link_libraries(test_emptyentries KF5::ConfigGui)  ########### next target ############### diff --git a/autotests/kconfig_compiler/kconfigcompiler_test.cpp b/autotests/kconfig_compiler/kconfigcompiler_test.cpp index 383745b1..d5b7baa2 100644 --- a/autotests/kconfig_compiler/kconfigcompiler_test.cpp +++ b/autotests/kconfig_compiler/kconfigcompiler_test.cpp @@ -52,6 +52,7 @@ static CompilerTestSet testCases = {      "test_translation_kde.cpp", "test_translation_kde.h",      "test_translation_kde_domain.cpp", "test_translation_kde_domain.h",      "test_translation_qt.cpp", "test_translation_qt.h", +    "test_emptyentries.cpp", "test_emptyentries.h",      nullptr  }; @@ -76,6 +77,7 @@ static CompilerTestSet testCasesToRun = {      "test_translation_kde",      "test_translation_kde_domain",      "test_translation_qt", +    "test_emptyentries",      nullptr  }; @@ -125,9 +127,7 @@ void KConfigCompiler_Test::testBaselineComparison()      if (content != contentRef)  {          appendFileDiff(fileRef.fileName(), file.fileName());      } -    // use split('\n') to avoid -    // the whole output shown inline -    QCOMPARE(content.split('\n'), contentRef.split('\n')); +      QVERIFY(content == contentRef);  } @@ -166,17 +166,23 @@ void KConfigCompiler_Test::appendFileDiff(const QString &oldFile, const QString          return;      } -    QStringList args; -    args << QStringLiteral("-u"); -    args << QFileInfo(oldFile).absoluteFilePath(); -    args << QFileInfo(newFile).absoluteFilePath(); +    QStringList args({ +        QStringLiteral("-u"), +        QFileInfo(oldFile).absoluteFilePath(), +        QFileInfo(newFile).absoluteFilePath() });      QProcess process;      process.start(m_diffExe, args, QIODevice::ReadOnly);      process.waitForStarted();      process.waitForFinished(); +      if (process.exitCode() == 1) { -        QByteArray out = process.readAllStandardOutput(); -        qDebug() << '\n' << out; +        const QString diffFileName = oldFile + QStringLiteral(".diff"); +        QFile diffFile(diffFileName); +        QVERIFY2(diffFile.open(QIODevice::WriteOnly), qPrintable(QLatin1String("Could not save diff file for ") + oldFile)); +        diffFile.write(process.readAllStandardOutput()); + +        // force a failure to print the flename where we stored the diff. +        QVERIFY2(false, qPrintable(QLatin1String("This test failed, look at the following file for details: ") + diffFileName));      }  } diff --git a/autotests/kconfig_compiler/test1.h.ref b/autotests/kconfig_compiler/test1.h.ref index 77c3b65f..52921ac6 100644 --- a/autotests/kconfig_compiler/test1.h.ref +++ b/autotests/kconfig_compiler/test1.h.ref @@ -8,6 +8,7 @@  #include <QDebug>  #include <qdir.h> +  class Test1 : public KConfigSkeleton  {    public: diff --git a/autotests/kconfig_compiler/test10.h.ref b/autotests/kconfig_compiler/test10.h.ref index 93839a54..82bbab84 100644 --- a/autotests/kconfig_compiler/test10.h.ref +++ b/autotests/kconfig_compiler/test10.h.ref @@ -14,7 +14,6 @@ class Test10 : public KConfigSkeleton      static Test10 *self();      ~Test10(); -      /**        Get foo bar      */ @@ -24,7 +23,6 @@ class Test10 : public KConfigSkeleton        return self()->mFooBar;      } -      /**        Get bar foo      */ diff --git a/autotests/kconfig_compiler/test11.h.ref b/autotests/kconfig_compiler/test11.h.ref index 395810ef..2acbe9e5 100644 --- a/autotests/kconfig_compiler/test11.h.ref +++ b/autotests/kconfig_compiler/test11.h.ref @@ -11,6 +11,7 @@  #include <QDebug>  #include "test11_types.h" +  class Test11 : public MyPrefs  {    public: diff --git a/autotests/kconfig_compiler/test11a.h.ref b/autotests/kconfig_compiler/test11a.h.ref index 23c83713..62b59381 100644 --- a/autotests/kconfig_compiler/test11a.h.ref +++ b/autotests/kconfig_compiler/test11a.h.ref @@ -11,6 +11,7 @@  #include <QDebug>  #include "test11_types.h" +  class Test11a : public MyPrefs  {    public: diff --git a/autotests/kconfig_compiler/test12.h.ref b/autotests/kconfig_compiler/test12.h.ref index d96d4609..e3954282 100644 --- a/autotests/kconfig_compiler/test12.h.ref +++ b/autotests/kconfig_compiler/test12.h.ref @@ -15,7 +15,6 @@ class Test12 : public KConfigSkeleton      Test12( );      ~Test12(); -      /**        Get RnRSource      */ diff --git a/autotests/kconfig_compiler/test13.h.ref b/autotests/kconfig_compiler/test13.h.ref index 6c67fc5d..3a6e7f89 100644 --- a/autotests/kconfig_compiler/test13.h.ref +++ b/autotests/kconfig_compiler/test13.h.ref @@ -16,7 +16,6 @@ class Test13 : public KConfigSkeleton      Test13( );      ~Test13(); -      Q_PROPERTY(QUrl picturesDir READ picturesDir CONSTANT)      /**        Get picturesDir @@ -46,7 +45,6 @@ class Test13 : public KConfigSkeleton        return mBrightness;      } -      Q_PROPERTY(bool startsWithUppercase READ startsWithUppercase CONSTANT)      /**        Get StartsWithUppercase diff --git a/autotests/kconfig_compiler/test9.h.ref b/autotests/kconfig_compiler/test9.h.ref index 9bcf0001..6e40cf48 100644 --- a/autotests/kconfig_compiler/test9.h.ref +++ b/autotests/kconfig_compiler/test9.h.ref @@ -8,6 +8,7 @@  #include <QDebug>  #include <qdir.h> +  class Test9 : public KConfigSkeleton  {    public: diff --git a/autotests/kconfig_compiler/test_dpointer.cpp.ref b/autotests/kconfig_compiler/test_dpointer.cpp.ref index c69d38a4..1bfb9f52 100644 --- a/autotests/kconfig_compiler/test_dpointer.cpp.ref +++ b/autotests/kconfig_compiler/test_dpointer.cpp.ref @@ -157,8 +157,8 @@ TestDPointer::TestDPointer( )  void TestDPointer::setAutoSave( bool v )  { -      if (!self()->isImmutable( QStringLiteral( "AutoSave" ) )) -        self()->d->autoSave = v; +    if (!self()->isImmutable( QStringLiteral( "AutoSave" ) )) +      self()->d->autoSave = v;  }  bool TestDPointer::autoSave() @@ -174,8 +174,8 @@ KConfigSkeleton::ItemBool *TestDPointer::autoSaveItem()  void TestDPointer::setAutoSaveInterval( int v )  { -      if (!self()->isImmutable( QStringLiteral( "AutoSaveInterval" ) )) -        self()->d->autoSaveInterval = v; +    if (!self()->isImmutable( QStringLiteral( "AutoSaveInterval" ) )) +      self()->d->autoSaveInterval = v;  }  int TestDPointer::autoSaveInterval() @@ -191,8 +191,8 @@ KConfigSkeleton::ItemInt *TestDPointer::autoSaveIntervalItem()  void TestDPointer::setConfirm( bool v )  { -      if (!self()->isImmutable( QStringLiteral( "Confirm" ) )) -        self()->d->confirm = v; +    if (!self()->isImmutable( QStringLiteral( "Confirm" ) )) +      self()->d->confirm = v;  }  bool TestDPointer::confirm() @@ -208,8 +208,8 @@ KConfigSkeleton::ItemBool *TestDPointer::confirmItem()  void TestDPointer::setArchiveFile( const QString & v )  { -      if (!self()->isImmutable( QStringLiteral( "ArchiveFile" ) )) -        self()->d->archiveFile = v; +    if (!self()->isImmutable( QStringLiteral( "ArchiveFile" ) )) +      self()->d->archiveFile = v;  }  QString TestDPointer::archiveFile() @@ -225,8 +225,8 @@ KConfigSkeleton::ItemString *TestDPointer::archiveFileItem()  void TestDPointer::setDestination( int v )  { -      if (!self()->isImmutable( QStringLiteral( "Destination" ) )) -        self()->d->destination = v; +    if (!self()->isImmutable( QStringLiteral( "Destination" ) )) +      self()->d->destination = v;  }  int TestDPointer::destination() @@ -242,8 +242,8 @@ KConfigSkeleton::ItemEnum *TestDPointer::destinationItem()  void TestDPointer::setHourSize( int v )  { -      if (!self()->isImmutable( QStringLiteral( "HourSize" ) )) -        self()->d->hourSize = v; +    if (!self()->isImmutable( QStringLiteral( "HourSize" ) )) +      self()->d->hourSize = v;  }  int TestDPointer::hourSize() @@ -259,8 +259,8 @@ KConfigSkeleton::ItemInt *TestDPointer::hourSizeItem()  void TestDPointer::setSelectionStartsEditor( bool v )  { -      if (!self()->isImmutable( QStringLiteral( "SelectionStartsEditor" ) )) -        self()->d->selectionStartsEditor = v; +    if (!self()->isImmutable( QStringLiteral( "SelectionStartsEditor" ) )) +      self()->d->selectionStartsEditor = v;  }  bool TestDPointer::selectionStartsEditor() @@ -276,8 +276,8 @@ KConfigSkeleton::ItemBool *TestDPointer::selectionStartsEditorItem()  void TestDPointer::setSelectedPlugins( const QStringList & v )  { -      if (!self()->isImmutable( QStringLiteral( "SelectedPlugins" ) )) -        self()->d->selectedPlugins = v; +    if (!self()->isImmutable( QStringLiteral( "SelectedPlugins" ) )) +      self()->d->selectedPlugins = v;  }  QStringList TestDPointer::selectedPlugins() @@ -293,8 +293,8 @@ KConfigSkeleton::ItemStringList *TestDPointer::selectedPluginsItem()  void TestDPointer::setHighlightColor( const QColor & v )  { -      if (!self()->isImmutable( QStringLiteral( "HighlightColor" ) )) -        self()->d->highlightColor = v; +    if (!self()->isImmutable( QStringLiteral( "HighlightColor" ) )) +      self()->d->highlightColor = v;  }  QColor TestDPointer::highlightColor() @@ -310,8 +310,8 @@ KConfigSkeleton::ItemColor *TestDPointer::highlightColorItem()  void TestDPointer::setAgendaBgColor( const QColor & v )  { -      if (!self()->isImmutable( QStringLiteral( "AgendaBgColor" ) )) -        self()->d->agendaBgColor = v; +    if (!self()->isImmutable( QStringLiteral( "AgendaBgColor" ) )) +      self()->d->agendaBgColor = v;  }  QColor TestDPointer::agendaBgColor() @@ -327,8 +327,8 @@ KConfigSkeleton::ItemColor *TestDPointer::agendaBgColorItem()  void TestDPointer::setTimeBarFont( const QFont & v )  { -      if (!self()->isImmutable( QStringLiteral( "TimeBarFont" ) )) -        self()->d->timeBarFont = v; +    if (!self()->isImmutable( QStringLiteral( "TimeBarFont" ) )) +      self()->d->timeBarFont = v;  }  QFont TestDPointer::timeBarFont() diff --git a/autotests/kconfig_compiler/test_emptyentries.cpp.ref b/autotests/kconfig_compiler/test_emptyentries.cpp.ref new file mode 100644 index 00000000..e23ddc3c --- /dev/null +++ b/autotests/kconfig_compiler/test_emptyentries.cpp.ref @@ -0,0 +1,59 @@ +// This file is generated by kconfig_compiler_kf5 from test_emptyentries.kcfg. +// All changes you do to this file will be lost. + +#include "test_emptyentries.h" + +#include <qglobal.h> +#include <QFile> + +#include <QDebug> + +class QMakeBuilderSettingsHelper +{ +  public: +    QMakeBuilderSettingsHelper() : q(nullptr) {} +    ~QMakeBuilderSettingsHelper() { delete q; } +    QMakeBuilderSettingsHelper(const QMakeBuilderSettingsHelper&) = delete; +    QMakeBuilderSettingsHelper& operator=(const QMakeBuilderSettingsHelper&) = delete; +    QMakeBuilderSettings *q; +}; +Q_GLOBAL_STATIC(QMakeBuilderSettingsHelper, s_globalQMakeBuilderSettings) +QMakeBuilderSettings *QMakeBuilderSettings::self() +{ +  if (!s_globalQMakeBuilderSettings()->q) +     qFatal("you need to call QMakeBuilderSettings::instance before using"); +  return s_globalQMakeBuilderSettings()->q; +} + +void QMakeBuilderSettings::instance(const QString& cfgfilename) +{ +  if (s_globalQMakeBuilderSettings()->q) { +     qDebug() << "QMakeBuilderSettings::instance called after the first use - ignoring"; +     return; +  } +  new QMakeBuilderSettings(KSharedConfig::openConfig(cfgfilename)); +  s_globalQMakeBuilderSettings()->q->read(); +} + +void QMakeBuilderSettings::instance(KSharedConfig::Ptr config) +{ +  if (s_globalQMakeBuilderSettings()->q) { +     qDebug() << "QMakeBuilderSettings::instance called after the first use - ignoring"; +     return; +  } +  new QMakeBuilderSettings(std::move(config)); +  s_globalQMakeBuilderSettings()->q->read(); +} + +QMakeBuilderSettings::QMakeBuilderSettings( KSharedConfig::Ptr config ) +  : KConfigSkeleton( std::move( config ) ) +{ +  Q_ASSERT(!s_globalQMakeBuilderSettings()->q); +  s_globalQMakeBuilderSettings()->q = this; +} + +QMakeBuilderSettings::~QMakeBuilderSettings() +{ +  s_globalQMakeBuilderSettings()->q = nullptr; +} + diff --git a/autotests/kconfig_compiler/test_emptyentries.h.ref b/autotests/kconfig_compiler/test_emptyentries.h.ref new file mode 100644 index 00000000..c2481e2d --- /dev/null +++ b/autotests/kconfig_compiler/test_emptyentries.h.ref @@ -0,0 +1,29 @@ +// This file is generated by kconfig_compiler_kf5 from test_emptyentries.kcfg. +// All changes you do to this file will be lost. +#ifndef QMAKEBUILDERSETTINGS_H +#define QMAKEBUILDERSETTINGS_H + +#include <kconfigskeleton.h> +#include <QCoreApplication> +#include <QDebug> + +#include <QString> + +class QMakeBuilderSettings : public KConfigSkeleton +{ +  public: +    static QMakeBuilderSettings *self(); +    static void instance(const QString& cfgfilename); +    static void instance(KSharedConfig::Ptr config); +    ~QMakeBuilderSettings(); + +  protected: +    QMakeBuilderSettings(KSharedConfig::Ptr config); +    friend class QMakeBuilderSettingsHelper; + + +  private: +}; + +#endif + diff --git a/autotests/kconfig_compiler/test_emptyentries.kcfg b/autotests/kconfig_compiler/test_emptyentries.kcfg new file mode 100644 index 00000000..1ad9326d --- /dev/null +++ b/autotests/kconfig_compiler/test_emptyentries.kcfg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" +      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +      xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 +      http://www.kde.org/standards/kcfg/1.0/kcfg.xsd"> +  <kcfgfile arg="true"/> +  <include>QString</include> +  <!-- save/restore is handled manually --> +</kcfg> diff --git a/autotests/kconfig_compiler/test_emptyentries.kcfgc b/autotests/kconfig_compiler/test_emptyentries.kcfgc new file mode 100644 index 00000000..5c9c71d7 --- /dev/null +++ b/autotests/kconfig_compiler/test_emptyentries.kcfgc @@ -0,0 +1,3 @@ +File=test_emptyentries.kcfg +ClassName=QMakeBuilderSettings +Singleton=true diff --git a/autotests/kconfig_compiler/test_emptyentries_main.cpp b/autotests/kconfig_compiler/test_emptyentries_main.cpp new file mode 100644 index 00000000..9396df77 --- /dev/null +++ b/autotests/kconfig_compiler/test_emptyentries_main.cpp @@ -0,0 +1,33 @@ +/* +Copyright (c) 2007 Andreas Pakulat <apaku@gmx.de> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "test_emptyentries.h" +#include <QGuiApplication> + +int main(int argc, char **argv) +{ +    QGuiApplication app(argc, argv); +    Q_UNUSED(app); +    QMakeBuilderSettings::instance("abc"); +    auto *t = QMakeBuilderSettings::self(); +    delete t; +    return 0; +} diff --git a/autotests/kconfig_compiler/test_translation_kde.h.ref b/autotests/kconfig_compiler/test_translation_kde.h.ref index f9b582c4..0e50b5a2 100644 --- a/autotests/kconfig_compiler/test_translation_kde.h.ref +++ b/autotests/kconfig_compiler/test_translation_kde.h.ref @@ -17,7 +17,6 @@ class TestTranslationKde : public KConfigSkeleton      TestTranslationKde( );      ~TestTranslationKde(); -      /**        Get Enable automatic saving of calendar      */ diff --git a/autotests/kconfig_compiler/test_translation_kde_domain.h.ref b/autotests/kconfig_compiler/test_translation_kde_domain.h.ref index 2fa42c71..4ed5d9bd 100644 --- a/autotests/kconfig_compiler/test_translation_kde_domain.h.ref +++ b/autotests/kconfig_compiler/test_translation_kde_domain.h.ref @@ -17,7 +17,6 @@ class TestTranslationKdeDomain : public KConfigSkeleton      TestTranslationKdeDomain( );      ~TestTranslationKdeDomain(); -      /**        Get Enable automatic saving of calendar      */ diff --git a/autotests/kconfig_compiler/test_translation_qt.h.ref b/autotests/kconfig_compiler/test_translation_qt.h.ref index 9831468b..2fe3274a 100644 --- a/autotests/kconfig_compiler/test_translation_qt.h.ref +++ b/autotests/kconfig_compiler/test_translation_qt.h.ref @@ -17,7 +17,6 @@ class TestTranslationQt : public KConfigSkeleton      TestTranslationQt( );      ~TestTranslationQt(); -      /**        Get Enable automatic saving of calendar      */ diff --git a/autotests/kconfigtest.h b/autotests/kconfigtest.h index 26d8e7f5..9963d476 100644 --- a/autotests/kconfigtest.h +++ b/autotests/kconfigtest.h @@ -16,6 +16,7 @@      the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      Boston, MA 02110-1301, USA.  */ +  #ifndef KCONFIGTEST_H  #define KCONFIGTEST_H diff --git a/src/kconfig_compiler/CMakeLists.txt b/src/kconfig_compiler/CMakeLists.txt index dc0a08db..2bd41cec 100644 --- a/src/kconfig_compiler/CMakeLists.txt +++ b/src/kconfig_compiler/CMakeLists.txt @@ -1,6 +1,13 @@ -set(kconfig_compiler_SRCS kconfig_compiler.cpp) +set(kconfig_compiler_SRCS +    KConfigParameters.cpp +    KConfigCodeGeneratorBase.cpp +    KConfigHeaderGenerator.cpp +    KConfigSourceGenerator.cpp +    KConfigXmlParser.cpp +    kconfig_compiler.cpp +)  add_executable(kconfig_compiler ${kconfig_compiler_SRCS}) diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp new file mode 100644 index 00000000..574aeff4 --- /dev/null +++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp @@ -0,0 +1,277 @@ +/* m_this file is part of the KDE libraries +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    m_this library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    m_this library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigParameters.h" +#include "KConfigCommonStructs.h" + +#include <QTextStream> +#include <QLatin1Char> +#include <QFileInfo> + +#include <ostream> +#include <QDebug> + +using std::endl; + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigCodeGeneratorBase::KConfigCodeGeneratorBase( +    const QString &inputFile, +    const QString &baseDir, +    const QString &fileName, +    const KConfigParameters ¶meters, +    ParseResult &parseResult) +    : m_inputFile(inputFile), m_baseDir(baseDir), m_fileName(fileName), m_cfg(parameters), parseResult(parseResult) +{ +    m_file.setFileName(m_fileName); +    if (!m_file.open(QIODevice::WriteOnly)) { +        cerr << "Can not open '" << m_fileName << "for writing." << endl; +        exit(1); +    } +    m_stream.setDevice(&m_file); +    m_stream.setCodec("utf-8"); + +    if (m_cfg.staticAccessors) { +        m_this = QStringLiteral("self()->"); +    } else { +        m_const = QStringLiteral(" const"); +    } +} + +KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() +{ +    save(); +} + +void KConfigCodeGeneratorBase::save() +{ +    m_file.close(); +} + +//TODO: Remove this weird logic and adapt the testcases +void KConfigCodeGeneratorBase::indent() +{ +    if (m_indentLevel >= 4) { +        m_indentLevel += 2; +    } else { +        m_indentLevel += 4; +    } +} + +void KConfigCodeGeneratorBase::unindent() +{ +    if (m_indentLevel > 4) { +        m_indentLevel -= 2; +    } else { +        m_indentLevel -= 4; +    } +} + +QString KConfigCodeGeneratorBase::whitespace() const +{ +    QString spaces; +    for (int i = 0; i < m_indentLevel; i++) { +        spaces.append(QLatin1Char(' ')); +    } +    return spaces; +} + +void KConfigCodeGeneratorBase::startScope() +{ +    m_stream << whitespace() << QLatin1Char('{'); +    m_stream << endl; +    indent(); +} + +void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) +{ +    unindent(); +    m_stream << whitespace() << QLatin1Char('}'); +    if (finalizer == ScopeFinalizer::Semicolon) { +        m_stream << ';'; +    } +    m_stream << endl; +} + +void KConfigCodeGeneratorBase::start() +{ +    const QString m_fileName = QFileInfo(m_inputFile).fileName(); +    m_stream << "// This file is generated by kconfig_compiler_kf5 from " << m_fileName << ".kcfg" << "." << endl; +    m_stream << "// All changes you do to this file will be lost." << endl; +} + +void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList) +{ +    for (auto include : qAsConst(headerList)) { +        if (include.startsWith(QLatin1Char('"'))) { +            m_stream << "#include " << include << endl; +        } else { +            m_stream << "#include <" << include << ">" << endl; +        } +    } +} + +// adds as many 'namespace foo {' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::beginNamespaces() +{ +    if (!m_cfg.nameSpace.isEmpty()) { +        for (const QString &ns : m_cfg.nameSpace.split(QStringLiteral("::"))) { +            m_stream << "namespace " << ns << " {" << endl; +        } +        m_stream << endl; +    } +} + +// adds as many '}' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::endNamespaces() +{ +    if (!m_cfg.nameSpace.isEmpty()) { +        m_stream << endl; +        const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1; +        for (int i = 0; i < namespaceCount; ++i) { +            m_stream << "}" << endl; +        } +    } +} + +// returns the member accesor implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const +{ +    QString result; +    QTextStream out(&result, QIODevice::WriteOnly); +    QString n = e->name; +    QString t = e->type; +    bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum"); + +    out << "return "; +    if (useEnumType) { +        out << "static_cast<" << enumType(e, globalEnums) << ">("; +    } +    out << m_this << varPath(n, m_cfg); +    if (!e->param.isEmpty()) { +        out << "[i]"; +    } +    if (useEnumType) { +        out << ")"; +    } +    out << ";" << endl; + +    return result; +} + +void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) +{ +    const QString n = e->name; +    const QString t = e->type; +    const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; + +    m_stream << whitespace() << "if ("; +    if (hasBody) { +        m_stream << "v != " << varExpression << " && "; +    } +    m_stream << "!" << m_this << "isImmutable( QStringLiteral( \""; +    if (!e->param.isEmpty()) { +        QString paramName = e->paramName; + +        m_stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( "; +        if (e->paramType == QLatin1String("Enum")) { +            m_stream << "QLatin1String( "; + +            if (m_cfg.globalEnums) { +                m_stream << enumName(e->param) << "ToString[i]"; +            } else { +                m_stream << enumName(e->param) << "::enumToString[i]"; +            } + +            m_stream << " )"; +        } else { +            m_stream << "i"; +        } +        m_stream << " )"; +    } else { +        m_stream << n << "\" )"; +    } +    m_stream << " ))"; +} + +void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) +{ +    QString n = e->name; +    QString t = e->type; + +    // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. +    if (!e->min.isEmpty()) { +        if (e->min != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) +            m_stream << whitespace() << "if (v < " << e->min << ")" << endl; +            m_stream << whitespace() << "{" << endl; +            m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n); +            m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl; +            m_stream << whitespace() << "  v = " << e->min << ";" << endl; +            m_stream << whitespace() << "}" << endl; +        } +    } + +    if (!e->max.isEmpty()) { +        m_stream << endl; +        m_stream << whitespace() << "if (v > " << e->max << ")" << endl; +        m_stream << whitespace() << "{" << endl; +        m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n); +        m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl; +        m_stream << whitespace() << "  v = " << e->max << ";" << endl; +        m_stream << whitespace() << "}" << endl << endl; +    } + +    const QString varExpression = m_this + varPath(n, m_cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]")); + +    // TODO: Remove this `hasBody` logic, always use an '{' for the if. +    const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; + +    // m_this call creates an `if (someTest ...) that's just to long to throw over the code. +    createIfSetLogic(e, varExpression); +    m_stream << (hasBody ? " {" : "") << endl; +    m_stream << whitespace() << "  " << varExpression << " = v;" << endl; + +    const auto listSignal = e->signalList; +    for (const Signal &signal : qAsConst(listSignal)) { +        if (signal.modify) { +            m_stream << whitespace() << "  Q_EMIT " << m_this << signal.name << "();" << endl; +        } else { +            m_stream << whitespace() <<  "  " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) +                << " |= " << signalEnumName(signal.name) << ";" << endl; +        } +    } +    if (hasBody) { +        m_stream << whitespace() << "}" << endl; +    } +} diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.h b/src/kconfig_compiler/KConfigCodeGeneratorBase.h new file mode 100644 index 00000000..5bbc6b71 --- /dev/null +++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.h @@ -0,0 +1,134 @@ +/* +    This file is part of KDE. + +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGCODEGENERATORBASE_H +#define KCONFIGCODEGENERATORBASE_H + +#include <QString> +#include <QTextStream> +#include <QFile> +#include <QVector> + +#include "KConfigParameters.h" +#include "KConfigCommonStructs.h" + +class CfgEntry; +struct ParseResult; + +/* This class manages the base of writing a C - Based code */ +class KConfigCodeGeneratorBase { +public: +    enum ScopeFinalizer {None, Semicolon}; + +    KConfigCodeGeneratorBase( +        const QString &inputFileName,  // The kcfg file +        const QString &baseDir,        // where we should store the generated file +        const QString &fileName,       // the name of the generated file +        const KConfigParameters ¶meters, // parameters passed to the generator +        ParseResult &parseResult // The pre processed configuration entries +    ); +    virtual ~KConfigCodeGeneratorBase(); + +    // iterates over the header list adding an #include directive. +    void addHeaders(const QStringList &header); + +    // Create all the namespace indentation levels based on the parsed result and parameters */ +    void beginNamespaces(); + +    // Closes all the namespaces adding lines with single '}' +    void endNamespaces(); + +    // Add the correct amount of whitespace in the code. +    QString whitespace() const; + +    // start a block scope `{` and increase indentation level. +    void endScope(ScopeFinalizer finalizer = None); +     +    // end a block scope `}` and decrease indentation level. +    void startScope(); + +    // start writing to the output file +    virtual void start(); + +    // save the result on the disk +    void save(); + +    // Code Implementations +    // Implements the `Get` methods for the CfgEntry +    // TODO: write to the stream directly without returning a QString. +    QString memberAccessorBody(const CfgEntry *e, bool globalEnums) const; + +    // Implements the `Set` methods for the CfgEntry +    void memberMutatorBody(const CfgEntry *e); + +    // This is the code that creates the logic for the Setter / Mutator. +    // It *just* creates the if test, no body. The reason is that just +    // the if test was more than 20 lines of code and hard to understand +    // what was happening in a bigger function. +    void createIfSetLogic(const CfgEntry *e, const QString &varExpression); + +protected: +    /* advance the number of spaces for the indentation level */ +    void indent(); + +    /* reduce the number of spaces for the indentation level */ +    void unindent(); + +    QString inputFile() const { return m_inputFile; } +    QString fileName() const { return m_fileName; } +    QString baseDir() const { return m_baseDir; } +    QString This() const { return m_this; } +    QString Const() const { return m_const; } +    KConfigParameters cfg() const { return m_cfg; } + +    // Can't be const. +    QTextStream& stream() { return m_stream; } + +    // HACK: This needs to be accesible because the HeaderGenerator actually modifies +    // it while running. Considering that this is a the result of the xml Parse, and not +    // the result of generating code, I consider this to be quite wrong - but moving the +    // changes away from the header generator only created more problems and I have to +    // investigate more. +    ParseResult &parseResult;       // the result of the parsed kcfg file + +private: +    QString m_inputFile; // the base file name, input file is based on this. + +    QString m_baseDir;   // Where we are going to save the file +    QString m_fileName;  // The file name + +    KConfigParameters m_cfg; // The parameters passed via the kcfgc file +    QTextStream m_stream;             // the stream that operates in the file to write data. +    QFile m_file;                     // The file handler. + +    // Special access to `this->` and `const` thru the code. +    QString m_this; +    QString m_const; +    int m_indentLevel = 0; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigCommonStructs.h b/src/kconfig_compiler/KConfigCommonStructs.h new file mode 100644 index 00000000..69b855ce --- /dev/null +++ b/src/kconfig_compiler/KConfigCommonStructs.h @@ -0,0 +1,195 @@ +/* This file is part of the KDE libraries +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGCOMMONSTRUCTS_H +#define KCONFIGCOMMONSTRUCTS_H + +#include <QString> +#include <QVector> +#include <QList> + +#include "KConfigParameters.h" + +struct Param +{ +    QString name; +    QString type; +}; + +struct Signal +{ +    QString name; +    QString label; +    QList<Param> arguments; +    bool modify = false; +}; + +class CfgEntry +{ +public: +    struct Choice { +        QString name; +        QString context; +        QString label; +        QString toolTip; +        QString whatsThis; +    }; +    class Choices +    { +    public: +        Choices() {} +        Choices(const QList<Choice> &d, const QString &n, const QString &p) +            : prefix(p), choices(d), mName(n) +        { +            int i = n.indexOf(QLatin1String("::")); +            if (i >= 0) { +                mExternalQual = n.left(i + 2); +            } +        } +        QString prefix; +        QList<Choice> choices; +        const QString &name() const +        { +            return mName; +        } +        const QString &externalQualifier() const +        { +            return mExternalQual; +        } +        bool external() const +        { +            return !mExternalQual.isEmpty(); +        } +    private: +        QString mName; +        QString mExternalQual; +    }; + +public: +    QString group; +    QString type; +    QString key; +    QString name; +    QString labelContext; +    QString label; +    QString toolTipContext; +    QString toolTip; +    QString whatsThisContext; +    QString whatsThis; +    QString code; +    QString defaultValue; +    QString param; +    QString paramName; +    QString paramType; +    Choices choices; +    QList<Signal> signalList; +    QStringList paramValues; +    QStringList paramDefaultValues; +    int paramMax; +    bool hidden; +    QString min; +    QString max; +}; + +struct ParseResult { +    QString cfgFileName; +    bool cfgFileNameArg = false; +    QList<Param> parameters; +    QList<Signal> signalList; +    QStringList includes; +    QList<CfgEntry *> entries; +    bool hasNonModifySignals = false; +}; + +// TODO: Move those methods +QString enumName(const QString &n); +QString enumName(const QString &n, const CfgEntry::Choices &c); +QString param(const QString &t); +QString cppType(const QString &t); +QString itemType(const QString &type); +QString changeSignalName(const QString &n); + +QString enumType(const CfgEntry *e, bool globalEnums); + +QString getDefaultFunction(const QString &n, const QString &className = QString()); +QString setFunction(const QString &n, const QString &className = QString()); +QString getFunction(const QString &n, const QString &className = QString()); + +QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg); +QString signalEnumName(const QString &signalName); + +bool isUnsigned(const QString &type); + +// returns the name of an member variable +// use itemPath to know the full path +// like using d-> in case of dpointer +QString varName(const QString &n, const KConfigParameters &cfg); + +QString varPath(const QString &n, const KConfigParameters &cfg); + +// returns the name of an item variable +// use itemPath to know the full path +// like using d-> in case of dpointer +QString itemVar(const CfgEntry *e, const KConfigParameters &cfg); + +QString itemPath(const CfgEntry *e, const KConfigParameters &cfg); + +QString filenameOnly(const QString &path); + +QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg); + +QString translatedString( +    const KConfigParameters &cfg, +    const QString &string,  +    const QString &context = QString(), +    const QString ¶m = QString(), +    const QString ¶mValue = QString()); + +// TODO: Sanitize those functions. +QString newItem( +    const CfgEntry* entry,  +    const QString &key, +    const QString& defaultValue, +    const KConfigParameters &cfg, +    const QString ¶m = QString()); + +QString userTextsFunctions( +    const CfgEntry *e, +    const KConfigParameters &cfg, +    QString itemVarStr = QString(),  +    const QString &i = QString()); + +QString paramString(const QString &s, const CfgEntry *e, int i); +QString paramString(const QString &group, const QList<Param> ¶meters); + +QString defaultValue(const QString &t); +QString memberGetDefaultBody(const CfgEntry *e); +QString literalString(const QString &s); +QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c); + +void addQuotes(QString &s); +void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n); + +#endif diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.cpp b/src/kconfig_compiler/KConfigHeaderGenerator.cpp new file mode 100644 index 00000000..5fc16109 --- /dev/null +++ b/src/kconfig_compiler/KConfigHeaderGenerator.cpp @@ -0,0 +1,614 @@ +/* This file is part of the KDE libraries +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#include "KConfigHeaderGenerator.h" +#include "KConfigCommonStructs.h" + +#include <QTextStream> +#include <QDebug> + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigHeaderGenerator::KConfigHeaderGenerator( +    const QString &inputFile, +    const QString &baseDir, +    const KConfigParameters &cfg, +    ParseResult &result) +    : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result) +{ +} + +void KConfigHeaderGenerator::start()  +{ +    KConfigCodeGeneratorBase::start(); +    startHeaderGuards(); +    createHeaders(); + +    beginNamespaces(); + +    createForwardDeclarations(); + +    doClassDefinition(); + +    endNamespaces(); +    endHeaderGuards(); +} + +void KConfigHeaderGenerator::doClassDefinition() +{ +    stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << endl; +    startScope(); + +    // Add Q_OBJECT macro if the config need signals. +    if (!parseResult.signalList.isEmpty() || cfg().generateProperties) { +        stream() << "  Q_OBJECT" << endl; +    } +    stream() << "  public:" << endl; +    implementEnums(); +    createConstructor(); +    createDestructor(); + +    for (auto *entry : parseResult.entries) { +        const QString n = entry->name; +        const QString t = entry->type; + +        const QString returnType = (cfg().useEnumTypes && t == QLatin1String("Enum")) +            ? enumType(entry, cfg().globalEnums) +            : cppType(t); + +        createSetters(entry); +        createProperties(entry, returnType); +        createGetters(entry, returnType); +        createDefaultValueMember(entry); +        createItemAcessors(entry, returnType); +    } + +    createSignals(); +    stream() << "  protected:" << endl; +    createSingleton(); + +    // TODO: Move those to functions too. +    if (parseResult.hasNonModifySignals) { +        stream() << whitespace() << "bool usrSave() override;" << endl; +    } + +    // Member variables +    if (!cfg().memberVariables.isEmpty() +        && cfg().memberVariables != QLatin1String("private") +        && cfg().memberVariables != QLatin1String("dpointer")) { +        stream() << "  " << cfg().memberVariables << ":" << endl; +    } + +    // Class Parameters +    for (const auto ¶meter : parseResult.parameters) { +        stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";" << endl; +    } + +    createNonDPointerHelpers(); +    createDPointer(); + +    if (cfg().customAddons) { +        stream() << whitespace() << "// Include custom additions" << endl; +        stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << '"' << endl; +    } + +    endScope(ScopeFinalizer::Semicolon); +} + +void KConfigHeaderGenerator::createHeaders() +{ +    addHeaders(cfg().headerIncludes); +    if (cfg().headerIncludes.size()) { +        stream() << endl; +    } + +    if (!cfg().singleton && parseResult.parameters.isEmpty()) { +        addHeaders({QStringLiteral("qglobal.h")}); +    } + +    if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) { +        addHeaders({QStringLiteral("kcoreconfigskeleton.h")}); +    } else { +        addHeaders({QStringLiteral("kconfigskeleton.h")}); +    } + +    addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")}); +    stream() << endl; + +    addHeaders(parseResult.includes); +    if (parseResult.includes.size()) { +        stream() << endl; +    } +} + +void KConfigHeaderGenerator::startHeaderGuards() +{ +    const bool hasNamespace = !cfg().nameSpace.isEmpty(); +    const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper(); +    const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QStringLiteral(""); +    const QString defineName = namespaceStr + cfg().className.toUpper() + QStringLiteral("_H"); + +    stream() << "#ifndef " << defineName << endl; +    stream() << "#define " << defineName << endl; +    stream() << endl; +} + +void KConfigHeaderGenerator::endHeaderGuards()  +{ +    stream() << endl; +    stream() << "#endif"; +    stream() << endl; +    // HACK: Original files ended with two last newlines, add them. +    stream() << endl; +} + +void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices) +{ +    const QList<CfgEntry::Choice> chlist = choices.choices; + +    if (chlist.isEmpty()) { +        return; +    } + +    QStringList values; +    for (const auto choice : qAsConst(chlist)) { +        values.append(choices.prefix + choice.name); +    } + +    if (choices.name().isEmpty()) { +        if (cfg().globalEnums) { +            stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; +        } else { +            // Create an automatically named enum +            stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << endl; +            stream() << whitespace() << "{" << endl; +            stream() << whitespace() << "  public:" << endl; +            stream() << whitespace() << "  enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; +            stream() << whitespace() << "};" << endl; +        } +    } else if (!choices.external()) { +        // Create a named enum +        stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; +    } +} + +void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values) +{ +    if (values.isEmpty()) { +        return; +    } + +    if (cfg().globalEnums) { +        // ### FIXME!! +        // make the following string table an index-based string search! +        // ### +        stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; +        stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];" << endl; +    } else { +        stream() << whitespace() << "class " << enumName(entry->param) << endl; +        stream() << whitespace() << "{" << endl; +        stream() << whitespace() << "  public:" << endl; +        stream() << whitespace() << "  enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; +        stream() << whitespace() << "  static const char* const enumToString[];" << endl; +        stream() << whitespace() << "};" << endl; +    } +} + +void KConfigHeaderGenerator::implementEnums() +{ +    if (!parseResult.entries.size()) { +        return; +    } + +    for (const auto entry : parseResult.entries) { +        const CfgEntry::Choices &choices = entry->choices; +        const QStringList values = entry->paramValues; + +        implementChoiceEnums(entry, choices); +        implementValueEnums(entry, values); +    } +   stream() << endl; +} + +void KConfigHeaderGenerator::createSignals() +{ +    // Signal definition. +    const bool hasSignals = !parseResult.signalList.isEmpty(); + +    unsigned val = 1 << parseResult.signalList.size(); +    if (!val) { +        cerr << "Too many signals to create unique bit masks" << endl; +        exit(1); +    } + +    if (!hasSignals) { +        return; +    } + +    stream() << "\n    enum {" << endl; +    val = 1; + +    // HACK: Use C-Style for add a comma in all but the last element, +    // just to make the source generated code equal to the old one. +    // When we are sure, revert this to a range-based-for and just add +    // a last comma, as it's valid c++. +    for (int i = 0, end = parseResult.signalList.size(); i < end; i++) { +        auto signal = parseResult.signalList.at(i); +        stream() << whitespace() << "  " << signalEnumName(signal.name) << " = 0x" << hex << val; +        if (i != end-1) { +            stream() << "," << endl; +        } + +        val <<= 1; +    } +    stream() << endl; +    stream() << whitespace() << "};" << dec << endl << endl; + +    stream() << "  Q_SIGNALS:"; +    for (const Signal &signal : parseResult.signalList) { +        stream() << endl; +        if (!signal.label.isEmpty()) { +            stream() << whitespace() << "/**" << endl; +            stream() << whitespace() << "  " << signal.label << endl; +            stream() << whitespace() << "*/" << endl; +        } +        stream() << whitespace() << "void " << signal.name << "("; +        QList<Param>::ConstIterator it, itEnd = signal.arguments.constEnd(); +        for (it = signal.arguments.constBegin(); it != itEnd;) { +            Param argument = *it; +            QString type = param(argument.type); +            if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) { +                for (auto *entry : parseResult.entries) { +                    if (entry->name == argument.name) { +                        type = enumType(entry, cfg().globalEnums); +                        break; +                    } +                } +            } +            stream() << type << " " << argument.name; +            if (++it != itEnd) { +                stream() << ", "; +            } +        } +        stream() << ");" << endl; +    } +    stream() << endl; + +    stream() << "  private:" << endl; +    stream() << whitespace() << "void itemChanged(quint64 flags);" << endl; +    stream() << endl; +} + +void KConfigHeaderGenerator::createDPointer() +{ +    if (!cfg().dpointer) { +        return; +    } + +    // use a private class for both member variables and items +    stream() << "  private:" << endl; +    for (const auto &entry : parseResult.entries) { +        if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) { +            stream() << whitespace() << ""; +            if (cfg().staticAccessors) { +                stream() << "static "; +            } +            stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; +            if (!entry->param.isEmpty()) { +                stream() << " " << cppType(entry->paramType) << " i "; +            } +            stream() << ")" << Const() << ";" << endl; +        } +    } +    stream() << whitespace() << "" << cfg().className << "Private *d;" << endl; +} + +void KConfigHeaderGenerator::createConstructor() +{ +    if (cfg().singleton) { +        stream() << whitespace() << "static " << cfg().className << " *self();" << endl; +        if (parseResult.cfgFileNameArg) { +            stream() << whitespace() << "static void instance(const QString& cfgfilename);" << endl; +            stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);" << endl; +        } +        return; +    } + +    stream() << whitespace() << "" << cfg().className << "("; +    if (parseResult.cfgFileNameArg) { +        if (cfg().forceStringFilename) +            stream() << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", "); +        else +            stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); +    } + +    bool first = true; +    for (const auto parameter : parseResult.parameters) { +        if (first) { +            first = false; +        } else { +            stream() << ","; +        } + +        stream() << " " << param(parameter.type) << " " << parameter.name; +    } + +    if (cfg().parentInConstructor) { +        if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { +            stream() << ","; +        } +        stream() << " QObject *parent = nullptr"; +    } +    stream() << " );" << endl; +} + +void KConfigHeaderGenerator::createDestructor() +{ +    stream() << whitespace() << "~" << cfg().className << "();" << endl << endl; +} + +void KConfigHeaderGenerator::createForwardDeclarations() +{ +    // Private class declaration +    if (cfg().dpointer) { +        stream() << "class " << cfg().className << "Private;" << endl << endl; +    } +} + +void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString& returnType) +{ +    if (!cfg().generateProperties) { +        return; +    } +    stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name); +    stream() << " READ " << getFunction(entry->name); + +    if (cfg().allMutators || cfg().mutators.contains(entry->name)) { +        const QString signal = changeSignalName(entry->name); +        stream() << " WRITE " << setFunction(entry->name); +        stream() << " NOTIFY " << signal; + +        //If we have the modified signal, we'll also need +        //the changed signal as well +        Signal s; +        s.name = signal; +        s.modify = true; +        parseResult.signalList.append(s); +    } else { +        stream() << " CONSTANT"; +    } +    stream() << ")" << endl; +} + +void KConfigHeaderGenerator::createSetters(const CfgEntry *entry) +{ +    // Manipulator +    if (!cfg().allMutators && !cfg().mutators.contains(entry->name)) { +        return; +    } + +    stream() << whitespace() << "/**" << endl; +    stream() << whitespace() << "  Set " << entry->label << endl; +    stream() << whitespace() << "*/" << endl; + +    if (cfg().staticAccessors) { +        stream() << whitespace() << "static" << endl; +    } + +    stream() << whitespace() << "void " << setFunction(entry->name) << "( "; +    if (!entry->param.isEmpty()) { +        stream() <<cppType(entry->paramType) << " i, "; +    } + +    stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") +        ? enumType(entry, cfg().globalEnums) +        : param(entry->type)); + +    stream() << " v )"; + +    // function body inline only if not using dpointer +    // for BC mode +    if (!cfg().dpointer) { +        stream() << endl; +        startScope(); +        memberMutatorBody(entry); +        endScope(); +        stream() << endl; +    } else { +        stream() << ";" << endl << endl; +    } +} + +void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString &returnType) +{ +    // Accessor +    stream() << whitespace() << "/**" << endl; +    stream() << whitespace() << "  Get " << entry->label << endl; +    stream() << whitespace() << "*/" << endl; +    if (cfg().staticAccessors) { +        stream() << whitespace() << "static" << endl; +    } +    stream() << whitespace() << ""; +    stream() << returnType; +    stream() << " " << getFunction(entry->name) << "("; +    if (!entry->param.isEmpty()) { +        stream() << " " << cppType(entry->paramType) << " i "; +    } +    stream() << ")" << Const(); + +    // function body inline only if not using dpointer +    // for BC mode +    if (!cfg().dpointer) { +        stream() << endl; +        startScope(); +        stream() << whitespace() << memberAccessorBody(entry, cfg().globalEnums); +        endScope(); +        stream() << endl; +    } else { +        stream() << ";" << endl << endl; +    } +} + +void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString &returnType) +{ +    // Item accessor +    if (!cfg().itemAccessors) { +        return; +    } +    stream() << whitespace() << "/**" << endl; +    stream() << whitespace() << "  Get Item object corresponding to " << entry->name << "()" +        << endl; +    stream() << whitespace() << "*/" << endl; +    stream() << whitespace() << "Item" << itemType(entry->type) << " *" +        << getFunction(entry->name) << "Item("; +    if (!entry->param.isEmpty()) { +        stream() << " " << cppType(entry->paramType) << " i "; +    } +    stream() << ")"; +    if (!cfg().dpointer) { +        stream() << endl; +        startScope(); +        stream() << whitespace() << itemAccessorBody(entry, cfg()); +        endScope(); +    } else { +        stream() << ";" << endl; +    } + +    stream() <<endl; +} + +void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry) +{ +    // Default value Accessor +    if (! ((cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) && !entry->defaultValue.isEmpty())) { +        return; +    } +    stream() << whitespace() << "/**" << endl; +    stream() << whitespace() << "  Get " << entry->label << " default value" << endl; +    stream() << whitespace() << "*/" << endl; +    if (cfg().staticAccessors) { +        stream() << whitespace() << "static" << endl; +    } +    stream() << whitespace() << ""; +    if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { +        stream() << enumType(entry, cfg().globalEnums); +    } else { +        stream() << cppType(entry->type); +    } +    stream() << " " << getDefaultFunction(entry->name) << "("; +    if (!entry->param.isEmpty()) { +        stream() << " " << cppType(entry->paramType) << " i "; +    } +    stream() << ")" << Const() << endl; +    stream() << whitespace() << "{" << endl; +    stream() << whitespace() << "    return "; +    if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { +        stream() << "static_cast<" << enumType(entry, cfg().globalEnums) << ">("; +    } +    stream() << getDefaultFunction(entry->name) << "_helper("; +    if (!entry->param.isEmpty()) { +        stream() << " i "; +    } +    stream() << ")"; +    if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { +        stream() << ")"; +    } +    stream() << ";" << endl; +    stream() << whitespace() << "}" << endl; +    stream() << endl; +} + +void KConfigHeaderGenerator::createSingleton() +{ +    // Private constructor for singleton +    if (!cfg().singleton) { +        return; +    } + +    stream() << whitespace() << "" << cfg().className << "("; +    if (parseResult.cfgFileNameArg) { +        stream() << "KSharedConfig::Ptr config"; +    } +    if (cfg().parentInConstructor) { +        if (parseResult.cfgFileNameArg) { +            stream() << ", "; +        } +        stream() << "QObject *parent = nullptr"; +    } +    stream() << ");" << endl; +    stream() << whitespace() << "friend class " << cfg().className << "Helper;" << endl << endl; +} + +void KConfigHeaderGenerator::createNonDPointerHelpers() +{ +    if (cfg().memberVariables == QLatin1String("dpointer")) { +        return; +    } + +    QString group; +    for (auto *entry : parseResult.entries) { +        if (entry->group != group) { +            group = entry->group; +            stream() << endl; +            stream() << whitespace() << "// " << group << endl; +        } +        stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg()); +        if (!entry->param.isEmpty()) { +            stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); +        } +        stream() << ";" << endl; + +        if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) { +            stream() << whitespace() << ""; +            if (cfg().staticAccessors) { +                stream() << "static "; +            } +            stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; +            if (!entry->param.isEmpty()) { +                stream() << " " << cppType(entry->paramType) << " i "; +            } +            stream() << ")" << Const() << ";" << endl; +        } +    } + +    stream() << endl << "  private:" << endl; +    if (cfg().itemAccessors) { +        for (auto *entry : parseResult.entries) { +            stream() << whitespace() << "Item" << itemType(entry->type) << " *" << itemVar(entry, cfg()); +            if (!entry->param.isEmpty()) { +                stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); +            } +            stream() << ";" << endl; +        } +    } + +    if (parseResult.hasNonModifySignals) { +        stream() << whitespace() << "uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";" << endl; +    } +} diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.h b/src/kconfig_compiler/KConfigHeaderGenerator.h new file mode 100644 index 00000000..0b85594a --- /dev/null +++ b/src/kconfig_compiler/KConfigHeaderGenerator.h @@ -0,0 +1,78 @@ +/* +    This file is part of KDE. + +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGHEADERGENERATOR_H +#define KCONFIGHEADERGENERATOR_H + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigCommonStructs.h" + +#include <QString> +#include <QList> + +class KConfigParameters; +class CfgEntry; +class QTextStream; +struct ParseResult; + +class KConfigHeaderGenerator : public KConfigCodeGeneratorBase { +public: +    KConfigHeaderGenerator( +        const QString &inputFile, +        const QString &baseDir, +        const KConfigParameters ¶meters, +        ParseResult &parseResult); + +        void start() override; + +private: +    void startHeaderGuards(); +    void endHeaderGuards(); + +    void implementEnums(); +    void implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices); +    void implementValueEnums(const CfgEntry *entry, const QStringList &values); + +    void doClassDefinition(); +    void createHeaders(); +    void createDPointer(); +    void createNonDPointerHelpers(); + +    void createConstructor(); +    void createDestructor(); +    void createForwardDeclarations(); +    void createSingleton(); +    void createSignals(); + +    void createSetters(const CfgEntry *entry); +    void createItemAcessors(const CfgEntry *entry, const QString &returnType); +    void createGetters(const CfgEntry *entry, const QString &returnType); +    void createProperties(const CfgEntry *entry, const QString &returnType); +    void createDefaultValueMember(const CfgEntry *entry); +}; + +#endif diff --git a/src/kconfig_compiler/KConfigParameters.cpp b/src/kconfig_compiler/KConfigParameters.cpp new file mode 100644 index 00000000..c8046ba6 --- /dev/null +++ b/src/kconfig_compiler/KConfigParameters.cpp @@ -0,0 +1,100 @@ +/* This file is part of the KDE libraries +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#include "KConfigParameters.h" + +// TODO: Remove this. +#undef QT_NO_CAST_FROM_ASCII + +#include <QDebug> +#include <QFileInfo> + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigParameters::KConfigParameters(const QString &codegenFilename) +{ +    if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) { +        cerr << "Codegen options file must have extension .kcfgc" << endl; +        exit(1); +    } + +    baseName = QFileInfo(codegenFilename).fileName(); +    baseName = baseName.left(baseName.length() - 6); + +    // Configure the compiler with some settings +    QSettings codegenConfig(codegenFilename, QSettings::IniFormat); + +    nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString(); +    className = codegenConfig.value(QStringLiteral("ClassName")).toString(); +    if (className.isEmpty()) { +        cerr << "Class name missing" << endl; +        exit(1); +    } +    inherits = codegenConfig.value(QStringLiteral("Inherits")).toString(); +    if (inherits.isEmpty()) { +        inherits = QStringLiteral("KConfigSkeleton"); +    } +    visibility = codegenConfig.value(QStringLiteral("Visibility")).toString(); +    if (!visibility.isEmpty()) { +        visibility += QLatin1Char(' '); +    } +    parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool(); +    forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool(); +    singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool(); +    staticAccessors = singleton; +    customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool(); +    memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString(); +    dpointer = (memberVariables == QLatin1String("dpointer")); +    headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList(); +    sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList(); +    mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList(); +    allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true"))); +    itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool(); +    setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool(); +    defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList(); +    allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true")); +    notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList(); +    allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true"))); +    globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool(); +    useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool(); +    const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower(); +    generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool(); +    if (trString == QLatin1String("kde")) { +        translationSystem = KdeTranslation; +        translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString(); +    } else { +        if (!trString.isEmpty() && trString != QLatin1String("qt")) { +            cerr << "Unknown translation system, falling back to Qt tr()" << endl; +        } +        translationSystem = QtTranslation; +    } +    qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString(); +    headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString(); +    sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString(); +} diff --git a/src/kconfig_compiler/KConfigParameters.h b/src/kconfig_compiler/KConfigParameters.h new file mode 100644 index 00000000..b114dcc9 --- /dev/null +++ b/src/kconfig_compiler/KConfigParameters.h @@ -0,0 +1,81 @@ +/* +    This file is part of KDE. +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGXTPARAMETERS_H +#define KCONFIGXTPARAMETERS_H + +#include <QSettings> +#include <QString> +#include <QStringList> + +/** +   Configuration Compiler Configuration +*/ +class KConfigParameters +{ +public: +    KConfigParameters(const QString &codegenFilename); + +public: +    enum TranslationSystem { +        QtTranslation, +        KdeTranslation +    }; + +    // These are read from the .kcfgc configuration file +    QString nameSpace;     // The namespace for the class to be generated +    QString className;     // The class name to be generated +    QString inherits;      // The class the generated class inherits (if empty, from KConfigSkeleton) +    QString visibility; +    bool parentInConstructor; // The class has the optional parent parameter in its constructor +    bool forceStringFilename; +    bool singleton;        // The class will be a singleton +    bool staticAccessors;  // provide or not static accessors +    bool customAddons; +    QString memberVariables; +    QStringList headerIncludes; +    QStringList sourceIncludes; +    QStringList mutators; +    QStringList defaultGetters; +    QStringList notifiers; +    QString qCategoryLoggingName; +    QString headerExtension; +    QString sourceExtension; +    bool allMutators; +    bool setUserTexts; +    bool allDefaultGetters; +    bool dpointer; +    bool globalEnums; +    bool useEnumTypes; +    bool itemAccessors; +    bool allNotifiers; +    TranslationSystem translationSystem; +    QString translationDomain; +    bool generateProperties; +    QString baseName; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp new file mode 100644 index 00000000..f306693b --- /dev/null +++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp @@ -0,0 +1,657 @@ +/* This file is part of the KDE libraries +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#include "KConfigSourceGenerator.h" +#include "KConfigCommonStructs.h" + + +KConfigSourceGenerator::KConfigSourceGenerator( +    const QString &inputFile, +    const QString &baseDir, +    const KConfigParameters &cfg, +    ParseResult &result) +    : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.sourceExtension, cfg, result) +{ +} + +void KConfigSourceGenerator::start()  +{ +    KConfigCodeGeneratorBase::start(); +    stream() << endl; +    createHeaders(); + +    if (!cfg().nameSpace.isEmpty()) { +        stream() << "using namespace " << cfg().nameSpace << ";"; +        stream() << endl << endl; +    } + +    createPrivateDPointerImplementation(); +    createSingletonImplementation(); +    createPreamble(); +    doConstructor(); +    doGetterSetterDPointerMode(); +    createDefaultValueGetterSetter(); +    createDestructor(); +    createNonModifyingSignalsHelper(); +    createSignalFlagsHandler(); +    includeMoc(); +} + +void KConfigSourceGenerator::createHeaders() +{ +    QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension; + +    // TODO: Make addQuotes return a string instead of replacing it inplace. +    addQuotes(headerName); + +    addHeaders({ headerName }); +    stream() << endl; + +    addHeaders(cfg().sourceIncludes); +    if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) { +        addHeaders({QStringLiteral("klocalizedstring.h")}); +        stream() << endl; +    } + +    // Header required by singleton implementation +    if (cfg().singleton) { +        addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")}); + +        // HACK: Add single line to fix test. +        if (cfg().singleton && parseResult.cfgFileNameArg) { +            stream() << endl; +        } +    } + +    if (cfg().singleton && parseResult.cfgFileNameArg) { +        addHeaders({QStringLiteral("QDebug")}); +    } + +    if (cfg().singleton) { +        stream() << endl; +    } +} + +void KConfigSourceGenerator::createPrivateDPointerImplementation() +{ +    // private class implementation +    if (!cfg().dpointer) { +        return; +    } + +    QString group; +    beginNamespaces(); +    stream() << "class " << cfg().className << "Private" << endl; +    stream() << "{" << endl; +    stream() << "  public:" << endl; + +    // Create Members +    for (auto *entry : parseResult.entries) { +        if (entry->group != group) { +            group = entry->group; +            stream() << endl; +            stream() << "    // " << group << endl; +        } +        stream() << "    " << cppType(entry->type) << " " << varName(entry->name, cfg()); +        if (!entry->param.isEmpty()) { +            stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); +        } +        stream() << ";" << endl; +    } +    stream() << endl << "    // items" << endl; + +    // Create Items. +    for (auto *entry : parseResult.entries) { +        const QString declType = entry->signalList.isEmpty() +                ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(entry->type)) +                : QStringLiteral("KConfigCompilerSignallingItem"); + +        stream() << "    " << declType << " *" << itemVar( entry, cfg() ); +        if (!entry->param.isEmpty()) { +            stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); +        } +        stream() << ";" << endl; +    } + +    if (parseResult.hasNonModifySignals) { +        stream() << "    uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";" << endl; +    } + +    stream() << "};" << endl << endl; +    endNamespaces(); +} + +void KConfigSourceGenerator::createSingletonImplementation() +{ +    // Singleton implementation +    if (!cfg().singleton) { +        return; +    } + +    beginNamespaces(); +    stream() << "class " << cfg().className << "Helper" << endl; +    stream() << '{' << endl; +    stream() << "  public:" << endl; +    stream() << "    " << cfg().className << "Helper() : q(nullptr) {}" << endl; +    stream() << "    ~" << cfg().className << "Helper() { delete q; }" << endl; +    stream() << "    " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;" << endl; +    stream() << "    " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;" << endl; +    stream() << "    " << cfg().className << " *q;" << endl; +    stream() << "};" << endl; +    endNamespaces(); + +    stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")" << endl; + +    stream() << cfg().className << " *" << cfg().className << "::self()" << endl; +    stream() << "{" << endl; +    if (parseResult.cfgFileNameArg) { +        stream() << "  if (!s_global" << cfg().className << "()->q)" << endl; +        stream() << "     qFatal(\"you need to call " << cfg().className << "::instance before using\");" << endl; +    } else { +        stream() << "  if (!s_global" << cfg().className << "()->q) {" << endl; +        stream() << "    new " << cfg().className << ';' << endl; +        stream() << "    s_global" << cfg().className << "()->q->read();" << endl; +        stream() << "  }" << endl << endl; +    } +    stream() << "  return s_global" << cfg().className << "()->q;" << endl; +    stream() << "}" << endl << endl; + +    if (parseResult.cfgFileNameArg) { +        auto instance = [this] (const QString &type, const QString &arg, bool isString) { +            stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")" << endl; +            stream() << "{" << endl; +            stream() << "  if (s_global" << cfg().className << "()->q) {" << endl; +            stream() << "     qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";" << endl; +            stream() << "     return;" << endl; +            stream() << "  }" << endl; +            stream() << "  new " << cfg().className << "("; +            if (isString) { +                stream() << "KSharedConfig::openConfig(" << arg << ")"; +            } else { +                stream() << "std::move(" << arg << ")"; +            } +            stream() << ");" << endl; +            stream() << "  s_global" << cfg().className << "()->q->read();" << endl; +            stream() << "}" << endl << endl; +        }; +        instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); +        instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); +    } +} + +void KConfigSourceGenerator::createPreamble() +{ +    QString cppPreamble; +    for (const auto entry : parseResult.entries) { +        if (entry->paramValues.isEmpty()) { +            continue; +        } +         +        cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(entry->param); +        cppPreamble += cfg().globalEnums +            ? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n") +            : QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n"); +    } + +    if (!cppPreamble.isEmpty()) { +        stream() << cppPreamble << endl; +    } +} + +void KConfigSourceGenerator::createConstructorParameterList() +{ +    if (parseResult.cfgFileNameArg) { +        if (!cfg().forceStringFilename) { +            stream() << " KSharedConfig::Ptr config"; +        } else { +            stream() << " const QString& config"; +        } +        stream() << (parseResult.parameters.isEmpty() ? "" : ","); +    } + +    for (QList<Param>::ConstIterator it = parseResult.parameters.constBegin(); +            it != parseResult.parameters.constEnd(); ++it) { +        if (it != parseResult.parameters.constBegin()) { +            stream() << ","; +        } +        stream() << " " << param((*it).type) << " " << (*it).name; +    } + +    if (cfg().parentInConstructor) { +        if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { +            stream() << ","; +        } +        stream() << " QObject *parent"; +    } + +} + +void KConfigSourceGenerator::createParentConstructorCall() +{ +    stream() << cfg().inherits << "("; +    if (!parseResult.cfgFileName.isEmpty()) { +        stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" "; +    } +    if (parseResult.cfgFileNameArg) { +        if (! cfg().forceStringFilename) { +            stream() << " std::move( config ) "; +        } else { +            stream() << " config "; +        } +    } +    if (!parseResult.cfgFileName.isEmpty()) { +        stream() << ") "; +    } +    stream() << ")" << endl; +} + +void KConfigSourceGenerator::createInitializerList() +{ +    for (const auto ¶meter : parseResult.parameters) { +        stream() << "  , mParam" << parameter.name << "(" << parameter.name << ")" << endl; +    } + +    if (parseResult.hasNonModifySignals && !cfg().dpointer) { +        stream() << "  , " << varName(QStringLiteral("settingsChanged"), cfg()) << "(0)" << endl; +    } +} + +void KConfigSourceGenerator::createEnums(const CfgEntry *entry) +{ +    if (entry->type != QLatin1String("Enum")) { +        return; +    } +    stream() << "  QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";" << endl; +     +    for (const auto &choice : qAsConst(entry->choices.choices)) { +        stream() << "  {" << endl; +        stream() << "    " << cfg().inherits << "::ItemEnum::Choice choice;" << endl; +        stream() << "    choice.name = QStringLiteral(\"" << choice.name << "\");" << endl; +        if (cfg().setUserTexts) { +            if (!choice.label.isEmpty()) { +                stream() << "    choice.label = " +                    << translatedString(cfg(), choice.label, choice.context) +                    << ";" << endl; +            } +            if (!choice.toolTip.isEmpty()) { +                stream() << "    choice.toolTip = " +                    << translatedString(cfg(), choice.toolTip, choice.context) +                    << ";" << endl; +            } +            if (!choice.whatsThis.isEmpty()) { +                stream() << "    choice.whatsThis = " +                    << translatedString(cfg(), choice.whatsThis, choice.context) +                    << ";" << endl; +            } +        } +        stream() << "    values" << entry->name << ".append( choice );" << endl; +        stream() << "  }" << endl; +    } +} + +void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key) +{ +   stream() << "  " << itemPath(entry, cfg()) << " = " +        << newItem(entry, key, entry->defaultValue, cfg()) << endl; + +    if (!entry->min.isEmpty()) { +        stream() << "  " << itemPath(entry, cfg()) << "->setMinValue(" << entry->min << ");" << endl; +    } +    if (!entry->max.isEmpty()) { +        stream() << "  " << itemPath(entry, cfg()) << "->setMaxValue(" << entry->max << ");" << endl; +    } + +    if (cfg().setUserTexts) { +        stream() << userTextsFunctions(entry, cfg()); +    } + +    if (cfg().allNotifiers || cfg().notifiers.contains(entry->name)) { +        stream() << "  " << itemPath(entry, cfg()) << "->setWriteFlags(KConfigBase::Notify);" << endl; +    } + +    stream() << "  addItem( " << itemPath(entry, cfg()); +    QString quotedName = entry->name; +    addQuotes(quotedName); +    if (quotedName != key) { +        stream() << ", QStringLiteral( \"" << entry->name << "\" )"; +    } +    stream() << " );" << endl; +} + +void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key) +{ +    for (int i = 0; i <= entry->paramMax; i++) { +        QString itemVarStr(itemPath(entry, cfg()) + QStringLiteral("[%1]").arg(i)); + +        QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i] +                           : !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i) +                           : defaultValue(entry->type); +         +        stream() << "  " << itemVarStr << " = " +            << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << endl; + +        if (cfg().setUserTexts) { +            stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName); +        } + +        // Make mutators for enum parameters work by adding them with $(..) replaced by the +        // param name. The check for isImmutable in the set* functions doesn't have the param +        // name available, just the corresponding enum value (int), so we need to store the +        // param names in a separate static list!. +        const bool isEnum = entry->paramType == QLatin1String("Enum"); +        const QString arg = isEnum ? entry->paramValues[i] : QString::number(i); + +        QString paramName = entry->paramName; + +        stream() << "  addItem( " << itemVarStr << ", QStringLiteral( \""; +        stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg( arg ); +        stream() << "\" ) );" << endl; +    } +} + +void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry) +{ +    if (entry->group == mCurrentGroup) { +        return; +    } + +    // HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases. +    static bool first = true; +    if (!entry->group.isEmpty()) { +        if (!first) { +            stream() << endl; +        } +        first = false; +    } + +    mCurrentGroup = entry->group; +    stream() << "  setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );"; +    stream() << endl << endl; +} + +void KConfigSourceGenerator::doConstructor() +{ +    // Constructor +    stream() << cfg().className << "::" << cfg().className << "("; +    createConstructorParameterList(); +    stream() << " )" << endl; +    stream() << "  : "; +    createParentConstructorCall(); +    createInitializerList(); + +    stream() << "{" << endl; + +    if (cfg().parentInConstructor) { +        stream() << "  setParent(parent);" << endl; +    } + +    if (cfg().dpointer) { +        stream() << "  d = new " << cfg().className << "Private;" << endl; +        if (parseResult.hasNonModifySignals) { +            stream() << "  " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;" << endl; +        } +    } + +    // Needed in case the singleton class is used as baseclass for +    // another singleton. +    if (cfg().singleton) { +        stream() << "  Q_ASSERT(!s_global" << cfg().className << "()->q);" << endl; +        stream() << "  s_global" << cfg().className << "()->q = this;" << endl; +    } + +    if (!parseResult.signalList.isEmpty()) { +        // this cast to base-class pointer-to-member is valid C++ +        // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ +        stream() << "  KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" +            << " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&" +            << cfg().className << "::itemChanged);" << endl; + +        stream() << endl; +    } + +    for (auto *entry : parseResult.entries) { +        handleCurrentGroupChange(entry); + +        const QString key = paramString(entry->key, parseResult.parameters); +        if (!entry->code.isEmpty()) { +            stream() << entry->code << endl; +        } +        createEnums(entry); + +        if (!cfg().dpointer) { +            stream() << itemDeclaration(entry, cfg()); +        } + +        if (entry->param.isEmpty()) { +            createNormalEntry(entry, key); +        } else { +            createIndexedEntry(entry, key); +        } +    } + +    stream() << "}" << endl << endl; +} + +void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry) +{ +    // Accessor +    if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { +        stream() << enumType(entry, cfg().globalEnums); +    } else { +        stream() << cppType(entry->type); +    } + +    stream() << " " << getFunction(entry->name, cfg().className) << "("; +    if (!entry->param.isEmpty()) { +        stream() << " " << cppType(entry->paramType) << " i "; +    } +    stream() << ")" << Const() << endl; + +    // function body inline only if not using dpointer +    // for BC mode +    startScope(); +    // HACK: Fix memberAccessorBody +    stream() << "  " << memberAccessorBody(entry, cfg().globalEnums); +    endScope(); +    stream() << endl; +} + +void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry) +{ +    // Manipulator +    if (!(cfg().allMutators || cfg().mutators.contains(entry->name))) { +        return; +    } + +    stream() << "void " << setFunction(entry->name, cfg().className) << "( "; +    if (!entry->param.isEmpty()) { +        stream() << cppType(entry->paramType) << " i, "; +    } + +    if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { +        stream() << enumType(entry, cfg().globalEnums); +    } else { +        stream() << param(entry->type); +    } +    stream() << " v )" << endl; + +    // function body inline only if not using dpointer +    // for BC mode +    startScope(); +    memberMutatorBody(entry); +    endScope(); +    stream() << endl; +} + +void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry) +{ +    // Item accessor +    if (!cfg().itemAccessors) { +        return; +    } +    stream() << endl; +    stream() << cfg().inherits << "::Item" << itemType(entry->type) << " *" +        << getFunction(entry->name, cfg().className) << "Item("; +    if (!entry->param.isEmpty()) { +        stream() << " " << cppType(entry->paramType) << " i "; +    } +    stream() << ")" << endl; +    startScope(); +    stream() << "  " << itemAccessorBody(entry, cfg()); +    endScope(); +} + +void KConfigSourceGenerator::doGetterSetterDPointerMode() +{ +    if (!cfg().dpointer) { +        return; +    } + +    // setters and getters go in Cpp if in dpointer mode +    for (auto *entry : parseResult.entries) { +        createSetterDPointerMode(entry); +        createGetterDPointerMode(entry); +        createItemGetterDPointerMode(entry); +        stream() << endl; +    } +} + +void KConfigSourceGenerator::createDefaultValueGetterSetter() +{ +    // default value getters always go in Cpp +    for (auto *entry : parseResult.entries) { +        QString n = entry->name; +        QString t = entry->type; + +        // Default value Accessor, as "helper" function +        if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) { +            stream() << cppType(t) << " " << getDefaultFunction(n, cfg().className) << "_helper("; +            if (!entry->param.isEmpty()) { +                stream() << " " << cppType(entry->paramType) << " i "; +            } +            stream() << ")" << Const() << endl; +            startScope(); +            stream() << memberGetDefaultBody(entry) << endl; +            endScope(); +            stream() << endl; +        } +    } +} + +void KConfigSourceGenerator::createDestructor() +{ +    stream() << cfg().className << "::~" << cfg().className << "()" << endl; +    startScope(); +    if (cfg().dpointer) { +        stream() << "  delete d;" << endl; +    } +    if (cfg().singleton) { +        stream() << "  s_global" << cfg().className << "()->q = nullptr;" << endl; +    } +    endScope(); +    stream() << endl; +} + +void KConfigSourceGenerator::createNonModifyingSignalsHelper() +{ +    if (!parseResult.hasNonModifySignals) { +        return; +    } +    stream() << "bool " << cfg().className << "::" << "usrSave()" << endl; +    startScope(); +    stream() << "  const bool res = " << cfg().inherits << "::usrSave();" << endl; +    stream() << "  if (!res) return false;" << endl << endl; +    for (const Signal &signal : parseResult.signalList) { +        if (signal.modify) { +            continue; +        } + +        stream() << "  if ( " << varPath(QStringLiteral("settingsChanged"), cfg()) << " & " << signalEnumName(signal.name) << " )" << endl; +        stream() << "    Q_EMIT " << signal.name << "("; +        QList<Param>::ConstIterator it, itEnd = signal.arguments.constEnd(); +        for (it = signal.arguments.constBegin(); it != itEnd;) { +            Param argument = *it; +            bool cast = false; +            if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) { +                for (int i = 0, end = parseResult.entries.count(); i < end; ++i) { +                    if (parseResult.entries.at(i)->name == argument.name) { +                        stream() << "static_cast<" << enumType(parseResult.entries.at(i), cfg().globalEnums) << ">("; +                        cast = true; +                        break; +                    } +                } +            } +            stream() << varPath(argument.name, cfg()); +            if (cast) { +                stream() << ")"; +            } +            if (++it != itEnd) { +                stream() << ", "; +            } +        } + +        stream() << ");" << endl; +    } + +    stream() << "  " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;" << endl; +    stream() << "  return true;" << endl; +    endScope(); +} + +void KConfigSourceGenerator::createSignalFlagsHandler()  +{ +    if (parseResult.signalList.isEmpty()) { +        return; +    } + +    stream() << endl; +    stream() << "void " << cfg().className << "::" << "itemChanged(quint64 flags) {" << endl; +    if (parseResult.hasNonModifySignals) +        stream() << "  " << varPath(QStringLiteral("settingsChanged"), cfg()) << " |= flags;" << endl; + +    if (!parseResult.signalList.isEmpty()) +        stream() << endl; + +    for (const Signal &signal : parseResult.signalList) { +        if (signal.modify) { +            stream() << "  if ( flags & " << signalEnumName(signal.name) << " ) {" << endl; +            stream() << "    Q_EMIT " << signal.name << "();" << endl; +            stream() << "  }" << endl; +        } +    } + +    stream() << "}" << endl; +} + +void KConfigSourceGenerator::includeMoc() { +    const QString mocFileName = cfg().baseName + QStringLiteral(".moc"); + +    if (parseResult.signalList.count() || cfg().generateProperties) { +        // Add includemoc if they are signals defined. +        stream() << endl; +        stream() << "#include \"" << mocFileName << "\"" << endl; +        stream() << endl; +    } +} diff --git a/src/kconfig_compiler/KConfigSourceGenerator.h b/src/kconfig_compiler/KConfigSourceGenerator.h new file mode 100644 index 00000000..510d4048 --- /dev/null +++ b/src/kconfig_compiler/KConfigSourceGenerator.h @@ -0,0 +1,86 @@ +/* +    This file is part of KDE. + +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGSOURCEGENERATOR_H +#define KCONFIGSOURCEGENERATOR_H + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigCommonStructs.h" + +#include <QString> +#include <QList> + +class KConfigParameters; +class CfgEntry; +class QTextStream; +struct ParseResult; + +class KConfigSourceGenerator : public KConfigCodeGeneratorBase { +public: +    KConfigSourceGenerator( +        const QString &inputFile, +        const QString &baseDir, +        const KConfigParameters ¶meters, +        ParseResult &parseResult); + +    void start() override; + +private: +    // Those are fairly self contained functions. +    void createHeaders(); +    void createPrivateDPointerImplementation(); +    void createSingletonImplementation(); +    void createPreamble(); +    void createDestructor(); +    void createConstructorParameterList(); +    void createParentConstructorCall(); +    void createInitializerList(); +    void createDefaultValueGetterSetter(); +    void createNonModifyingSignalsHelper(); +    void createSignalFlagsHandler(); +    void includeMoc(); + +    // Constructor related methods +    // the `do` methods have related helper functions that are only related +    // to it. So we can break the function into many smaller ones and create +    // logic inside of the `do` function. +    void doConstructor(); +    void createEnums(const CfgEntry *entry); +    void createNormalEntry(const CfgEntry *entry, const QString &key); +    void createIndexedEntry(const CfgEntry *entry, const QString &key); +    void handleCurrentGroupChange(const CfgEntry *entry); + +    void doGetterSetterDPointerMode(); +    void createGetterDPointerMode(const CfgEntry *entry); +    void createSetterDPointerMode(const CfgEntry *entry); +    void createItemGetterDPointerMode(const CfgEntry *entry); + +private: +    QString mCurrentGroup; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigXmlParser.cpp b/src/kconfig_compiler/KConfigXmlParser.cpp new file mode 100644 index 00000000..11c76abd --- /dev/null +++ b/src/kconfig_compiler/KConfigXmlParser.cpp @@ -0,0 +1,564 @@ +/* This file is part of the KDE libraries +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#include "KConfigXmlParser.h" +#include "KConfigParameters.h" + +#include <QDomAttr> +#include <QDomElement> +#include <QDomNode> +#include <QFile> +#include <QList> +#include <QStringList> +#include <QTextStream> + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +//TODO: Move preprocessDefault to Header / CPP implementation. +// it makes no sense for a parser to process those values and generate code. + +static void preProcessDefault(QString &defaultValue, const QString &name, +                              const QString &type, +                              const CfgEntry::Choices &choices, +                              QString &code, const KConfigParameters &cfg) +{ +    if (type == QLatin1String("String") && !defaultValue.isEmpty()) { +        defaultValue = literalString(defaultValue); + +    } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { +        defaultValue = literalString(defaultValue); +    } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { +        // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. +        defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); +    } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { +        QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); +        if (!code.isEmpty()) { +            cpp << endl; +        } + +        if (type == QLatin1String("UrlList")) { +            cpp << "  QList<QUrl> default" << name << ";" << endl; +        } else { +            cpp << "  QStringList default" << name << ";" << endl; +        } +        const QStringList defaults = defaultValue.split(QLatin1Char(',')); +        QStringList::ConstIterator it; +        for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { +            cpp << "  default" << name << ".append( "; +            if (type == QLatin1String("UrlList")) { +                cpp << "QUrl::fromUserInput("; +            } +            cpp << "QString::fromUtf8( \"" << *it << "\" ) "; +            if (type == QLatin1String("UrlList")) { +                cpp << ") "; +            } +            cpp << ");" << endl; +        } +        defaultValue = QLatin1String("default") + name; + +    } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { +        const QRegularExpression colorRe(QRegularExpression::anchoredPattern( +                                         QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); + +        if (colorRe.match(defaultValue).hasMatch()) { +            defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); +        } else { +            defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); +        } + +    } else if (type == QLatin1String("Enum")) { +        QList<CfgEntry::Choice>::ConstIterator it; +        for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { +            if ((*it).name == defaultValue) { +                if (cfg.globalEnums && choices.name().isEmpty()) { +                    defaultValue.prepend(choices.prefix); +                } else { +                    defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); +                } +                break; +            } +        } + +    } else if (type == QLatin1String("IntList")) { +        QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); +        if (!code.isEmpty()) { +            cpp << endl; +        } + +        cpp << "  QList<int> default" << name << ";" << endl; +        if (!defaultValue.isEmpty()) { +            const QStringList defaults = defaultValue.split(QLatin1Char(',')); +            QStringList::ConstIterator it; +            for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { +                cpp << "  default" << name << ".append( " << *it << " );" +                    << endl; +            } +        } +        defaultValue = QLatin1String("default") + name; +    } +} + +static QString dumpNode(const QDomNode &node) +{ +    QString msg; +    QTextStream s(&msg, QIODevice::WriteOnly); +    node.save(s, 0); + +    msg = msg.simplified(); +    if (msg.length() > 40) { +        return msg.left(37) + QLatin1String("..."); +    } +    return msg; +} + +void KConfigXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e) +{ +    readEntry.param = e.attribute(QStringLiteral("name")); +    readEntry.paramType = e.attribute(QStringLiteral("type")); +     +    if (readEntry.param.isEmpty()) { +        cerr << "Parameter must have a name: " << dumpNode(e) << endl; +        exit (1); +    } +     +    if (readEntry.paramType.isEmpty()) { +        cerr << "Parameter must have a type: " << dumpNode(e) << endl; +        exit(1); +    } + +    if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) { +        bool ok; +        readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); +        if (!ok) { +            cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << dumpNode(e) << endl; +            exit(1); +        } +    } else if (readEntry.paramType == QLatin1String("Enum")) { +        for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { +            if (e2.tagName() == QLatin1String("values")) { +                for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { +                    if (e3.tagName() == QLatin1String("value")) { +                        readEntry.paramValues.append(e3.text()); +                    } +                } +                break; +            } +        } +        if (readEntry.paramValues.isEmpty()) { +            cerr << "No values specified for parameter '" << readEntry.param << "'." << endl; +            exit(1); +        } +        readEntry.paramMax = readEntry.paramValues.count() - 1; +    } else { +        cerr << "Parameter '" << readEntry.param << "' has type " << readEntry.paramType  +            << " but must be of type int, uint or Enum." << endl; +        exit(1); +    } +} + +bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element) +{ +    for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { +        if (e.attribute(QStringLiteral("param")).isEmpty()) { +            if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { +                return true; +            } +        } +    } +    return false; +} + + +void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e) +{ +    QList<CfgEntry::Choice> chlist; +    for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { +        if (e2.tagName() != QLatin1String("choice")) { +            continue; +        } +        CfgEntry::Choice choice; +        choice.name = e2.attribute(QStringLiteral("name")); +        if (choice.name.isEmpty()) { +            cerr << "Tag <choice> requires attribute 'name'." << endl; +        } +        for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { +            if (e3.tagName() == QLatin1String("label")) { +                choice.label = e3.text(); +                choice.context = e3.attribute(QStringLiteral("context")); +            } +            if (e3.tagName() == QLatin1String("tooltip")) { +                choice.toolTip = e3.text(); +                choice.context = e3.attribute(QStringLiteral("context")); +            } +            if (e3.tagName() == QLatin1String("whatsthis")) { +                choice.whatsThis = e3.text(); +                choice.context = e3.attribute(QStringLiteral("context")); +            } +        } +        chlist.append(choice); +    } +     +    QString name = e.attribute(QStringLiteral("name")); +    QString prefix = e.attribute(QStringLiteral("prefix")); + +    readEntry.choices = CfgEntry::Choices(chlist, name, prefix); +} + +void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) +{ +    for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { +        QString tag = e.tagName(); +        if (tag == QLatin1String("label")) { +            readEntry.label = e.text(); +            readEntry.labelContext = e.attribute(QStringLiteral("context")); +        } else if (tag == QLatin1String("tooltip")) { +            readEntry.toolTip = e.text(); +            readEntry.toolTipContext = e.attribute(QStringLiteral("context")); +        } else if (tag == QLatin1String("whatsthis")) { +            readEntry.whatsThis = e.text(); +            readEntry.whatsThisContext = e.attribute(QStringLiteral("context")); +        } else if (tag == QLatin1String("min")) { +            readEntry.min = e.text(); +        } else if (tag == QLatin1String("max")) { +            readEntry.max = e.text(); +        } else if (tag == QLatin1String("code")) { +            readEntry.code = e.text(); +        } else if (tag == QLatin1String("parameter")) { +            readParameterFromEntry(readEntry, e); +        } else if (tag == QLatin1String("default")) { +            if (e.attribute(QStringLiteral("param")).isEmpty()) { +                readEntry.defaultValue = e.text(); +            } +        } else if (tag == QLatin1String("choices")) { +            readChoicesFromEntry(readEntry, e); +        } else if (tag == QLatin1String("emit")) { +            Signal signal; +            signal.name = e.attribute(QStringLiteral("signal")); +            readEntry.signalList.append(signal); +        } +    } +} + +void KConfigXmlParser::createChangedSignal(CfgEntry &readEntry) +{ +    if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) { +        Signal s; +        s.name = changeSignalName(readEntry.name); +        s.modify = true; +        readEntry.signalList.append(s); +    } +} + +void KConfigXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element) +{ +    bool nameIsEmpty = readEntry.name.isEmpty(); +    if (nameIsEmpty && readEntry.key.isEmpty()) { +        cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; +        exit (1); +    } + +    if (readEntry.key.isEmpty()) { +        readEntry.key = readEntry.name; +    } + +    if (nameIsEmpty) { +        readEntry.name = readEntry.key; +        readEntry.name.remove(QLatin1Char(' ')); +    } else if (readEntry.name.contains(QLatin1Char(' '))) { +        cout << "Entry '" << readEntry.name << "' contains spaces! <name> elements can not contain spaces!" << endl; +        readEntry.name.remove(QLatin1Char(' ')); +    } + +    if (readEntry.name.contains(QStringLiteral("$("))) { +        if (readEntry.param.isEmpty()) { +            cerr << "Name may not be parameterized: " << readEntry.name << endl; +            exit (1); +        } +    } else { +        if (!readEntry.param.isEmpty()) { +            cerr << "Name must contain '$(" << readEntry.param << ")': " << readEntry.name << endl; +            exit (1); +        } +    } +} + +void KConfigXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element) +{ +    if (readEntry.param.isEmpty()) { +        return; +    } +    // Adjust name +    readEntry.paramName = readEntry.name; + +    readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')')); +    // Lookup defaults for indexed entries +    for (int i = 0; i <= readEntry.paramMax; i++) { +        readEntry.paramDefaultValues.append(QString()); +    } + +    for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { +        QString tag = e.tagName(); +        if (tag != QLatin1String("default")) { +            continue; +        } +        QString index = e.attribute(QStringLiteral("param")); +        if (index.isEmpty()) { +            continue; +        } + +        bool ok; +        int i = index.toInt(&ok); +        if (!ok) { +            i = readEntry.paramValues.indexOf(index); +            if (i == -1) { +                cerr << "Index '" << index << "' for default value is unknown." << endl; +                exit (1); +            } +        } + +        if ((i < 0) || (i > readEntry.paramMax)) { +            cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << endl; +            exit (1); +        } + +        QString tmpDefaultValue = e.text(); + +        if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { +            preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); +        } + +        readEntry.paramDefaultValues[i] = tmpDefaultValue; +    } +} + +CfgEntry *KConfigXmlParser::parseEntry(const QString &group, const QDomElement &element) +{ +    CfgEntry readEntry; +    readEntry.type = element.attribute(QStringLiteral("type")); +    readEntry.name = element.attribute(QStringLiteral("name")); +    readEntry.key = element.attribute(QStringLiteral("key")); +    readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");; +    readEntry.group = group; + +    const bool nameIsEmpty = readEntry.name.isEmpty(); + +    readGroupElements(readEntry, element); + +    createChangedSignal(readEntry); +    validateNameAndKey(readEntry, element); + +    if (readEntry.label.isEmpty()) { +        readEntry.label = readEntry.key; +    } + +    if (readEntry.type.isEmpty()) { +        readEntry.type = QStringLiteral("String");    // XXX : implicit type might be bad +    } + +    readParamDefaultValues(readEntry, element); + +    if (!mValidNameRegexp.match(readEntry.name).hasMatch()) { +        if (nameIsEmpty) +            cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " +                 "it is not a valid name. You need to specify a valid name for this entry." << endl; +        else { +            cerr << "The name '" << readEntry.name << "' is not a valid name for an entry." << endl; +        } +        exit (1); +    } + +    if (mAllNames.contains(readEntry.name)) { +        if (nameIsEmpty) +            cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " +                 "it does not result in a unique name. You need to specify a unique name for this entry." << endl; +        else { +            cerr << "The name '" << readEntry.name << "' is not unique." << endl; +        } +        exit (1); +    } + +    mAllNames.append(readEntry.name); + +    if (!hasDefaultCode(readEntry, element)) { +        // TODO: Move all the options to CfgEntry. +        preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); +    } + +    // TODO: Try to Just return the CfgEntry we populated instead of  +    // creating another one to fill the code. +    CfgEntry *result = new CfgEntry(); +    result->group = readEntry.group; +    result->type = readEntry.type; +    result->key = readEntry.key; +    result->name = readEntry.name; +    result->labelContext = readEntry.labelContext; +    result->label = readEntry.label; +    result->toolTipContext = readEntry.toolTipContext; +    result->toolTip = readEntry.toolTip; +    result->whatsThisContext = readEntry.whatsThisContext; +    result->whatsThis = readEntry.whatsThis; +    result->code = readEntry.code; +    result->defaultValue = readEntry.defaultValue; +    result->choices = readEntry.choices; +    result->signalList = readEntry.signalList; +    result->hidden = readEntry.hidden; + +    if (!readEntry.param.isEmpty()) { +        result->param = readEntry.param; +        result->paramName = readEntry.paramName; +        result->paramType = readEntry.paramType; +        result->paramValues = readEntry.paramValues; +        result->paramDefaultValues = readEntry.paramDefaultValues; +        result->paramMax = readEntry.paramMax; +    } +    result->min = readEntry.min; +    result->max = readEntry.max; + +    return result; +} + +// TODO: Change the name of the config variable. +KConfigXmlParser::KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName) +    : cfg(cfg), mInputFileName(inputFileName) +{ +    mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); +} +  +void KConfigXmlParser::start() +{ +    QFile input(mInputFileName); +    QDomDocument doc; +    QString errorMsg; +    int errorRow; +    int errorCol; +    if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { +        cerr << "Unable to load document." << endl; +        cerr << "Parse error in " << mInputFileName << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; +        exit (1); +    } +     +    QDomElement cfgElement = doc.documentElement(); +    if (cfgElement.isNull()) { +        cerr << "No document in kcfg file" << endl; +        exit (1); +    } + +    for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) { +        QString tag = element.tagName(); + +        if (tag == QLatin1String("include")) { +            readIncludeTag(element); +        } else if (tag == QLatin1String("kcfgfile")) { +            readKcfgfileTag(element); +        } else if (tag == QLatin1String("group")) { +            readGroupTag(element); +        } else if (tag == QLatin1String("signal")) { +            readSignalTag(element); +        } +    } +} + +ParseResult KConfigXmlParser::getParseResult() const +{ +    return mParseResult; +} + +void KConfigXmlParser::readIncludeTag(const QDomElement &e) +{ +    QString includeFile = e.text(); +    if (!includeFile.isEmpty()) { +        mParseResult.includes.append(includeFile); +    } +} + +void KConfigXmlParser::readGroupTag(const QDomElement &e) +{ +    QString group = e.attribute(QStringLiteral("name")); +    if (group.isEmpty()) { +        cerr << "Group without name" << endl; +        exit (1); +    } + +    for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { +        if (e2.tagName() != QLatin1String("entry")) { +            continue; +        } +        CfgEntry *entry = parseEntry(group, e2); +        if (entry) { +            mParseResult.entries.append(entry); +        } else { +            cerr << "Can not parse entry." << endl; +            exit (1); +        } +    } +} + +void KConfigXmlParser::readKcfgfileTag(const QDomElement &e) +{ +    mParseResult.cfgFileName = e.attribute(QStringLiteral("name")); +    mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); +    for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { +        if (e2.tagName() == QLatin1String("parameter")) { +            Param p; +            p.name = e2.attribute(QStringLiteral("name")); +            p.type = e2.attribute(QStringLiteral("type")); +            if (p.type.isEmpty()) { +                p.type = QStringLiteral("String"); +            } +            mParseResult.parameters.append(p); +        } +    } +} + +void KConfigXmlParser::readSignalTag(const QDomElement &e) +{ +    QString signalName = e.attribute(QStringLiteral("name")); +    if (signalName.isEmpty()) { +        cerr << "Signal without name." << endl; +        exit (1); +    } +    Signal theSignal; +    theSignal.name = signalName; + +    for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { +        if (e2.tagName() == QLatin1String("argument")) { +            Param argument; +            argument.type = e2.attribute(QStringLiteral("type")); +            if (argument.type.isEmpty()) { +                cerr << "Signal argument without type." << endl; +                exit (1); +            } +            argument.name= e2.text(); +            theSignal.arguments.append(argument); +        } else if (e2.tagName() == QLatin1String("label")) { +            theSignal.label = e2.text(); +        } +    } +     +    mParseResult.signalList.append(theSignal); +} diff --git a/src/kconfig_compiler/KConfigXmlParser.h b/src/kconfig_compiler/KConfigXmlParser.h new file mode 100644 index 00000000..5dbd2745 --- /dev/null +++ b/src/kconfig_compiler/KConfigXmlParser.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE libraries +    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> +    Copyright (c) 2003 Waldo Bastian <bastian@kde.org> +    Copyright (c) 2003 Zack Rusin <zack@kde.org> +    Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> +    Copyright (c) 2008 Allen Winter <winter@kde.org> +    Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + +    This library is free software; you can redistribute it and/or +    modify it under the terms of the GNU Library General Public +    License as published by the Free Software Foundation; either +    version 2 of the License, or (at your option) any later version. + +    This library is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +    Library General Public License for more details. + +    You should have received a copy of the GNU Library General Public License +    along with this library; see the file COPYING.LIB.  If not, write to +    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +    Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGXMLPARSER_H +#define KCONFIGXMLPARSER_H + +#include <QDomDocument> +#include <QString> +#include <QRegularExpression> + +#include "KConfigCommonStructs.h" +#include "KConfigParameters.h" + +/* This parses the contents of a Xml file into a ParseResult Structure, + * It also fails hard: + * If start() succeeds, you can use the result, + * if start() fails, the program aborts with an error message so there's + * no possibility of generating incorrect code information. + */ +class KConfigXmlParser +{ +public: +    KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName); + +    // Start the parser and reads the contents of the inputFileName into the ParseResult Structure +    void start(); + +    // Get the result of the parse +    ParseResult getParseResult() const; + +private: +    // creates a `somethingChanged` signal for every property +    void createChangedSignal(CfgEntry &readEntry); + +    void validateNameAndKey(CfgEntry &readEntry, const QDomElement &element); + +    // TODO: Use std::optional and CfgEntry (without heap allocation) for this function +    // *or* fail hard if the parse fails. +    CfgEntry *parseEntry(const QString &group, const QDomElement &element); + +    // Steps +    void readIncludeTag(const QDomElement &element); +    void readGroupTag(const QDomElement &element); +    void readKcfgfileTag(const QDomElement &element); +    void readSignalTag(const QDomElement &element); + +    // Those are the Entries in the Xml, that represent a parameter within the <group> </group> tag. +    void readParameterFromEntry(CfgEntry &entry, const QDomElement &element); +    bool hasDefaultCode(CfgEntry &entry, const QDomElement &element); +    void readChoicesFromEntry(CfgEntry &entry, const QDomElement &element); +    void readGroupElements(CfgEntry &entry, const QDomElement &element); +    void readParamDefaultValues(CfgEntry &entry, const QDomElement &element); + +private: +    ParseResult mParseResult; +    KConfigParameters cfg; +    QString mInputFileName; +    QStringList mAllNames; +    QRegularExpression mValidNameRegexp; +}; + +#endif diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp index 848595f2..146ff884 100644 --- a/src/kconfig_compiler/kconfig_compiler.cpp +++ b/src/kconfig_compiler/kconfig_compiler.cpp @@ -34,15 +34,19 @@  #include <QDomAttr>  #include <QRegularExpression>  #include <QStringList> - -#include <qcommandlineparser.h> -#include <qcommandlineoption.h> +#include <QCommandLineParser> +#include <QCommandLineOption>  #include <ostream>  #include <iostream>  #include <stdlib.h>  #include "../../kconfig_version.h" +#include "KConfigParameters.h" +#include "KConfigCommonStructs.h" +#include "KConfigHeaderGenerator.h" +#include "KConfigSourceGenerator.h" +#include "KConfigXmlParser.h"  namespace  { @@ -50,444 +54,7 @@ QTextStream cout(stdout);  QTextStream cerr(stderr);  } -QStringList allNames; -QRegularExpression *validNameRegexp; -QString This; -QString Const; - -/** -   Configuration Compiler Configuration -*/ -class CfgConfig -{ -public: -    CfgConfig(const QString &codegenFilename) -    { -        // Configure the compiler with some settings -        QSettings codegenConfig(codegenFilename, QSettings::IniFormat); - -        nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString(); -        className = codegenConfig.value(QStringLiteral("ClassName")).toString(); -        if (className.isEmpty()) { -            cerr << "Class name missing" << endl; -            exit(1); -        } -        inherits = codegenConfig.value(QStringLiteral("Inherits")).toString(); -        if (inherits.isEmpty()) { -            inherits = QStringLiteral("KConfigSkeleton"); -        } -        visibility = codegenConfig.value(QStringLiteral("Visibility")).toString(); -        if (!visibility.isEmpty()) { -            visibility += ' '; -        } -        parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool(); -        forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool(); -        singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool(); -        staticAccessors = singleton; -        customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool(); -        memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString(); -        dpointer = (memberVariables == QLatin1String("dpointer")); -        headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList(); -        sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList(); -        mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList(); -        allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true"))); -        itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool(); -        setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool(); -        defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList(); -        allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true")); -        notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList(); -        allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true"))); -        globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool(); -        useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool(); -        const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower(); -        generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool(); -        if (trString == QLatin1String("kde")) { -            translationSystem = KdeTranslation; -            translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString(); -        } else { -            if (!trString.isEmpty() && trString != QLatin1String("qt")) { -                cerr << "Unknown translation system, falling back to Qt tr()" << endl; -            } -            translationSystem = QtTranslation; -        } -        qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString(); -        headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString(); -        sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString(); -    } - -public: -    enum TranslationSystem { -        QtTranslation, -        KdeTranslation -    }; - -    // These are read from the .kcfgc configuration file -    QString nameSpace;     // The namespace for the class to be generated -    QString className;     // The class name to be generated -    QString inherits;      // The class the generated class inherits (if empty, from KConfigSkeleton) -    QString visibility; -    bool parentInConstructor; // The class has the optional parent parameter in its constructor -    bool forceStringFilename; -    bool singleton;        // The class will be a singleton -    bool staticAccessors;  // provide or not static accessors -    bool customAddons; -    QString memberVariables; -    QStringList headerIncludes; -    QStringList sourceIncludes; -    QStringList mutators; -    QStringList defaultGetters; -    QStringList notifiers; -    QString qCategoryLoggingName; -    QString headerExtension; -    QString sourceExtension; -    bool allMutators; -    bool setUserTexts; -    bool allDefaultGetters; -    bool dpointer; -    bool globalEnums; -    bool useEnumTypes; -    bool itemAccessors; -    bool allNotifiers; -    TranslationSystem translationSystem; -    QString translationDomain; -    bool generateProperties; -}; - -struct SignalArguments { -    QString type; -    QString variableName; -}; - -class Signal -{ -public: -    Signal() : modify(false) {} - -    QString name; -    QString label; -    QList<SignalArguments> arguments; -    bool modify; -}; - -class CfgEntry -{ -public: -    struct Choice { -        QString name; -        QString context; -        QString label; -        QString toolTip; -        QString whatsThis; -    }; -    class Choices -    { -    public: -        Choices() {} -        Choices(const QList<Choice> &d, const QString &n, const QString &p) -            : prefix(p), choices(d), mName(n) -        { -            int i = n.indexOf(QLatin1String("::")); -            if (i >= 0) { -                mExternalQual = n.left(i + 2); -            } -        } -        QString prefix; -        QList<Choice> choices; -        const QString &name() const -        { -            return mName; -        } -        const QString &externalQualifier() const -        { -            return mExternalQual; -        } -        bool external() const -        { -            return !mExternalQual.isEmpty(); -        } -    private: -        QString mName; -        QString mExternalQual; -    }; - -    CfgEntry(const QString &group, const QString &type, const QString &key, -             const QString &name, const QString &labelContext, const QString &label, -             const QString &toolTipContext, const QString &toolTip, const QString &whatsThisContext, const QString &whatsThis, const QString &code, -             const QString &defaultValue, const Choices &choices, const QList<Signal> &signalList, -             bool hidden) -        : mGroup(group), mType(type), mKey(key), mName(name), -          mLabelContext(labelContext), mLabel(label), mToolTipContext(toolTipContext), mToolTip(toolTip), -          mWhatsThisContext(whatsThisContext), mWhatsThis(whatsThis), -          mCode(code), mDefaultValue(defaultValue), mChoices(choices), -          mSignalList(signalList), mParamMax(0), mHidden(hidden) -    { -    } - -    void setGroup(const QString &group) -    { -        mGroup = group; -    } -    QString group() const -    { -        return mGroup; -    } - -    void setType(const QString &type) -    { -        mType = type; -    } -    QString type() const -    { -        return mType; -    } - -    void setKey(const QString &key) -    { -        mKey = key; -    } -    QString key() const -    { -        return mKey; -    } - -    void setName(const QString &name) -    { -        mName = name; -    } -    QString name() const -    { -        return mName; -    } - -    void setLabelContext(const QString &labelContext) -    { -        mLabelContext = labelContext; -    } -    QString labelContext() const -    { -        return mLabelContext; -    } - -    void setLabel(const QString &label) -    { -        mLabel = label; -    } -    QString label() const -    { -        return mLabel; -    } - -    void setToolTipContext(const QString &toolTipContext) -    { -        mToolTipContext = toolTipContext; -    } -    QString toolTipContext() const -    { -        return mToolTipContext; -    } - -    void setToolTip(const QString &toolTip) -    { -        mToolTip = toolTip; -    } -    QString toolTip() const -    { -        return mToolTip; -    } - -    void setWhatsThisContext(const QString &whatsThisContext) -    { -        mWhatsThisContext = whatsThisContext; -    } -    QString whatsThisContext() const -    { -        return mWhatsThisContext; -    } - -    void setWhatsThis(const QString &whatsThis) -    { -        mWhatsThis = whatsThis; -    } -    QString whatsThis() const -    { -        return mWhatsThis; -    } - -    void setDefaultValue(const QString &d) -    { -        mDefaultValue = d; -    } -    QString defaultValue() const -    { -        return mDefaultValue; -    } - -    void setCode(const QString &d) -    { -        mCode = d; -    } -    QString code() const -    { -        return mCode; -    } - -    void setMinValue(const QString &d) -    { -        mMin = d; -    } -    QString minValue() const -    { -        return mMin; -    } - -    void setMaxValue(const QString &d) -    { -        mMax = d; -    } -    QString maxValue() const -    { -        return mMax; -    } - -    void setParam(const QString &d) -    { -        mParam = d; -    } -    QString param() const -    { -        return mParam; -    } - -    void setParamName(const QString &d) -    { -        mParamName = d; -    } -    QString paramName() const -    { -        return mParamName; -    } - -    void setParamType(const QString &d) -    { -        mParamType = d; -    } -    QString paramType() const -    { -        return mParamType; -    } - -    void setChoices(const QList<Choice> &d, const QString &n, const QString &p) -    { -        mChoices = Choices(d, n, p); -    } -    Choices choices() const -    { -        return mChoices; -    } - -    void setParamValues(const QStringList &d) -    { -        mParamValues = d; -    } -    QStringList paramValues() const -    { -        return mParamValues; -    } - -    void setParamDefaultValues(const QStringList &d) -    { -        mParamDefaultValues = d; -    } -    QString paramDefaultValue(int i) const -    { -        return mParamDefaultValues[i]; -    } - -    void setParamMax(int d) -    { -        mParamMax = d; -    } -    int paramMax() const -    { -        return mParamMax; -    } - -    void setSignalList(const QList<Signal> &value) -    { -        mSignalList = value; -    } -    QList<Signal> signalList() const -    { -        return mSignalList; -    } - -    bool hidden() const -    { -        return mHidden; -    } - -    void dump() const -    { -        cerr << "<entry>" << endl; -        cerr << "  group: " << mGroup << endl; -        cerr << "  type: " << mType << endl; -        cerr << "  key: " << mKey << endl; -        cerr << "  name: " << mName << endl; -        cerr << "  label context: " << mLabelContext << endl; -        cerr << "  label: " << mLabel << endl; -// whatsthis -        cerr << "  code: " << mCode << endl; -//      cerr << "  values: " << mValues.join(":") << endl; - -        if (!param().isEmpty()) { -            cerr << "  param name: " << mParamName << endl; -            cerr << "  param type: " << mParamType << endl; -            cerr << "  paramvalues: " << mParamValues.join(QChar::fromLatin1(':')) << endl; -        } -        cerr << "  default: " << mDefaultValue << endl; -        cerr << "  hidden: " << mHidden << endl; -        cerr << "  min: " << mMin << endl; -        cerr << "  max: " << mMax << endl; -        cerr << "</entry>" << endl; -    } - -private: -    QString mGroup; -    QString mType; -    QString mKey; -    QString mName; -    QString mLabelContext; -    QString mLabel; -    QString mToolTipContext; -    QString mToolTip; -    QString mWhatsThisContext; -    QString mWhatsThis; -    QString mCode; -    QString mDefaultValue; -    QString mParam; -    QString mParamName; -    QString mParamType; -    Choices mChoices; -    QList<Signal> mSignalList; -    QStringList mParamValues; -    QStringList mParamDefaultValues; -    int mParamMax; -    bool mHidden; -    QString mMin; -    QString mMax; -}; - -class Param -{ -public: -    QString name; -    QString type; -}; - -// returns the name of an member variable -// use itemPath to know the full path -// like using d-> in case of dpointer -static QString varName(const QString &n, const CfgConfig &cfg) +QString varName(const QString &n, const KConfigParameters &cfg)  {      QString result;      if (!cfg.dpointer) { @@ -500,7 +67,7 @@ static QString varName(const QString &n, const CfgConfig &cfg)      return result;  } -static QString varPath(const QString &n, const CfgConfig &cfg) +QString varPath(const QString &n, const KConfigParameters &cfg)  {      QString result;      if (cfg.dpointer) { @@ -511,14 +78,14 @@ static QString varPath(const QString &n, const CfgConfig &cfg)      return result;  } -static QString enumName(const QString &n) +QString enumName(const QString &n)  {      QString result = QLatin1String("Enum") + n;      result[4] = result[4].toUpper();      return result;  } -static QString enumName(const QString &n, const CfgEntry::Choices &c) +QString enumName(const QString &n, const CfgEntry::Choices &c)  {      QString result = c.name();      if (result.isEmpty()) { @@ -528,11 +95,11 @@ static QString enumName(const QString &n, const CfgEntry::Choices &c)      return result;  } -static QString enumType(const CfgEntry *e, bool globalEnums) +QString enumType(const CfgEntry *e, bool globalEnums)  { -    QString result = e->choices().name(); +    QString result = e->choices.name();      if (result.isEmpty()) { -        result = QLatin1String("Enum") + e->name(); +        result = QLatin1String("Enum") + e->name;          if (!globalEnums) {              result += QLatin1String("::type");          } @@ -541,7 +108,7 @@ static QString enumType(const CfgEntry *e, bool globalEnums)      return result;  } -static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) +QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)  {      QString result = c.name();      if (result.isEmpty()) { @@ -555,7 +122,7 @@ static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)      return result;  } -static QString setFunction(const QString &n, const QString &className = QString()) +QString setFunction(const QString &n, const QString &className)  {      QString result = QLatin1String("set") + n;      result[3] = result[3].toUpper(); @@ -566,12 +133,12 @@ static QString setFunction(const QString &n, const QString &className = QString(      return result;  } -static QString changeSignalName(const QString &n) +QString changeSignalName(const QString &n)  {      return n+QStringLiteral("Changed");  } -static QString getDefaultFunction(const QString &n, const QString &className = QString()) +QString getDefaultFunction(const QString &n, const QString &className)  {      QString result = QLatin1String("default") +  n + QLatin1String("Value");      result[7] = result[7].toUpper(); @@ -582,7 +149,7 @@ static QString getDefaultFunction(const QString &n, const QString &className = Q      return result;  } -static QString getFunction(const QString &n, const QString &className = QString()) +QString getFunction(const QString &n, const QString &className)  {      QString result = n;      result[0] = result[0].toLower(); @@ -593,7 +160,7 @@ static QString getFunction(const QString &n, const QString &className = QString(      return result;  } -static void addQuotes(QString &s) +void addQuotes(QString &s)  {      if (!s.startsWith(QLatin1Char('"'))) {          s.prepend(QLatin1Char('"')); @@ -613,7 +180,7 @@ static QString quoteString(const QString &s)      return QLatin1Char('\"') + r + QLatin1Char('\"');  } -static QString literalString(const QString &s) +QString literalString(const QString &s)  {      bool isAscii = true;      for (int i = s.length(); i--;) @@ -628,20 +195,8 @@ static QString literalString(const QString &s)      }  } -static QString dumpNode(const QDomNode &node) -{ -    QString msg; -    QTextStream s(&msg, QIODevice::WriteOnly); -    node.save(s, 0); - -    msg = msg.simplified(); -    if (msg.length() > 40) { -        return msg.left(37) + QLatin1String("..."); -    } -    return msg; -} -static QString signalEnumName(const QString &signalName) +QString signalEnumName(const QString &signalName)  {      QString result;      result = QLatin1String("signal") + signalName; @@ -650,345 +205,8 @@ static QString signalEnumName(const QString &signalName)      return result;  } -static void preProcessDefault(QString &defaultValue, const QString &name, -                              const QString &type, -                              const CfgEntry::Choices &choices, -                              QString &code, const CfgConfig &cfg) -{ -    if (type == QLatin1String("String") && !defaultValue.isEmpty()) { -        defaultValue = literalString(defaultValue); - -    } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { -        defaultValue = literalString(defaultValue); -    } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { -        // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. -        defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); -    } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { -        QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); -        if (!code.isEmpty()) { -            cpp << endl; -        } - -        if (type == QLatin1String("UrlList")) { -            cpp << "  QList<QUrl> default" << name << ";" << endl; -        } else { -            cpp << "  QStringList default" << name << ";" << endl; -        } -        const QStringList defaults = defaultValue.split(QLatin1Char(',')); -        QStringList::ConstIterator it; -        for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { -            cpp << "  default" << name << ".append( "; -            if (type == QLatin1String("UrlList")) { -                cpp << "QUrl::fromUserInput("; -            } -            cpp << "QString::fromUtf8( \"" << *it << "\" ) "; -            if (type == QLatin1String("UrlList")) { -                cpp << ") "; -            } -            cpp << ");" << endl; -        } -        defaultValue = QLatin1String("default") + name; - -    } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { -        const QRegularExpression colorRe(QRegularExpression::anchoredPattern( -                                         QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); - -        if (colorRe.match(defaultValue).hasMatch()) { -            defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); -        } else { -            defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); -        } - -    } else if (type == QLatin1String("Enum")) { -        QList<CfgEntry::Choice>::ConstIterator it; -        for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { -            if ((*it).name == defaultValue) { -                if (cfg.globalEnums && choices.name().isEmpty()) { -                    defaultValue.prepend(choices.prefix); -                } else { -                    defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); -                } -                break; -            } -        } - -    } else if (type == QLatin1String("IntList")) { -        QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); -        if (!code.isEmpty()) { -            cpp << endl; -        } - -        cpp << "  QList<int> default" << name << ";" << endl; -        if (!defaultValue.isEmpty()) { -            const QStringList defaults = defaultValue.split(QLatin1Char(',')); -            QStringList::ConstIterator it; -            for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { -                cpp << "  default" << name << ".append( " << *it << " );" -                    << endl; -            } -        } -        defaultValue = QLatin1String("default") + name; -    } -} - -CfgEntry *parseEntry(const QString &group, const QDomElement &element, const CfgConfig &cfg) -{ -    bool defaultCode = false; -    QString type = element.attribute(QStringLiteral("type")); -    QString name = element.attribute(QStringLiteral("name")); -    QString key = element.attribute(QStringLiteral("key")); -    QString hidden = element.attribute(QStringLiteral("hidden")); -    QString labelContext; -    QString label; -    QString toolTipContext; -    QString toolTip; -    QString whatsThisContext; -    QString whatsThis; -    QString defaultValue; -    QString code; -    QString param; -    QString paramName; -    QString paramType; -    CfgEntry::Choices choices; -    QList<Signal> signalList; -    QStringList paramValues; -    QStringList paramDefaultValues; -    QString minValue; -    QString maxValue; -    int paramMax = 0; - -    for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { -        QString tag = e.tagName(); -        if (tag == QLatin1String("label")) { -            label = e.text(); -            labelContext = e.attribute(QStringLiteral("context")); -        } else if (tag == QLatin1String("tooltip")) { -            toolTip = e.text(); -            toolTipContext = e.attribute(QStringLiteral("context")); -        } else if (tag == QLatin1String("whatsthis")) { -            whatsThis = e.text(); -            whatsThisContext = e.attribute(QStringLiteral("context")); -        } else if (tag == QLatin1String("min")) { -            minValue = e.text(); -        } else if (tag == QLatin1String("max")) { -            maxValue = e.text(); -        } else if (tag == QLatin1String("code")) { -            code = e.text(); -        } else if (tag == QLatin1String("parameter")) { -            param = e.attribute(QStringLiteral("name")); -            paramType = e.attribute(QStringLiteral("type")); -            if (param.isEmpty()) { -                cerr << "Parameter must have a name: " << dumpNode(e) << endl; -                return nullptr; -            } -            if (paramType.isEmpty()) { -                cerr << "Parameter must have a type: " << dumpNode(e) << endl; -                return nullptr; -            } -            if ((paramType == QLatin1String("Int")) || (paramType == QLatin1String("UInt"))) { -                bool ok; -                paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); -                if (!ok) { -                    cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " -                         << dumpNode(e) << endl; -                    return nullptr; -                } -            } else if (paramType == QLatin1String("Enum")) { -                for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { -                    if (e2.tagName() == QLatin1String("values")) { -                        for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { -                            if (e3.tagName() == QLatin1String("value")) { -                                paramValues.append(e3.text()); -                            } -                        } -                        break; -                    } -                } -                if (paramValues.isEmpty()) { -                    cerr << "No values specified for parameter '" << param -                         << "'." << endl; -                    return nullptr; -                } -                paramMax = paramValues.count() - 1; -            } else { -                cerr << "Parameter '" << param << "' has type " << paramType -                     << " but must be of type int, uint or Enum." << endl; -                return nullptr; -            } -        } else if (tag == QLatin1String("default")) { -            if (e.attribute(QStringLiteral("param")).isEmpty()) { -                defaultValue = e.text(); -                if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { -                    defaultCode = true; -                } -            } -        } else if (tag == QLatin1String("choices")) { -            QString name = e.attribute(QStringLiteral("name")); -            QString prefix = e.attribute(QStringLiteral("prefix")); -            QList<CfgEntry::Choice> chlist; -            for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { -                if (e2.tagName() == QLatin1String("choice")) { -                    CfgEntry::Choice choice; -                    choice.name = e2.attribute(QStringLiteral("name")); -                    if (choice.name.isEmpty()) { -                        cerr << "Tag <choice> requires attribute 'name'." << endl; -                    } -                    for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { -                        if (e3.tagName() == QLatin1String("label")) { -                            choice.label = e3.text(); -                            choice.context = e3.attribute(QStringLiteral("context")); -                        } -                        if (e3.tagName() == QLatin1String("tooltip")) { -                            choice.toolTip = e3.text(); -                            choice.context = e3.attribute(QStringLiteral("context")); -                        } -                        if (e3.tagName() == QLatin1String("whatsthis")) { -                            choice.whatsThis = e3.text(); -                            choice.context = e3.attribute(QStringLiteral("context")); -                        } -                    } -                    chlist.append(choice); -                } -            } -            choices = CfgEntry::Choices(chlist, name, prefix); -        } else if (tag == QLatin1String("emit")) { -            Signal signal; -            signal.name = e.attribute(QStringLiteral("signal")); -            signalList.append(signal); -        } -    } - -    if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(name))) { -        Signal s; -        s.name = changeSignalName(name); -        s.modify = true; -        signalList.append(s); -    } - -    bool nameIsEmpty = name.isEmpty(); -    if (nameIsEmpty && key.isEmpty()) { -        cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; -        return nullptr; -    } - -    if (key.isEmpty()) { -        key = name; -    } - -    if (nameIsEmpty) { -        name = key; -        name.remove(' '); -    } else if (name.contains(' ')) { -        cout << "Entry '" << name << "' contains spaces! <name> elements can not contain spaces!" << endl; -        name.remove(' '); -    } - -    if (name.contains(QStringLiteral("$("))) { -        if (param.isEmpty()) { -            cerr << "Name may not be parameterized: " << name << endl; -            return nullptr; -        } -    } else { -        if (!param.isEmpty()) { -            cerr << "Name must contain '$(" << param << ")': " << name << endl; -            return nullptr; -        } -    } - -    if (label.isEmpty()) { -        label = key; -    } - -    if (type.isEmpty()) { -        type = QStringLiteral("String");    // XXX : implicit type might be bad -    } -    if (!param.isEmpty()) { -        // Adjust name -        paramName = name; -        name.remove("$(" + param + ')'); -        // Lookup defaults for indexed entries -        for (int i = 0; i <= paramMax; i++) { -            paramDefaultValues.append(QString()); -        } - -        for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { -            QString tag = e.tagName(); -            if (tag == QLatin1String("default")) { -                QString index = e.attribute(QStringLiteral("param")); -                if (index.isEmpty()) { -                    continue; -                } - -                bool ok; -                int i = index.toInt(&ok); -                if (!ok) { -                    i = paramValues.indexOf(index); -                    if (i == -1) { -                        cerr << "Index '" << index << "' for default value is unknown." << endl; -                        return nullptr; -                    } -                } - -                if ((i < 0) || (i > paramMax)) { -                    cerr << "Index '" << i << "' for default value is out of range [0, " << paramMax << "]." << endl; -                    return nullptr; -                } - -                QString tmpDefaultValue = e.text(); - -                if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { -                    preProcessDefault(tmpDefaultValue, name, type, choices, code, cfg); -                } - -                paramDefaultValues[i] = tmpDefaultValue; -            } -        } -    } - -    if (!validNameRegexp->match(name).hasMatch()) { -        if (nameIsEmpty) -            cerr << "The key '" << key << "' can not be used as name for the entry because " -                 "it is not a valid name. You need to specify a valid name for this entry." << endl; -        else { -            cerr << "The name '" << name << "' is not a valid name for an entry." << endl; -        } -        return nullptr; -    } - -    if (allNames.contains(name)) { -        if (nameIsEmpty) -            cerr << "The key '" << key << "' can not be used as name for the entry because " -                 "it does not result in a unique name. You need to specify a unique name for this entry." << endl; -        else { -            cerr << "The name '" << name << "' is not unique." << endl; -        } -        return nullptr; -    } -    allNames.append(name); - -    if (!defaultCode) { -        preProcessDefault(defaultValue, name, type, choices, code, cfg); -    } - -    CfgEntry *result = new CfgEntry(group, type, key, name, labelContext, label, toolTipContext, toolTip, whatsThisContext, whatsThis, -                                    code, defaultValue, choices, signalList, -                                    hidden == QLatin1String("true")); -    if (!param.isEmpty()) { -        result->setParam(param); -        result->setParamName(paramName); -        result->setParamType(paramType); -        result->setParamValues(paramValues); -        result->setParamDefaultValues(paramDefaultValues); -        result->setParamMax(paramMax); -    } -    result->setMinValue(minValue); -    result->setMaxValue(maxValue); - -    return result; -} - -static bool isUnsigned(const QString &type) +bool isUnsigned(const QString &type)  {      if (type == QLatin1String("UInt")) {          return true; @@ -1168,47 +386,47 @@ QString itemType(const QString &type)      return t;  } -static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg) +QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)  {      if (cfg.itemAccessors) {          return QString();      }      QString type; -    if (!e->signalList().isEmpty()) { +    if (!e->signalList.isEmpty()) {          type = QStringLiteral("KConfigCompilerSignallingItem");      } else { -        type = cfg.inherits + "::Item" + itemType(e->type()); +        type = cfg.inherits + "::Item" + itemType(e->type);      } -    QString fCap = e->name(); +    QString fCap = e->name;      fCap[0] = fCap[0].toUpper();      return "  " + type + "  *item" + fCap + -            ( (!e->param().isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax()+1)) : QString()) + ";\n"; +            ( (!e->param.isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax+1)) : QString()) + ";\n";  }  // returns the name of an item variable  // use itemPath to know the full path  // like using d-> in case of dpointer -static QString itemVar(const CfgEntry *e, const CfgConfig &cfg) +QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)  {      QString result;      if (cfg.itemAccessors) {          if (!cfg.dpointer) { -            result = 'm' + e->name() + "Item"; +            result = 'm' + e->name + "Item";              result[1] = result[1].toUpper();          } else { -            result = e->name() + "Item"; +            result = e->name + "Item";              result[0] = result[0].toLower();          }      } else { -        result = "item" + e->name(); +        result = "item" + e->name;          result[4] = result[4].toUpper();      }      return result;  } -static QString itemPath(const CfgEntry *e, const CfgConfig &cfg) +QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)  {      QString result;      if (cfg.dpointer) { @@ -1220,18 +438,18 @@ static QString itemPath(const CfgEntry *e, const CfgConfig &cfg)  }  QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue, -                const CfgConfig &cfg, const QString ¶m = QString()) { +                const KConfigParameters &cfg, const QString ¶m) { -    QList<Signal> sigs = entry->signalList(); +    QList<Signal> sigs = entry->signalList;      QString t;      if (!sigs.isEmpty()) {          t += QLatin1String("new KConfigCompilerSignallingItem(");      } -    t += "new "+ cfg.inherits + "::Item" + itemType(entry->type()) + "( currentGroup(), " -            + key + ", " + varPath( entry->name(), cfg ) + param; +    t += "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), " +            + key + ", " + varPath( entry->name, cfg ) + param; -    if (entry->type() == QLatin1String("Enum")) { -        t += ", values" + entry->name(); +    if (entry->type == QLatin1String("Enum")) { +        t += ", values" + entry->name;      }      if (!defaultValue.isEmpty()) {          t += QLatin1String(", ") + defaultValue; @@ -1255,11 +473,11 @@ QString newItem(const CfgEntry* entry, const QString &key, const QString& defaul  QString paramString(const QString &s, const CfgEntry *e, int i)  {      QString result = s; -    QString needle = "$(" + e->param() + ')'; +    QString needle = "$(" + e->param + ')';      if (result.contains(needle)) {          QString tmp; -        if (e->paramType() == QLatin1String("Enum")) { -            tmp = e->paramValues().at(i); +        if (e->paramType == QLatin1String("Enum")) { +            tmp = e->paramValues.at(i);          } else {              tmp = QString::number(i);          } @@ -1289,12 +507,12 @@ QString paramString(const QString &group, const QList<Param> ¶meters)      return "QStringLiteral( \"" + paramString + "\" )" + arguments;  } -QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString ¶m = QString(), const QString ¶mValue = QString()) +QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue)  {      QString result;      switch (cfg.translationSystem) { -    case CfgConfig::QtTranslation: +    case KConfigParameters::QtTranslation:          if (!context.isEmpty()) {              result += "/*: " + context + " */ QCoreApplication::translate(\"";          } else { @@ -1303,7 +521,7 @@ QString translatedString(const CfgConfig &cfg, const QString &string, const QStr          result += cfg.className + "\", ";          break; -    case CfgConfig::KdeTranslation: +    case KConfigParameters::KdeTranslation:          if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {              result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", ";          } else if (!cfg.translationDomain.isEmpty()) { @@ -1330,62 +548,36 @@ QString translatedString(const CfgConfig &cfg, const QString &string, const QStr  }  /* int i is the value of the parameter */ -QString userTextsFunctions(CfgEntry *e, const CfgConfig &cfg, QString itemVarStr = QString(), const QString &i = QString()) +QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)  {      QString txt;      if (itemVarStr.isNull()) {          itemVarStr = itemPath(e, cfg);      } -    if (!e->label().isEmpty()) { +    if (!e->label.isEmpty()) {          txt += "  " + itemVarStr + "->setLabel( "; -        txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i); +        txt += translatedString(cfg, e->label, e->labelContext, e->param, i);          txt += QLatin1String(" );\n");      } -    if (!e->toolTip().isEmpty()) { +    if (!e->toolTip.isEmpty()) {          txt += "  " + itemVarStr + "->setToolTip( "; -        txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i); +        txt += translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i);          txt += QLatin1String(" );\n");      } -    if (!e->whatsThis().isEmpty()) { +    if (!e->whatsThis.isEmpty()) {          txt += "  " + itemVarStr + "->setWhatsThis( "; -        txt += translatedString(cfg, e->whatsThis(), e->whatsThisContext(), e->param(), i); +        txt += translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i);          txt += QLatin1String(" );\n");      }      return txt;  } -// returns the member accesor implementation -// which should go in the h file if inline -// or the cpp file if not inline -QString memberAccessorBody(CfgEntry *e, bool globalEnums, const CfgConfig &cfg) -{ -    QString result; -    QTextStream out(&result, QIODevice::WriteOnly); -    QString n = e->name(); -    QString t = e->type(); -    bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum"); - -    out << "return "; -    if (useEnumType) { -        out << "static_cast<" << enumType(e, globalEnums) << ">("; -    } -    out << This << varPath(n, cfg); -    if (!e->param().isEmpty()) { -        out << "[i]"; -    } -    if (useEnumType) { -        out << ")"; -    } -    out << ";" << endl; - -    return result; -}  // returns the member mutator implementation  // which should go in the h file if inline  // or the cpp file if not inline - -void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &n) +//TODO: Fix add Debug Method, it should also take the debug string. +void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)  {      if (cfg.qCategoryLoggingName.isEmpty()) {         out << "  qDebug() << \"" << setFunction(n); @@ -1394,101 +586,30 @@ void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &n)      }  } -QString memberMutatorBody(CfgEntry *e, const CfgConfig &cfg) -{ -    QString result; -    QTextStream out(&result, QIODevice::WriteOnly); -    QString n = e->name(); -    QString t = e->type(); - -    if (!e->minValue().isEmpty()) { -        if (e->minValue() != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) -            out << "if (v < " << e->minValue() << ")" << endl; -            out << "{" << endl; -            addDebugMethod(out, cfg, n); -            out << ": value \" << v << \" is less than the minimum value of "; -            out << e->minValue() << "\";" << endl; -            out << "  v = " << e->minValue() << ";" << endl; -            out << "}" << endl; -        } -    } - -    if (!e->maxValue().isEmpty()) { -        out << endl << "if (v > " << e->maxValue() << ")" << endl; -        out << "{" << endl; -        addDebugMethod(out, cfg, n); -        out << ": value \" << v << \" is greater than the maximum value of "; -        out << e->maxValue() << "\";" << endl; -        out << "  v = " << e->maxValue() << ";" << endl; -        out << "}" << endl << endl; -    } - -    const QString varExpression = This + varPath(n, cfg) + (e->param().isEmpty() ? QString() : QStringLiteral("[i]")); - -    const bool hasBody = !e->signalList().empty() || cfg.generateProperties; -    out << "if ("; -    if (hasBody) { -        out << "v != " << varExpression << " && "; -    } -    out << "!" << This << "isImmutable( QStringLiteral( \""; -    if (!e->param().isEmpty()) { -        out << e->paramName().replace("$(" + e->param() + ")", QLatin1String("%1")) << "\" ).arg( "; -        if (e->paramType() == QLatin1String("Enum")) { -            out << "QLatin1String( "; - -            if (cfg.globalEnums) { -                out << enumName(e->param()) << "ToString[i]"; -            } else { -                out << enumName(e->param()) << "::enumToString[i]"; -            } - -            out << " )"; -        } else { -            out << "i"; -        } -        out << " )"; -    } else { -        out << n << "\" )"; -    } -    out << " ))" << (hasBody ? " {" : "") << endl; -    out << "  " << varExpression << " = v;" << endl; - -    const auto listSignal = e->signalList(); -    for (const Signal &signal : listSignal) { -        if (signal.modify) { -            out << "  Q_EMIT " << This << signal.name << "();" << endl; -        } else { -            out << "  " << This << varPath(QStringLiteral("settingsChanged"), cfg) << " |= " << signalEnumName(signal.name) << ";" << endl; -        } -    } -    if (hasBody) { -        out << "}" << endl; -    } - -    return result; -}  // returns the member get default implementation  // which should go in the h file if inline  // or the cpp file if not inline -QString memberGetDefaultBody(CfgEntry *e) +QString memberGetDefaultBody(const CfgEntry *e)  { -    QString result = e->code(); +    QString result = e->code;      QTextStream out(&result, QIODevice::WriteOnly);      out << endl; -    if (!e->param().isEmpty()) { +    if (!e->param.isEmpty()) {          out << "  switch (i) {" << endl; -        for (int i = 0; i <= e->paramMax(); ++i) { -            if (!e->paramDefaultValue(i).isEmpty()) { -                out << "  case " << i << ": return " << e->paramDefaultValue(i) << ';' << endl; +        for (int i = 0; i <= e->paramMax; ++i) { +            if (!e->paramDefaultValues[i].isEmpty()) { +                out << "  case " << i << ": return " << e->paramDefaultValues[i] << ';' << endl;              }          } +        QString defaultValue = e->defaultValue; +          out << "  default:" << endl; -        out << "    return " << e->defaultValue().replace("$(" + e->param() + ')', QLatin1String("i")) << ';' << endl; +        out << "    return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ';' << endl;          out << "  }" << endl;      } else { -        out << "  return " << e->defaultValue() << ';'; +        out << "  return " << e->defaultValue << ';';      }      return result; @@ -1497,13 +618,13 @@ QString memberGetDefaultBody(CfgEntry *e)  // returns the item accesor implementation  // which should go in the h file if inline  // or the cpp file if not inline -QString itemAccessorBody(CfgEntry *e, const CfgConfig &cfg) +QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)  {      QString result;      QTextStream out(&result, QIODevice::WriteOnly);      out << "return " << itemPath(e, cfg); -    if (!e->param().isEmpty()) { +    if (!e->param.isEmpty()) {          out << "[i]";      }      out << ";" << endl; @@ -1529,30 +650,42 @@ QString indent(QString text, int spaces)      return result;  } -// adds as many 'namespace foo {' lines to p_out as -// there are namespaces in p_ns -void beginNamespaces(const QString &p_ns, QTextStream &p_out) +bool hasErrors(KConfigXmlParser &parser, const ParseResult& parseResult, const KConfigParameters &cfg)  { -    if (!p_ns.isEmpty()) { -        const QStringList nameSpaces = p_ns.split(QStringLiteral("::")); -        for (const QString &ns : nameSpaces) { -            p_out << "namespace " << ns << " {" << endl; -        } -        p_out << endl; +    if (cfg.className.isEmpty()) { +        cerr << "Class name missing" << endl; +        return true;      } -} -// adds as many '}' lines to p_out as -// there are namespaces in p_ns -void endNamespaces(const QString &p_ns, QTextStream &p_out) -{ -    if (!p_ns.isEmpty()) { -        const int namespaceCount = p_ns.count(QStringLiteral("::")) + 1; -        for (int i = 0; i < namespaceCount; ++i) { -            p_out << "}" << endl; -        } -        p_out << endl; +    if (cfg.singleton && !parseResult.parameters.isEmpty()) { +        cerr << "Singleton class can not have parameters" << endl; +        return true;      } + +    if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) { +        cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; +        return true; +    } + +    /* TODO: For some reason some configuration files prefer to have *no* entries +    * at all in it, and the generated code is mostly bogus as KConfigXT will not +    * handle save / load / properties, etc, nothing. +    * +    * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop +    * project. +    * I think we should remove the possibility of creating configuration classes from configuration +    * files that don't really have configuration in it. but I'm changing this right now to allow +    * kdevelop files to pass. +    * +    * Remove for KDE 6 +    * (to make things more interesting, it failed in a code that's never used within KDevelop... ) +    */ +    if (parseResult.entries.isEmpty()) { +        cerr << "No entries." << endl; +        return false; +    } + +    return false;  }  int main(int argc, char **argv) @@ -1561,22 +694,22 @@ int main(int argc, char **argv)      app.setApplicationName(QStringLiteral("kconfig_compiler"));      app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING)); -    validNameRegexp = new QRegularExpression(QRegularExpression::anchoredPattern( -                                             QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); -      QString inputFilename, codegenFilename; +    QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, +            QCoreApplication::translate("main", "Directory to generate files in [.]"), +            QCoreApplication::translate("main", "directory"), QStringLiteral(".")); + +    QCommandLineOption licenseOption ( +        QStringList { QStringLiteral("l"), QStringLiteral("license") }, +        QCoreApplication::translate("main", "Display software license.")); +      QCommandLineParser parser;      parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));      parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file")); -    QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, -            QCoreApplication::translate("main", "Directory to generate files in [.]"), -            QCoreApplication::translate("main", "directory"), QStringLiteral("."));      parser.addOption(targetDirectoryOption); - -    QCommandLineOption licenseOption (QStringList { QStringLiteral("l"), QStringLiteral("license") }, QCoreApplication::translate("main", "Display software license."));      parser.addOption (licenseOption);      parser.addVersionOption(); @@ -1584,7 +717,7 @@ int main(int argc, char **argv)      parser.process(app);      if (parser.isSet(licenseOption)) { -	cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl; +	    cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl;          cout << "    Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << endl;          cout << "This program comes with ABSOLUTELY NO WARRANTY." << endl;          cout << "You may redistribute copies of this program" << endl; @@ -1595,16 +728,18 @@ int main(int argc, char **argv)      const QStringList args = parser.positionalArguments();      if (args.count() < 2) { -	cerr << "Too few arguments." << endl; -	return 1; +	    cerr << "Too few arguments." << endl; +	    return 1;      } +      if (args.count() > 2) { -	cerr << "Too many arguments." << endl; -	return 1; +    	cerr << "Too many arguments." << endl; +        return 1;      }      inputFilename = args.at(0);      codegenFilename = args.at(1); +    // TODO: Transform baseDir into a helper.      QString baseDir = parser.value(targetDirectoryOption);  #ifdef Q_OS_WIN @@ -1614,1124 +749,34 @@ int main(int argc, char **argv)  #endif          baseDir.append("/"); -    if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) { -        cerr << "Codegen options file must have extension .kcfgc" << endl; -        return 1; -    } -    QString baseName = QFileInfo(codegenFilename).fileName(); -    baseName = baseName.left(baseName.length() - 6); - -    CfgConfig cfg = CfgConfig(codegenFilename); - -    QFile input(inputFilename); - -    QDomDocument doc; -    QString errorMsg; -    int errorRow; -    int errorCol; -    if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { -        cerr << "Unable to load document." << endl; -        cerr << "Parse error in " << inputFilename << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; -        return 1; -    } - -    QDomElement cfgElement = doc.documentElement(); - -    if (cfgElement.isNull()) { -        cerr << "No document in kcfg file" << endl; -        return 1; -    } - -    QString cfgFileName; -    bool cfgFileNameArg = false; -    QList<Param> parameters; -    QList<Signal> signalList; -    QStringList includes; - -    QList<CfgEntry *> entries; - -    for (QDomElement e = cfgElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { -        QString tag = e.tagName(); - -        if (tag == QLatin1String("include")) { -            QString includeFile = e.text(); -            if (!includeFile.isEmpty()) { -                includes.append(includeFile); -            } - -        } else if (tag == QLatin1String("kcfgfile")) { -            cfgFileName = e.attribute(QStringLiteral("name")); -            cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); -            for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { -                if (e2.tagName() == QLatin1String("parameter")) { -                    Param p; -                    p.name = e2.attribute(QStringLiteral("name")); -                    p.type = e2.attribute(QStringLiteral("type")); -                    if (p.type.isEmpty()) { -                        p.type = QStringLiteral("String"); -                    } -                    parameters.append(p); -                } -            } - -        } else if (tag == QLatin1String("group")) { -            QString group = e.attribute(QStringLiteral("name")); -            if (group.isEmpty()) { -                cerr << "Group without name" << endl; -                return 1; -            } -            for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { -                if (e2.tagName() != QLatin1String("entry")) { -                    continue; -                } -                CfgEntry *entry = parseEntry(group, e2, cfg); -                if (entry) { -                    entries.append(entry); -                } else { -                    cerr << "Can not parse entry." << endl; -                    return 1; -                } -            } -        } else if (tag == QLatin1String("signal")) { -            QString signalName = e.attribute(QStringLiteral("name")); -            if (signalName.isEmpty()) { -                cerr << "Signal without name." << endl; -                return 1; -            } -            Signal theSignal; -            theSignal.name = signalName; - -            for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { -                if (e2.tagName() == QLatin1String("argument")) { -                    SignalArguments argument; -                    argument.type = e2.attribute(QStringLiteral("type")); -                    if (argument.type.isEmpty()) { -                        cerr << "Signal argument without type." << endl; -                        return 1; -                    } -                    argument.variableName = e2.text(); -                    theSignal.arguments.append(argument); -                } else if (e2.tagName() == QLatin1String("label")) { -                    theSignal.label = e2.text(); -                } -            } -            signalList.append(theSignal); -        } -    } - -    if (cfg.className.isEmpty()) { -        cerr << "Class name missing" << endl; -        return 1; -    } - -    if (cfg.singleton && !parameters.isEmpty()) { -        cerr << "Singleton class can not have parameters" << endl; -        return 1; -    } - -    if (!cfgFileName.isEmpty() && cfgFileNameArg) { -        cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; -        return 1; -    } - -    if (entries.isEmpty()) { -        cerr << "No entries." << endl; -    } - -#if 0 -    CfgEntry *cfg; -    for (cfg = entries.first(); cfg; cfg = entries.next()) { -        cfg->dump(); -    } -#endif - -    QString headerFileName = baseName + '.' + cfg.headerExtension; -    QString implementationFileName = baseName + '.' + cfg.sourceExtension; -    QString mocFileName = baseName + ".moc"; -    QString cppPreamble; // code to be inserted at the beginnin of the cpp file, e.g. initialization of static values - -    QFile header(baseDir + headerFileName); -    if (!header.open(QIODevice::WriteOnly)) { -        cerr << "Can not open '" << baseDir  << headerFileName << "for writing." << endl; -        return 1; -    } - -    QTextStream h(&header); - -    h.setCodec("utf-8"); - -    h << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl; -    h << "// All changes you do to this file will be lost." << endl; - -    h << "#ifndef " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String("")) -      << cfg.className.toUpper() << "_H" << endl; -    h << "#define " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String("")) -      << cfg.className.toUpper() << "_H" << endl << endl; - -    // Includes -    QStringList::ConstIterator it; -    for (it = cfg.headerIncludes.constBegin(); it != cfg.headerIncludes.constEnd(); ++it) { -        if ((*it).startsWith('"')) { -            h << "#include " << *it << endl; -        } else { -            h << "#include <" << *it << ">" << endl; -        } -    } - -    if (!cfg.headerIncludes.isEmpty()) { -        h << endl; -    } - -    if (!cfg.singleton && parameters.isEmpty()) { -        h << "#include <qglobal.h>" << endl; -    } - -    if (cfg.inherits == QLatin1String("KCoreConfigSkeleton")) { -        h << "#include <kcoreconfigskeleton.h>" << endl; -    } else { -        h << "#include <kconfigskeleton.h>" << endl; -    } - -    h << "#include <QCoreApplication>" << endl; -    h << "#include <QDebug>" << endl << endl; - -    // Includes -    for (it = includes.constBegin(); it != includes.constEnd(); ++it) { -        if ((*it).startsWith('"')) { -            h << "#include " << *it << endl; -        } else { -            h << "#include <" << *it << ">" << endl; -        } -    } - -    beginNamespaces(cfg.nameSpace, h); - -    // Private class declaration -    if (cfg.dpointer) { -        h << "class " << cfg.className << "Private;" << endl << endl; -    } - -    // Class declaration header -    h << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits << endl; - -    h << "{" << endl; -    // Add Q_OBJECT macro if the config need signals. -    if (!signalList.isEmpty() || cfg.generateProperties) { -        h << "  Q_OBJECT" << endl; -    } -    h << "  public:" << endl; - -    // enums -    QList<CfgEntry *>::ConstIterator itEntry; -    for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -        const CfgEntry::Choices &choices = (*itEntry)->choices(); -        const QList<CfgEntry::Choice> chlist = choices.choices; -        if (!chlist.isEmpty()) { -            QStringList values; -            QList<CfgEntry::Choice>::ConstIterator itChoice; -            for (itChoice = chlist.constBegin(); itChoice != chlist.constEnd(); ++itChoice) { -                values.append(choices.prefix + (*itChoice).name); -            } -            if (choices.name().isEmpty()) { -                if (cfg.globalEnums) { -                    h << "    enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; -                } else { -                    // Create an automatically named enum -                    h << "    class " << enumName((*itEntry)->name(), (*itEntry)->choices()) << endl; -                    h << "    {" << endl; -                    h << "      public:" << endl; -                    h << "      enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; -                    h << "    };" << endl; -                } -            } else if (!choices.external()) { -                // Create a named enum -                h << "    enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; -            } -        } -        const QStringList values = (*itEntry)->paramValues(); -        if (!values.isEmpty()) { -            if (cfg.globalEnums) { -                // ### FIXME!! -                // make the following string table an index-based string search! -                // ### -                h << "    enum " << enumName((*itEntry)->param()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; -                h << "    static const char* const " << enumName((*itEntry)->param()) << "ToString[];" << endl; -                cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) + -                               "ToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n"; -            } else { -                h << "    class " << enumName((*itEntry)->param()) << endl; -                h << "    {" << endl; -                h << "      public:" << endl; -                h << "      enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; -                h << "      static const char* const enumToString[];" << endl; -                h << "    };" << endl; -                cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) + -                               "::enumToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n"; -            } -        } -    } -    h << endl; - - -    // Constructor or singleton accessor -    if (!cfg.singleton) { -        h << "    " << cfg.className << "("; -        if (cfgFileNameArg) { -            if (cfg.forceStringFilename) -                h << " const QString &cfgfilename" -                  << (parameters.isEmpty() ? " = QString()" : ", "); -            else -                h << " KSharedConfig::Ptr config" -                  << (parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); -        } -        for (QList<Param>::ConstIterator it = parameters.constBegin(); -                it != parameters.constEnd(); ++it) { -            if (it != parameters.constBegin()) { -                h << ","; -            } -            h << " " << param((*it).type) << " " << (*it).name; -        } -        if (cfg.parentInConstructor) { -            if (cfgFileNameArg || !parameters.isEmpty()) { -                h << ","; -            } -            h << " QObject *parent = nullptr"; -        } -        h << " );" << endl; -    } else { -        h << "    static " << cfg.className << " *self();" << endl; -        if (cfgFileNameArg) { -            h << "    static void instance(const QString& cfgfilename);" << endl; -            h << "    static void instance(KSharedConfig::Ptr config);" << endl; -        } -    } - -    // Destructor -    h << "    ~" << cfg.className << "();" << endl << endl; - -    // global variables -    if (cfg.staticAccessors) { -        This = QStringLiteral("self()->"); -    } else { -        Const = QStringLiteral(" const"); -    } - -    for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -        QString n = (*itEntry)->name(); -        QString t = (*itEntry)->type(); - -        // Manipulator -        if (cfg.allMutators || cfg.mutators.contains(n)) { -            h << "    /**" << endl; -            h << "      Set " << (*itEntry)->label() << endl; -            h << "    */" << endl; -            if (cfg.staticAccessors) { -                h << "    static" << endl; -            } -            h << "    void " << setFunction(n) << "( "; -            if (!(*itEntry)->param().isEmpty()) { -                h << cppType((*itEntry)->paramType()) << " i, "; -            } -            if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                h << enumType(*itEntry, cfg.globalEnums); -            } else { -                h << param(t); -            } -            h << " v )"; -            // function body inline only if not using dpointer -            // for BC mode -            if (!cfg.dpointer) { -                h << endl << "    {" << endl; -                h << indent(memberMutatorBody(*itEntry, cfg), 6); -                h << "    }" << endl; -            } else { -                h << ";" << endl; -            } -        } -        h << endl; - -        QString returnType; -        if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -            returnType = enumType(*itEntry, cfg.globalEnums); -        } else { -            returnType = cppType(t); -        } - -        if (cfg.generateProperties) { -            h << "    Q_PROPERTY(" << returnType << ' ' << getFunction(n); -            h << " READ " << getFunction(n); -            if (cfg.allMutators || cfg.mutators.contains(n)) { -                const QString signal = changeSignalName(n); -                h << " WRITE " << setFunction(n); -                h << " NOTIFY " << signal; - -                //If we have the modified signal, we'll also need -                //the changed signal as well -                Signal s; -                s.name = signal; -                s.modify = true; -                signalList.append(s); -            } else { -                h << " CONSTANT"; -            } -            h << ")" << endl; -        } -        // Accessor -        h << "    /**" << endl; -        h << "      Get " << (*itEntry)->label() << endl; -        h << "    */" << endl; -        if (cfg.staticAccessors) { -            h << "    static" << endl; -        } -        h << "    "; -        h << returnType; -        h << " " << getFunction(n) << "("; -        if (!(*itEntry)->param().isEmpty()) { -            h << " " << cppType((*itEntry)->paramType()) << " i "; -        } -        h << ")" << Const; -        // function body inline only if not using dpointer -        // for BC mode -        if (!cfg.dpointer) { -            h << endl << "    {" << endl; -            h << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 6); -            h << "    }" << endl; -        } else { -            h << ";" << endl; -        } - -        // Default value Accessor -        if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) { -            h << endl; -            h << "    /**" << endl; -            h << "      Get " << (*itEntry)->label() << " default value" << endl; -            h << "    */" << endl; -            if (cfg.staticAccessors) { -                h << "    static" << endl; -            } -            h << "    "; -            if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                h << enumType(*itEntry, cfg.globalEnums); -            } else { -                h << cppType(t); -            } -            h << " " << getDefaultFunction(n) << "("; -            if (!(*itEntry)->param().isEmpty()) { -                h << " " << cppType((*itEntry)->paramType()) << " i "; -            } -            h << ")" << Const << endl; -            h << "    {" << endl; -            h << "        return "; -            if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                h << "static_cast<" << enumType(*itEntry, cfg.globalEnums) << ">("; -            } -            h << getDefaultFunction(n) << "_helper("; -            if (!(*itEntry)->param().isEmpty()) { -                h << " i "; -            } -            h << ")"; -            if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                h << ")"; -            } -            h << ";" << endl; -            h << "    }" << endl; -        } - -        // Item accessor -        if (cfg.itemAccessors) { -            h << endl; -            h << "    /**" << endl; -            h << "      Get Item object corresponding to " << n << "()" -              << endl; -            h << "    */" << endl; -            h << "    Item" << itemType((*itEntry)->type()) << " *" -              << getFunction(n) << "Item("; -            if (!(*itEntry)->param().isEmpty()) { -                h << " " << cppType((*itEntry)->paramType()) << " i "; -            } -            h << ")"; -            if (!cfg.dpointer) { -                h << endl << "    {" << endl; -                h << indent(itemAccessorBody((*itEntry), cfg), 6); -                h << "    }" << endl; -            } else { -                h << ";" << endl; -            } -        } - -        h << endl; -    } - -    // Signal definition. -    const bool hasSignals = !signalList.isEmpty(); -    bool hasNonModifySignals = false; -    if (hasSignals) { -        h << "\n    enum {" << endl; -        unsigned val = 1; -        QList<Signal>::ConstIterator it, itEnd = signalList.constEnd(); -        for (it = signalList.constBegin(); it != itEnd; val <<= 1) { -            hasNonModifySignals |= !it->modify; -            if (!val) { -                cerr << "Too many signals to create unique bit masks" << endl; -                exit(1); -            } -            Signal signal = *it; -            h << "      " << signalEnumName(signal.name) << " = 0x" << -     #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) -                 hex -     #else -                 Qt::hex -     #endif -              << val; -            if (++it != itEnd) { -                h << ","; -            } -            h << endl; -        } -        h << "    };" << -     #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) -             dec -     #else -             Qt::dec -     #endif -          << endl << endl; - -        h << "  Q_SIGNALS:"; -        for (const Signal &signal : qAsConst(signalList)) { -            h << endl; -            if (!signal.label.isEmpty()) { -                h << "    /**" << endl; -                h << "      " << signal.label << endl; -                h << "    */" << endl; -            } -            h << "    void " << signal.name << "("; -            QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd(); -            for (it = signal.arguments.constBegin(); it != itEnd;) { -                SignalArguments argument = *it; -                QString type = param(argument.type); -                if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { -                    for (int i = 0, end = entries.count(); i < end; ++i) { -                        if (entries[i]->name() == argument.variableName) { -                            type = enumType(entries[i], cfg.globalEnums); -                            break; -                        } -                    } -                } -                h << type << " " << argument.variableName; -                if (++it != itEnd) { -                    h << ", "; -                } -            } -            h << ");" << endl; -        } -        h << endl; - -        h << "  private:" << endl; -        h << "    void itemChanged(quint64 flags);" << endl; -        h << endl; -    } - -    h << "  protected:" << endl; - -    // Private constructor for singleton -    if (cfg.singleton) { -        h << "    " << cfg.className << "("; -        if (cfgFileNameArg) { -            h << "KSharedConfig::Ptr config"; -        } -        if (cfg.parentInConstructor) { -            if (cfgFileNameArg) { -                h << ", "; -            } -            h << "QObject *parent = nullptr"; -        } -        h << ");" << endl; -        h << "    friend class " << cfg.className << "Helper;" << endl << endl; -    } - -    if (hasNonModifySignals) { -        h << "    bool usrSave() override;" << endl; -    } - -    // Member variables -    if (!cfg.memberVariables.isEmpty() && cfg.memberVariables != QLatin1String("private") && cfg.memberVariables != QLatin1String("dpointer")) { -        h << "  " << cfg.memberVariables << ":" << endl; -    } - -    // Class Parameters -    for (QList<Param>::ConstIterator it = parameters.constBegin(); -            it != parameters.constEnd(); ++it) { -        h << "    " << cppType((*it).type) << " mParam" << (*it).name << ";" << endl; -    } - -    if (cfg.memberVariables != QLatin1String("dpointer")) { -        QString group; -        for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -            if ((*itEntry)->group() != group) { -                group = (*itEntry)->group(); -                h << endl; -                h << "    // " << group << endl; -            } -            h << "    " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg); -            if (!(*itEntry)->param().isEmpty()) { -                h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); -            } -            h << ";" << endl; - -            if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) { -                h << "    "; -                if (cfg.staticAccessors) { -                    h << "static "; -                } -                h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; -                if (!(*itEntry)->param().isEmpty()) { -                    h << " " << cppType((*itEntry)->paramType()) << " i "; -                } -                h << ")" << Const << ";" << endl; -            } -        } - -        h << endl << "  private:" << endl; -        if (cfg.itemAccessors) { -            for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -                h << "    Item" << itemType((*itEntry)->type()) << " *" << itemVar(*itEntry, cfg); -                if (!(*itEntry)->param().isEmpty()) { -                    h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); -                } -                h << ";" << endl; -            } -        } -        if (hasNonModifySignals) { -            h << "    uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; -        } - -    } else { -        // use a private class for both member variables and items -        h << "  private:" << endl; -        for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -            if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) { -                h << "    "; -                if (cfg.staticAccessors) { -                    h << "static "; -                } -                h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; -                if (!(*itEntry)->param().isEmpty()) { -                    h << " " << cppType((*itEntry)->paramType()) << " i "; -                } -                h << ")" << Const << ";" << endl; -            } -        } -        h << "    " + cfg.className + "Private *d;" << endl; -    } +    KConfigParameters cfg(codegenFilename); -    if (cfg.customAddons) { -        h << "    // Include custom additions" << endl; -        h << "    #include \"" << baseName << "_addons." << cfg.headerExtension << '"' << endl; -    } +    KConfigXmlParser xmlParser(cfg, inputFilename); +     +    // The Xml Parser aborts in the case of an error, so if we get +    // to parseResult, we have a working Xml file. +    xmlParser.start(); -    h << "};" << endl << endl; +    ParseResult parseResult = xmlParser.getParseResult(); -    endNamespaces(cfg.nameSpace, h); - -    h << "#endif" << endl << endl; - -    header.close(); - -    QFile implementation(baseDir + implementationFileName); -    if (!implementation.open(QIODevice::WriteOnly)) { -        cerr << "Can not open '" << implementationFileName << "for writing." -             << endl; +    if (hasErrors(xmlParser, parseResult, cfg)) {          return 1;      } -    QTextStream cpp(&implementation); - -    cpp.setCodec("utf-8"); - -    cpp << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl; -    cpp << "// All changes you do to this file will be lost." << endl << endl; - -    cpp << "#include \"" << headerFileName << "\"" << endl << endl; - -    for (it = cfg.sourceIncludes.constBegin(); it != cfg.sourceIncludes.constEnd(); ++it) { -        if ((*it).startsWith('"')) { -            cpp << "#include " << *it << endl; -        } else { -            cpp << "#include <" << *it << ">" << endl; -        } -    } - -    if (!cfg.sourceIncludes.isEmpty()) { -        cpp << endl; -    } - -    if (cfg.setUserTexts && cfg.translationSystem == CfgConfig::KdeTranslation) { -        cpp << "#include <klocalizedstring.h>" << endl << endl; -    } - -    // Header required by singleton implementation -    if (cfg.singleton) { -        cpp << "#include <qglobal.h>" << endl << "#include <QFile>" << endl << endl; -    } -    if (cfg.singleton && cfgFileNameArg) { -        cpp << "#include <QDebug>" << endl << endl; -    } - -    if (!cfg.nameSpace.isEmpty()) { -        cpp << "using namespace " << cfg.nameSpace << ";" << endl << endl; -    } - -    QString group; - -    // private class implementation -    if (cfg.dpointer) { -        beginNamespaces(cfg.nameSpace, cpp); -        cpp << "class " << cfg.className << "Private" << endl; -        cpp << "{" << endl; -        cpp << "  public:" << endl; -        for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -            if ((*itEntry)->group() != group) { -                group = (*itEntry)->group(); -                cpp << endl; -                cpp << "    // " << group << endl; -            } -            cpp << "    " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg); -            if (!(*itEntry)->param().isEmpty()) { -                cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); -            } -            cpp << ";" << endl; -        } -        cpp << endl << "    // items" << endl; -        for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -            const QString declType = (*itEntry)->signalList().isEmpty() -                    ? QString(cfg.inherits + "::Item" + itemType((*itEntry)->type())) -                    : QStringLiteral("KConfigCompilerSignallingItem"); -            cpp << "    " << declType << " *" << itemVar( *itEntry, cfg ); -            if (!(*itEntry)->param().isEmpty()) { -                cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); -            } -            cpp << ";" << endl; -        } -        if (hasNonModifySignals) { -            cpp << "    uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; -        } - -        cpp << "};" << endl << endl; -        endNamespaces(cfg.nameSpace, cpp); -    } - -    // Singleton implementation -    if (cfg.singleton) { -        beginNamespaces(cfg.nameSpace, cpp); -        cpp << "class " << cfg.className << "Helper" << endl; -        cpp << '{' << endl; -        cpp << "  public:" << endl; -        cpp << "    " << cfg.className << "Helper() : q(nullptr) {}" << endl; -        cpp << "    ~" << cfg.className << "Helper() { delete q; }" << endl; -        cpp << "    " << cfg.className << "Helper(const " << cfg.className << "Helper&) = delete;" << endl; -        cpp << "    " << cfg.className << "Helper& operator=(const " << cfg.className << "Helper&) = delete;" << endl; -        cpp << "    " << cfg.className << " *q;" << endl; -        cpp << "};" << endl; -        endNamespaces(cfg.nameSpace, cpp); -        cpp << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << cfg.className << ")" << endl; - -        cpp << cfg.className << " *" << cfg.className << "::self()" << endl; -        cpp << "{" << endl; -        if (cfgFileNameArg) { -            cpp << "  if (!s_global" << cfg.className << "()->q)" << endl; -            cpp << "     qFatal(\"you need to call " << cfg.className << "::instance before using\");" << endl; -        } else { -            cpp << "  if (!s_global" << cfg.className << "()->q) {" << endl; -            cpp << "    new " << cfg.className << ';' << endl; -            cpp << "    s_global" << cfg.className << "()->q->read();" << endl; -            cpp << "  }" << endl << endl; -        } -        cpp << "  return s_global" << cfg.className << "()->q;" << endl; -        cpp << "}" << endl << endl; - -        if (cfgFileNameArg) { -            auto instance = [&cfg, &cpp] (const QString &type, const QString &arg, bool isString) { -                cpp << "void " << cfg.className << "::instance(" << type << " " << arg << ")" << endl; -                cpp << "{" << endl; -                cpp << "  if (s_global" << cfg.className << "()->q) {" << endl; -                cpp << "     qDebug() << \"" << cfg.className << "::instance called after the first use - ignoring\";" << endl; -                cpp << "     return;" << endl; -                cpp << "  }" << endl; -                cpp << "  new " << cfg.className << "("; -                if (isString) { -                    cpp << "KSharedConfig::openConfig(" << arg << ")"; -                } else { -                    cpp << "std::move(" << arg << ")"; -                } -                cpp << ");" << endl; -                cpp << "  s_global" << cfg.className << "()->q->read();" << endl; -                cpp << "}" << endl << endl; -            }; -            instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); -            instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); -        } -    } - -    if (!cppPreamble.isEmpty()) { -        cpp << cppPreamble << endl; -    } - -    // Constructor -    cpp << cfg.className << "::" << cfg.className << "("; -    if (cfgFileNameArg) { -        if (! cfg.forceStringFilename) { -            cpp << " KSharedConfig::Ptr config"; -        } else { -            cpp << " const QString& config"; -        } -        cpp << (parameters.isEmpty() ? "" : ","); -    } - -    for (QList<Param>::ConstIterator it = parameters.constBegin(); -            it != parameters.constEnd(); ++it) { -        if (it != parameters.constBegin()) { -            cpp << ","; -        } -        cpp << " " << param((*it).type) << " " << (*it).name; -    } - -    if (cfg.parentInConstructor) { -        if (cfgFileNameArg || !parameters.isEmpty()) { -            cpp << ","; -        } -        cpp << " QObject *parent"; -    } -    cpp << " )" << endl; - -    cpp << "  : " << cfg.inherits << "("; -    if (!cfgFileName.isEmpty()) { -        cpp << " QStringLiteral( \"" << cfgFileName << "\" "; -    } -    if (cfgFileNameArg) { -        if (! cfg.forceStringFilename) { -            cpp << " std::move( config ) "; -        } else { -            cpp << " config "; -        } -    } -    if (!cfgFileName.isEmpty()) { -        cpp << ") "; -    } -    cpp << ")" << endl; - -    // Store parameters -    for (QList<Param>::ConstIterator it = parameters.constBegin(); -            it != parameters.constEnd(); ++it) { -        cpp << "  , mParam" << (*it).name << "(" << (*it).name << ")" << endl; -    } - -    if (hasNonModifySignals && !cfg.dpointer) { -        cpp << "  , " << varName(QStringLiteral("settingsChanged"), cfg) << "(0)" << endl; -    } - -    cpp << "{" << endl; - -    if (cfg.parentInConstructor) { -        cpp << "  setParent(parent);" << endl; -    } - -    if (cfg.dpointer) { -        cpp << "  d = new " + cfg.className + "Private;" << endl; -        if (hasNonModifySignals) { -            cpp << "  " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; -        } -    } -    // Needed in case the singleton class is used as baseclass for -    // another singleton. -    if (cfg.singleton) { -        cpp << "  Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl; -        cpp << "  s_global" << cfg.className << "()->q = this;" << endl; -    } - -    group.clear(); - -    if (hasSignals) { -        // this cast to base-class pointer-to-member is valid C++ -        // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ -        cpp << "  KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" -            << " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&" -            << cfg.className << "::itemChanged);" << endl << endl; -    } - -    for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -        if ((*itEntry)->group() != group) { -            if (!group.isEmpty()) { -                cpp << endl; -            } -            group = (*itEntry)->group(); -            cpp << "  setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl; -        } - -        QString key = paramString((*itEntry)->key(), parameters); -        if (!(*itEntry)->code().isEmpty()) { -            cpp << (*itEntry)->code() << endl; -        } -        if ((*itEntry)->type() == QLatin1String("Enum")) { -            cpp << "  QList<" + cfg.inherits + "::ItemEnum::Choice> values" -                << (*itEntry)->name() << ";" << endl; -            const QList<CfgEntry::Choice> choices = (*itEntry)->choices().choices; -            QList<CfgEntry::Choice>::ConstIterator it; -            for (it = choices.constBegin(); it != choices.constEnd(); ++it) { -                cpp << "  {" << endl; -                cpp << "    " + cfg.inherits + "::ItemEnum::Choice choice;" << endl; -                cpp << "    choice.name = QStringLiteral(\"" << (*it).name << "\");" << endl; -                if (cfg.setUserTexts) { -                    if (!(*it).label.isEmpty()) { -                        cpp << "    choice.label = " -                            << translatedString(cfg, (*it).label, (*it).context) -                            << ";" << endl; -                    } -                    if (!(*it).toolTip.isEmpty()) { -                        cpp << "    choice.toolTip = " -                            << translatedString(cfg, (*it).toolTip, (*it).context) -                            << ";" << endl; -                    } -                    if (!(*it).whatsThis.isEmpty()) { -                        cpp << "    choice.whatsThis = " -                            << translatedString(cfg, (*it).whatsThis, (*it).context) -                            << ";" << endl; -                    } -                } -                cpp << "    values" << (*itEntry)->name() << ".append( choice );" << endl; -                cpp << "  }" << endl; -            } -        } - -        if (!cfg.dpointer) { -            cpp << itemDeclaration(*itEntry, cfg); -        } - -        if ((*itEntry)->param().isEmpty()) { -            // Normal case -            cpp << "  " << itemPath(*itEntry, cfg) << " = " -                << newItem((*itEntry), key, (*itEntry)->defaultValue(), cfg) << endl; - -            if (!(*itEntry)->minValue().isEmpty()) { -                cpp << "  " << itemPath(*itEntry, cfg) << "->setMinValue(" << (*itEntry)->minValue() << ");" << endl; -            } -            if (!(*itEntry)->maxValue().isEmpty()) { -                cpp << "  " << itemPath(*itEntry, cfg) << "->setMaxValue(" << (*itEntry)->maxValue() << ");" << endl; -            } - -            if (cfg.setUserTexts) { -                cpp << userTextsFunctions((*itEntry), cfg); -            } - -            if (cfg.allNotifiers || cfg.notifiers.contains((*itEntry)->name())) { -                cpp << "  " << itemPath(*itEntry, cfg) << "->setWriteFlags(KConfigBase::Notify);" << endl; -            } - -            cpp << "  addItem( " << itemPath(*itEntry, cfg); -            QString quotedName = (*itEntry)->name(); -            addQuotes(quotedName); -            if (quotedName != key) { -                cpp << ", QStringLiteral( \"" << (*itEntry)->name() << "\" )"; -            } -            cpp << " );" << endl; -        } else { -            // Indexed -            for (int i = 0; i <= (*itEntry)->paramMax(); i++) { -                QString defaultStr; -                QString itemVarStr(itemPath(*itEntry, cfg) + QStringLiteral("[%1]").arg(i)); - -                if (!(*itEntry)->paramDefaultValue(i).isEmpty()) { -                    defaultStr = (*itEntry)->paramDefaultValue(i); -                } else if (!(*itEntry)->defaultValue().isEmpty()) { -                    defaultStr = paramString((*itEntry)->defaultValue(), (*itEntry), i); -                } else { -                    defaultStr = defaultValue((*itEntry)->type()); -                } - -                cpp << "  " << itemVarStr << " = " -                    << newItem((*itEntry), paramString(key, *itEntry, i), defaultStr, cfg, QStringLiteral("[%1]").arg(i)) << endl; - -                if (cfg.setUserTexts) { -                    cpp << userTextsFunctions(*itEntry, cfg, itemVarStr, (*itEntry)->paramName()); -                } - -                // Make mutators for enum parameters work by adding them with $(..) replaced by the -                // param name. The check for isImmutable in the set* functions doesn't have the param -                // name available, just the corresponding enum value (int), so we need to store the -                // param names in a separate static list!. -                cpp << "  addItem( " << itemVarStr << ", QStringLiteral( \""; -                if ((*itEntry)->paramType() == QLatin1String("Enum")) { -                    cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg((*itEntry)->paramValues()[i]); -                } else { -                    cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg(i); -                } -                cpp << "\" ) );" << endl; -            } -        } +    // TODO: Move this to somewhere saner. +    for (const auto &signal : qAsConst(parseResult.signalList)) { +        parseResult.hasNonModifySignals |= !signal.modify;      } -    cpp << "}" << endl << endl; - -    if (cfg.dpointer) { -        // setters and getters go in Cpp if in dpointer mode -        for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -            QString n = (*itEntry)->name(); -            QString t = (*itEntry)->type(); - -            // Manipulator -            if (cfg.allMutators || cfg.mutators.contains(n)) { -                cpp << "void " << setFunction(n, cfg.className) << "( "; -                if (!(*itEntry)->param().isEmpty()) { -                    cpp << cppType((*itEntry)->paramType()) << " i, "; -                } -                if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                    cpp << enumType(*itEntry, cfg.globalEnums); -                } else { -                    cpp << param(t); -                } -                cpp << " v )" << endl; -                // function body inline only if not using dpointer -                // for BC mode -                cpp << "{" << endl; -                cpp << indent(memberMutatorBody(*itEntry, cfg), 6); -                cpp << "}" << endl << endl; -            } - -            // Accessor -            if (cfg.useEnumTypes && t == QLatin1String("Enum")) { -                cpp << enumType(*itEntry, cfg.globalEnums); -            } else { -                cpp << cppType(t); -            } -            cpp << " " << getFunction(n, cfg.className) << "("; -            if (!(*itEntry)->param().isEmpty()) { -                cpp << " " << cppType((*itEntry)->paramType()) << " i "; -            } -            cpp << ")" << Const << endl; -            // function body inline only if not using dpointer -            // for BC mode -            cpp << "{" << endl; -            cpp << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 2); -            cpp << "}" << endl << endl; - -            // Default value Accessor -- written by the loop below - -            // Item accessor -            if (cfg.itemAccessors) { -                cpp << endl; -                cpp << cfg.inherits + "::Item" << itemType((*itEntry)->type()) << " *" -                    << getFunction(n, cfg.className) << "Item("; -                if (!(*itEntry)->param().isEmpty()) { -                    cpp << " " << cppType((*itEntry)->paramType()) << " i "; -                } -                cpp << ")" << endl; -                cpp << "{" << endl; -                cpp << indent(itemAccessorBody(*itEntry, cfg), 2); -                cpp << "}" << endl; -            } - -            cpp << endl; -        } -    } - -    // default value getters always go in Cpp -    for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { -        QString n = (*itEntry)->name(); -        QString t = (*itEntry)->type(); - -        // Default value Accessor, as "helper" function -        if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) { -            cpp << cppType(t) << " " << getDefaultFunction(n, cfg.className) << "_helper("; -            if (!(*itEntry)->param().isEmpty()) { -                cpp << " " << cppType((*itEntry)->paramType()) << " i "; -            } -            cpp << ")" << Const << endl; -            cpp << "{" << endl; -            cpp << memberGetDefaultBody(*itEntry) << endl; -            cpp << "}" << endl << endl; -        } -    } - -    // Destructor -    cpp << cfg.className << "::~" << cfg.className << "()" << endl; -    cpp << "{" << endl; -    if (cfg.dpointer) { -        cpp << "  delete d;" << endl; -    } -    if (cfg.singleton) { -        cpp << "  s_global" << cfg.className << "()->q = nullptr;" << endl; -    } -    cpp << "}" << endl << endl; - -    if (hasNonModifySignals) { -        cpp << "bool " << cfg.className << "::" << "usrSave()" << endl; -        cpp << "{" << endl; -        cpp << "  const bool res = " << cfg.inherits << "::usrSave();" << endl; -        cpp << "  if (!res) return false;" << endl << endl; -        for (const Signal &signal : qAsConst(signalList)) { -            if (signal.modify) { -                continue; -            } - -            cpp << "  if ( " << varPath(QStringLiteral("settingsChanged"), cfg) << " & " << signalEnumName(signal.name) << " )" << endl; -            cpp << "    Q_EMIT " << signal.name << "("; -            QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd(); -            for (it = signal.arguments.constBegin(); it != itEnd;) { -                SignalArguments argument = *it; -                bool cast = false; -                if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { -                    for (int i = 0, end = entries.count(); i < end; ++i) { -                        if (entries[i]->name() == argument.variableName) { -                            cpp << "static_cast<" << enumType(entries[i], cfg.globalEnums) << ">("; -                            cast = true; -                            break; -                        } -                    } -                } -                cpp << varPath(argument.variableName, cfg); -                if (cast) { -                    cpp << ")"; -                } -                if (++it != itEnd) { -                    cpp << ", "; -                } -            } -            cpp << ");" << endl; -        } - -        cpp << "  " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; -        cpp << "  return true;" << endl; -        cpp << "}" << endl; -    } - -    if (hasSignals) { -        cpp << endl; -        cpp << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl; -        if (hasNonModifySignals) -            cpp << "  " << varPath(QStringLiteral("settingsChanged"), cfg) << " |= flags;" << endl; - -        if (!signalList.isEmpty()) -            cpp << endl; - -        for (const Signal &signal : qAsConst(signalList)) { -            if (signal.modify) { -                cpp << "  if ( flags & " << signalEnumName(signal.name) << " ) {" << endl; -                cpp << "    Q_EMIT " << signal.name << "();" << endl; -                cpp << "  }" << endl; -            } -        } - -        cpp << "}" << endl; -    } - -    if (hasSignals || cfg.generateProperties) { -        // Add includemoc if they are signals defined. -        cpp << endl; -        cpp << "#include \"" << mocFileName << "\"" << endl; -        cpp << endl; -    } +    // remove '.kcfg' from the name. +    const QString baseName = inputFilename.mid(0, inputFilename.size()-5); +    KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult); +    headerGenerator.start(); +    headerGenerator.save(); -    // clear entries list -    qDeleteAll(entries); +    KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult); +    sourceGenerator.start(); +    sourceGenerator.save(); -    implementation.close(); +    qDeleteAll(parseResult.entries);  } | 
