diff --git a/.gitignore b/.gitignore index 237993e..f0dfc68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ build/ CMakeLists.txt.user /.project +core/src/Version.cpp +core/include/Version.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e160ed8..4852e74 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -75,7 +75,7 @@ qt5_use_modules(${EXECUTABLE_NAME} Core Widgets) LIST(APPEND CHECKSTYLE_INPUT_FILES ${APPLICATION_SOURCES}) SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_INPUT_FILES) # Vera++ exclusion files -#LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/path/to/exclusionFiles.tcl) +LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vera-exclusions/exclusions.txt) SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES) # diff --git a/app/src/main.cpp b/app/src/main.cpp index 216e576..0812bf2 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -27,8 +27,7 @@ int main(int argc, char *argv[]) { - int ad; - SqpApplication a(argc, argv); + SqpApplication a{argc, argv}; SqpApplication::setOrganizationName("LPP"); SqpApplication::setOrganizationDomain("lpp.fr"); SqpApplication::setApplicationName("SciQLop"); diff --git a/app/vera-exclusions/exclusions.txt b/app/vera-exclusions/exclusions.txt new file mode 100644 index 0000000..f6204f8 --- /dev/null +++ b/app/vera-exclusions/exclusions.txt @@ -0,0 +1,3 @@ +# Ignore false positive relative to sqpApp macro +SqpApplication\.h:\d+:.IPSIS_S03.*found: sqpApp +SqpApplication\.h:\d+:.IPSIS_S04_VARIABLE.*found: sqpApp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 27e4fb1..2253ebd 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -79,10 +79,12 @@ IF(BUILD_TESTS) ${testDirectory}/*.c ${testDirectory}/*.cpp ${testDirectory}/*.h) - LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) - LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) + LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) + # LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) ADD_EXECUTABLE(${testName} ${testFile} ${currentTestSources}) + set_property(TARGET ${testName} PROPERTY CXX_STANDARD 14) + set_property(TARGET ${testName} PROPERTY CXX_STANDARD_REQUIRED ON) TARGET_LINK_LIBRARIES( ${testName} ${TEST_LIBRARIES} ) qt5_use_modules(${testName} Test) diff --git a/core/include/Common/spimpl.h b/core/include/Common/spimpl.h index b4d5e04..7f6e8be 100644 --- a/core/include/Common/spimpl.h +++ b/core/include/Common/spimpl.h @@ -89,8 +89,9 @@ using default_copier_t = typename default_copier::type; template struct is_default_manageable - : public std::integral_constant >::value - && std::is_same >::value> { + : public std::integral_constant >::value + && std::is_same >::value> { }; } @@ -131,10 +132,11 @@ public: } template - impl_ptr(U *u, typename std::enable_if::value - && is_default_manageable::value, - dummy_t_>::type - = dummy_t_()) SPIMPL_NOEXCEPT + impl_ptr(U *u, + typename std::enable_if::value + && is_default_manageable::value, + dummy_t_>::type + = dummy_t_()) SPIMPL_NOEXCEPT : impl_ptr(u, &details::default_delete, &details::default_copy) { } @@ -151,12 +153,12 @@ public: #ifdef SPIMPL_HAS_AUTO_PTR template - impl_ptr(std::auto_ptr &&u, typename std::enable_if::value - && is_default_manageable::value, - dummy_t_>::type - = dummy_t_()) SPIMPL_NOEXCEPT - : ptr_(u.release(), &details::default_delete), - copier_(&details::default_copy) + impl_ptr(std::auto_ptr &&u, + typename std::enable_if::value + && is_default_manageable::value, + dummy_t_>::type + = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(u.release(), &details::default_delete), + copier_(&details::default_copy) { } #endif diff --git a/core/include/DataSource/DataSourceController.h b/core/include/DataSource/DataSourceController.h index 94e9d7f..ef886ab 100644 --- a/core/include/DataSource/DataSourceController.h +++ b/core/include/DataSource/DataSourceController.h @@ -1,22 +1,22 @@ #ifndef SCIQLOP_DATASOURCECONTROLLER_H #define SCIQLOP_DATASOURCECONTROLLER_H -#include "DataSourceController.h" - #include #include +#include #include Q_DECLARE_LOGGING_CATEGORY(LOG_DataSourceController) +class DataSourceItem; + /** - * @brief The DataSourceController class aims to make the link between SciQlop - * and its plugins. This is the intermediate class that SciQlop have to use - * in the way to connect a data source. Please first use load method to intialize - * a plugin specified by its metadata name (JSON plugin source) then others specifics - * method will ba able to access it. - * You can load a data source driver plugin then create a data source. + * @brief The DataSourceController class aims to make the link between SciQlop and its plugins. This + * is the intermediate class that SciQlop has to use in the way to connect a data source. Please + * first use register method to initialize a plugin specified by its metadata name (JSON plugin + * source) then others specifics method will be able to access it. You can load a data source driver + * plugin then create a data source. */ class DataSourceController : public QObject { Q_OBJECT @@ -24,11 +24,33 @@ public: explicit DataSourceController(QObject *parent = 0); virtual ~DataSourceController(); + /** + * Registers a data source. The method delivers a unique id that can be used afterwards to + * access to the data source properties (structure, connection parameters, data provider, etc.) + * @param dataSourceName the name of the data source + * @return the unique id with which the data source has been registered + */ + QUuid registerDataSource(const QString &dataSourceName) noexcept; + + /** + * Sets the structure of a data source. The controller takes ownership of the structure. + * @param dataSourceUid the unique id with which the data source has been registered into the + * controller. If it is invalid, the method has no effect. + * @param dataSourceItem the structure of the data source + * @sa registerDataSource() + */ + void setDataSourceItem(const QUuid &dataSourceUid, + std::unique_ptr dataSourceItem) noexcept; + public slots: /// Manage init/end of the controller void initialize(); void finalize(); +signals: + /// Signal emitted when a structure has been set for a data source + void dataSourceItemSet(const DataSourceItem &dataSourceItem); + private: void waitForFinish(); diff --git a/core/include/DataSource/DataSourceItem.h b/core/include/DataSource/DataSourceItem.h new file mode 100644 index 0000000..2f71ec6 --- /dev/null +++ b/core/include/DataSource/DataSourceItem.h @@ -0,0 +1,52 @@ +#ifndef SCIQLOP_DATASOURCEITEM_H +#define SCIQLOP_DATASOURCEITEM_H + +#include + +#include +#include + +/** + * @brief The DataSourceItem class aims to represent a structure element of a data source. + * A data source has a tree structure that is made up of a main DataSourceItem object (root) + * containing other DataSourceItem objects (children). + * For each DataSourceItem can be associated a set of data representing it. + */ +class DataSourceItem { +public: + explicit DataSourceItem(QVector data = {}); + + /** + * Adds a child to the item. The item takes ownership of the child. + * @param child the child to add + */ + void appendChild(std::unique_ptr child) noexcept; + + /** + * Returns the item's child associated to an index + * @param childIndex the index to search + * @return a pointer to the child if index is valid, nullptr otherwise + */ + DataSourceItem *child(int childIndex) const noexcept; + + int childCount() const noexcept; + + /** + * Get the data associated to an index + * @param dataIndex the index to search + * @return the data found if index is valid, default QVariant otherwise + */ + QVariant data(int dataIndex) const noexcept; + + /** + * Get the item's parent + * @return a pointer to the parent if it exists, nullptr if the item is a root + */ + DataSourceItem *parentItem() const noexcept; + +private: + class DataSourceItemPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_DATASOURCEITEMMODEL_H diff --git a/core/src/DataSource/DataSourceController.cpp b/core/src/DataSource/DataSourceController.cpp index 40ac74d..0149694 100644 --- a/core/src/DataSource/DataSourceController.cpp +++ b/core/src/DataSource/DataSourceController.cpp @@ -1,4 +1,5 @@ -#include "DataSource/DataSourceController.h" +#include +#include #include #include @@ -10,31 +11,60 @@ Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController") class DataSourceController::DataSourceControllerPrivate { public: - DataSourceControllerPrivate() {} - QMutex m_WorkingMutex; + /// Data sources registered + QHash m_DataSources; + /// Data sources structures + std::map > m_DataSourceItems; }; DataSourceController::DataSourceController(QObject *parent) : impl{spimpl::make_unique_impl()} { - qCDebug(LOG_DataSourceController()) << tr("Construction du DataSourceController") - << QThread::currentThread(); + qCDebug(LOG_DataSourceController()) + << tr("DataSourceController construction") << QThread::currentThread(); } DataSourceController::~DataSourceController() { - qCDebug(LOG_DataSourceController()) << tr("Desctruction du DataSourceController") - << QThread::currentThread(); + qCDebug(LOG_DataSourceController()) + << tr("DataSourceController destruction") << QThread::currentThread(); this->waitForFinish(); } +QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept +{ + auto dataSourceUid = QUuid::createUuid(); + impl->m_DataSources.insert(dataSourceUid, dataSourceName); + + return dataSourceUid; +} + +void DataSourceController::setDataSourceItem( + const QUuid &dataSourceUid, std::unique_ptr dataSourceItem) noexcept +{ + if (impl->m_DataSources.contains(dataSourceUid)) { + impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem))); + + // Retrieves the data source item to emit the signal with it + auto it = impl->m_DataSourceItems.find(dataSourceUid); + if (it != impl->m_DataSourceItems.end()) { + emit dataSourceItemSet(*it->second); + } + } + else { + qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no " + "data source has been registered with the uid") + .arg(dataSourceUid.toString()); + } +} + void DataSourceController::initialize() { - qCDebug(LOG_DataSourceController()) << tr("initialize du DataSourceController") - << QThread::currentThread(); + qCDebug(LOG_DataSourceController()) + << tr("DataSourceController init") << QThread::currentThread(); impl->m_WorkingMutex.lock(); - qCDebug(LOG_DataSourceController()) << tr("initialize du DataSourceController END"); + qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END"); } void DataSourceController::finalize() @@ -44,5 +74,5 @@ void DataSourceController::finalize() void DataSourceController::waitForFinish() { - QMutexLocker locker(&impl->m_WorkingMutex); + QMutexLocker locker{&impl->m_WorkingMutex}; } diff --git a/core/src/DataSource/DataSourceItem.cpp b/core/src/DataSource/DataSourceItem.cpp new file mode 100644 index 0000000..2d7e242 --- /dev/null +++ b/core/src/DataSource/DataSourceItem.cpp @@ -0,0 +1,50 @@ +#include + +#include + +struct DataSourceItem::DataSourceItemPrivate { + explicit DataSourceItemPrivate(QVector data) + : m_Parent{nullptr}, m_Children{}, m_Data{std::move(data)} + { + } + + DataSourceItem *m_Parent; + std::vector > m_Children; + QVector m_Data; +}; + +DataSourceItem::DataSourceItem(QVector data) + : impl{spimpl::make_unique_impl(data)} +{ +} + +void DataSourceItem::appendChild(std::unique_ptr child) noexcept +{ + child->impl->m_Parent = this; + impl->m_Children.push_back(std::move(child)); +} + +DataSourceItem *DataSourceItem::child(int childIndex) const noexcept +{ + if (childIndex < 0 || childIndex >= childCount()) { + return nullptr; + } + else { + return impl->m_Children.at(childIndex).get(); + } +} + +int DataSourceItem::childCount() const noexcept +{ + return impl->m_Children.size(); +} + +QVariant DataSourceItem::data(int dataIndex) const noexcept +{ + return impl->m_Data.value(dataIndex); +} + +DataSourceItem *DataSourceItem::parentItem() const noexcept +{ + return impl->m_Parent; +} diff --git a/core/tests/DataSource/TestDataSourceController.cpp b/core/tests/DataSource/TestDataSourceController.cpp new file mode 100644 index 0000000..03279dc --- /dev/null +++ b/core/tests/DataSource/TestDataSourceController.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include +#include + +#include + +class TestDataSourceController : public QObject { + Q_OBJECT +private slots: + void testRegisterDataSource(); + void testSetDataSourceItem(); +}; + +void TestDataSourceController::testRegisterDataSource() +{ + DataSourceController dataSourceController{}; + + auto uid = dataSourceController.registerDataSource(QStringLiteral("Source1")); + QVERIFY(!uid.isNull()); +} + +void TestDataSourceController::testSetDataSourceItem() +{ + DataSourceController dataSourceController{}; + + // Spy to test controllers' signals + QSignalSpy signalSpy{&dataSourceController, SIGNAL(dataSourceItemSet(const DataSourceItem &))}; + + // Create a data source item + auto source1Name = QStringLiteral("Source1"); + auto source1Values = QVector{source1Name}; + auto source1Item = std::make_unique(std::move(source1Values)); + + // Add data source item to the controller and check that a signal has been emitted after setting + // data source item in the controller + auto source1Uid = dataSourceController.registerDataSource(source1Name); + dataSourceController.setDataSourceItem(source1Uid, std::move(source1Item)); + QCOMPARE(signalSpy.count(), 1); + + // Try to a data source item with an unregistered uid and check that no signal has been emitted + auto unregisteredUid = QUuid::createUuid(); + dataSourceController.setDataSourceItem(unregisteredUid, std::make_unique()); + QCOMPARE(signalSpy.count(), 1); +} + +QTEST_MAIN(TestDataSourceController) +#include "TestDataSourceController.moc" diff --git a/formatting/vera-exclusions/exclusions.txt b/formatting/vera-exclusions/exclusions.txt index 44b1f23..2b00cc6 100644 --- a/formatting/vera-exclusions/exclusions.txt +++ b/formatting/vera-exclusions/exclusions.txt @@ -2,3 +2,7 @@ .*IPSIS_S04_METHOD.*found: Q_DECLARE_LOGGING_CATEGORY.* .*IPSIS_S04_VARIABLE.*found: impl.* + +# Ignore false positive relative to 'noexcept' keyword +.*IPSIS_S04_VARIABLE.*found: noexcept +.*IPSIS_S06.*found: noexcept diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index e3cd44b..3269442 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -110,9 +110,11 @@ IF(BUILD_TESTS) ${testDirectory}/*.cpp ${testDirectory}/*.h) LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) - LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) + # LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) ADD_EXECUTABLE(${testName} ${testFile} ${currentTestSources}) + set_property(TARGET ${testName} PROPERTY CXX_STANDARD 14) + set_property(TARGET ${testName} PROPERTY CXX_STANDARD_REQUIRED ON) TARGET_LINK_LIBRARIES( ${testName} ${TEST_LIBRARIES} ) qt5_use_modules(${testName} Test) diff --git a/gui/include/SqpApplication.h b/gui/include/SqpApplication.h index 997d97a..21dee94 100644 --- a/gui/include/SqpApplication.h +++ b/gui/include/SqpApplication.h @@ -10,12 +10,19 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_SqpApplication) +#if defined(sqpApp) +#undef sqpApp +#endif +#define sqpApp (static_cast(QCoreApplication::instance())) + +class DataSourceController; + /** * @brief The SqpApplication class aims to make the link between SciQlop - * and its plugins. This is the intermediate class that SciQlop have to use - * in the way to connect a data source. Please first use load method to intialize + * and its plugins. This is the intermediate class that SciQlop has to use + * in the way to connect a data source. Please first use load method to initialize * a plugin specified by its metadata name (JSON plugin source) then others specifics - * method will ba able to access it. + * method will be able to access it. * You can load a data source driver plugin then create a data source. */ @@ -26,6 +33,8 @@ public: virtual ~SqpApplication(); void initialize(); + DataSourceController &dataSourceController() const noexcept; + private: class SqpApplicationPrivate; spimpl::unique_impl_ptr impl; diff --git a/gui/src/SqpApplication.cpp b/gui/src/SqpApplication.cpp index c510e53..bd378d0 100644 --- a/gui/src/SqpApplication.cpp +++ b/gui/src/SqpApplication.cpp @@ -7,10 +7,14 @@ Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication") class SqpApplication::SqpApplicationPrivate { public: - SqpApplicationPrivate() {} + SqpApplicationPrivate() : m_DataSourceController{std::make_unique()} + { + m_DataSourceController->moveToThread(&m_DataSourceControllerThread); + } + virtual ~SqpApplicationPrivate() { - qCInfo(LOG_SqpApplication()) << tr("Desctruction du SqpApplicationPrivate"); + qCInfo(LOG_SqpApplication()) << tr("SqpApplicationPrivate destruction"); m_DataSourceControllerThread.quit(); m_DataSourceControllerThread.wait(); } @@ -21,12 +25,9 @@ public: SqpApplication::SqpApplication(int &argc, char **argv) - : QApplication(argc, argv), impl{spimpl::make_unique_impl()} + : QApplication{argc, argv}, impl{spimpl::make_unique_impl()} { - qCInfo(LOG_SqpApplication()) << tr("Construction du SqpApplication"); - - impl->m_DataSourceController = std::make_unique(); - impl->m_DataSourceController->moveToThread(&impl->m_DataSourceControllerThread); + qCInfo(LOG_SqpApplication()) << tr("SqpApplication construction"); connect(&impl->m_DataSourceControllerThread, &QThread::started, impl->m_DataSourceController.get(), &DataSourceController::initialize); @@ -43,3 +44,8 @@ SqpApplication::~SqpApplication() void SqpApplication::initialize() { } + +DataSourceController &SqpApplication::dataSourceController() const noexcept +{ + return *impl->m_DataSourceController; +}