From 3595efca866fd2fae0d8046f2557c32b23470dd0 Mon Sep 17 00:00:00 2001
From: Tobias Knopff <t.knopff@fz-juelich.de>
Date: Fri, 16 Jul 2021 17:11:00 +0200
Subject: [PATCH] Make DataLoaders1D independent of concrete DataLoaders

---
 GUI/DataLoaders/DataLoaderUtil.cpp            | 28 +++++++++
 GUI/DataLoaders/DataLoaderUtil.h              | 24 ++++++++
 GUI/DataLoaders/DataLoadersRegistration.cpp   | 45 ++++++++++++++
 GUI/Models/DataLoaders1D.cpp                  | 61 ++++++-------------
 GUI/Models/DataLoaders1D.h                    | 19 ++++--
 .../SpecularDataImportWidget.cpp              |  3 +-
 6 files changed, 131 insertions(+), 49 deletions(-)
 create mode 100644 GUI/DataLoaders/DataLoaderUtil.cpp
 create mode 100644 GUI/DataLoaders/DataLoaderUtil.h
 create mode 100644 GUI/DataLoaders/DataLoadersRegistration.cpp

diff --git a/GUI/DataLoaders/DataLoaderUtil.cpp b/GUI/DataLoaders/DataLoaderUtil.cpp
new file mode 100644
index 00000000000..7333459b916
--- /dev/null
+++ b/GUI/DataLoaders/DataLoaderUtil.cpp
@@ -0,0 +1,28 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/DataLoaders/UserDefinedDataLoader1D.cpp
+//! @brief     Implements class UserDefinedDataLoader1D
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/DataLoaders/DataLoaderUtil.h"
+#include "GUI/DataLoaders/UserDefinedDataLoader1D.h"
+#include "GUI/Models/DataLoaders1D.h"
+
+void cloneAsUserDefinedLoader(AbstractDataLoader* loader, const QString& name)
+{
+    loader->applyImportSettings();
+    auto clonedLoader = dynamic_cast<AbstractDataLoader1D*>(loader->clone());
+    const auto defaultProperties = loader->serialize();
+
+    DataLoaders1D::instance().addUserDefinedLoader
+        (new UserDefinedDataLoader1D(clonedLoader, name, defaultProperties));
+}
+
diff --git a/GUI/DataLoaders/DataLoaderUtil.h b/GUI/DataLoaders/DataLoaderUtil.h
new file mode 100644
index 00000000000..e4f90be4ee0
--- /dev/null
+++ b/GUI/DataLoaders/DataLoaderUtil.h
@@ -0,0 +1,24 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/DataLoaders/UserDefinedDataLoader1D.h
+//! @brief     Utility functions for data loaders
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_DATALOADERS_DATALOADERUTIL_H
+#define BORNAGAIN_GUI_DATALOADERS_DATALOADERUTIL_H
+
+class AbstractDataLoader;
+class QString;
+
+//! clones the loader as a user defined loader and puts it in DataLoaders1D store
+void cloneAsUserDefinedLoader(AbstractDataLoader* loader, const QString& name);
+
+#endif // BORNAGAIN_GUI_DATALOADERS_DATALOADERUTIL_H
diff --git a/GUI/DataLoaders/DataLoadersRegistration.cpp b/GUI/DataLoaders/DataLoadersRegistration.cpp
new file mode 100644
index 00000000000..58733b9de2d
--- /dev/null
+++ b/GUI/DataLoaders/DataLoadersRegistration.cpp
@@ -0,0 +1,45 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/DataLoaders/DataLoadersRegistration.cpp
+//! @brief     Perform the registration of the dataloaders at the corresponding sites
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Models/DataLoaders1D.h"
+#include "GUI/DataLoaders/AutomaticDataLoader1D.h"
+#include "GUI/DataLoaders/QREDataLoader.h"
+
+namespace {
+
+QREDataLoader* createQREDataLoader()
+{
+    return new QREDataLoader();
+}
+
+AutomaticDataLoader1D* createAutomaticDataLoader1D()
+{
+    return new AutomaticDataLoader1D();
+}
+
+bool register_dataloaders() {
+    // the ordering in here defines the ordering in the selection combo box in the open file dialog
+    // and also in the selection combo box on the format configuration page. Furthermore the first
+    // returned loader will be used as default in the open file dialog if it is started for the
+    // first time
+    DataLoaders1D::instance().addBuiltInLoader
+        (QREDataLoader().persistentClassName(), createQREDataLoader);
+    DataLoaders1D::instance().addBuiltInLoader
+        (AutomaticDataLoader1D().persistentClassName(), createAutomaticDataLoader1D);
+
+    return true;
+}
+
+bool registration = register_dataloaders();
+}
diff --git a/GUI/Models/DataLoaders1D.cpp b/GUI/Models/DataLoaders1D.cpp
index d8661e217b3..e77d4007a42 100644
--- a/GUI/Models/DataLoaders1D.cpp
+++ b/GUI/Models/DataLoaders1D.cpp
@@ -13,23 +13,7 @@
 //  ************************************************************************************************
 
 #include "GUI/Models/DataLoaders1D.h"
-#include "GUI/DataLoaders/AutomaticDataLoader1D.h"
-#include "GUI/DataLoaders/QREDataLoader.h"
-#include "GUI/DataLoaders/UserDefinedDataLoader1D.h"
-
-namespace {
-
-// the one and only instance of DataLoaders1D
-DataLoaders1D dataLoaders1D;
-
-} // namespace
-
-DataLoaders1D* DataLoaders1D::m_instance = nullptr;
-
-DataLoaders1D::DataLoaders1D()
-{
-    m_instance = this;
-}
+#include "GUI/Models/AbstractDataLoader1D.h"
 
 DataLoaders1D::~DataLoaders1D()
 {
@@ -39,23 +23,25 @@ DataLoaders1D::~DataLoaders1D()
 
 DataLoaders1D& DataLoaders1D::instance()
 {
-    return *m_instance;
+    static DataLoaders1D inst;
+
+    return inst;
+}
+
+void DataLoaders1D::addBuiltInLoader(const QString& name, 
+                                     const std::function<AbstractDataLoader1D*()>& create)
+{
+    m_createLoaders.emplace(name, create);
+    m_builtInLoaders.push_back(create());
 }
 
-void DataLoaders1D::initBuiltInLoaders()
+void DataLoaders1D::addUserDefinedLoader(AbstractDataLoader* loader)
 {
-    // the ordering in here defines the ordering in the selection combo box in the open file dialog
-    // and also in the selection combo box on the format configuration page. Furthermore the first
-    // returned loader will be used as default in the open file dialog if it is started for the
-    // first time
-    m_builtInLoaders << new QREDataLoader();
-    m_builtInLoaders << new AutomaticDataLoader1D();
+    m_userDefinedLoaders.push_back(loader);
 }
 
 QVector<AbstractDataLoader*> DataLoaders1D::loaders() const
 {
-    if (m_builtInLoaders.isEmpty())
-        const_cast<DataLoaders1D*>(this)->initBuiltInLoaders();
     return m_builtInLoaders + m_userDefinedLoaders;
 }
 
@@ -64,22 +50,13 @@ QVector<AbstractDataLoader*> DataLoaders1D::recentlyUsedLoaders() const
     return m_recentlyUsedLoaders;
 }
 
-void DataLoaders1D::cloneAsUserDefinedLoader(AbstractDataLoader* loader, const QString& name)
-{
-    loader->applyImportSettings();
-    auto clonedLoader = dynamic_cast<AbstractDataLoader1D*>(loader->clone());
-    const auto defaultProperties = loader->serialize();
-
-    m_userDefinedLoaders << new UserDefinedDataLoader1D(clonedLoader, name, defaultProperties);
-}
-
 AbstractDataLoader1D* DataLoaders1D::createFromPersistentName(const QString& persistentClassName)
 {
-    if (persistentClassName == AutomaticDataLoader1D().persistentClassName())
-        return new AutomaticDataLoader1D();
-
-    if (persistentClassName == QREDataLoader().persistentClassName())
-        return new QREDataLoader();
+    std::map<QString,std::function<AbstractDataLoader1D*()>>::const_iterator it =
+        m_createLoaders.find(persistentClassName);
 
-    return nullptr;
+    if (it != m_createLoaders.end())
+        return it->second();
+    else
+        return nullptr;
 }
diff --git a/GUI/Models/DataLoaders1D.h b/GUI/Models/DataLoaders1D.h
index b94f54fe8f8..e5eee7004be 100644
--- a/GUI/Models/DataLoaders1D.h
+++ b/GUI/Models/DataLoaders1D.h
@@ -17,6 +17,9 @@
 
 #include <QVector>
 
+#include <map>
+#include <functional>
+
 class AbstractDataLoader;
 class AbstractDataLoader1D;
 
@@ -24,12 +27,18 @@ class AbstractDataLoader1D;
 
 class DataLoaders1D {
 public:
-    DataLoaders1D();
     ~DataLoaders1D();
 
     //! The one and only instance
     static DataLoaders1D& instance();
 
+    //! Register a built-in loader with the given class name and factory function
+    void addBuiltInLoader(const QString& persistentClassName,
+                          const std::function<AbstractDataLoader1D*()>& create);
+
+    //! Register a built-in loader with the given class name and factory function
+    void addUserDefinedLoader(AbstractDataLoader* loader);
+
     //! all defined loaders. A null element in the list defines a separator
     //! The returned pointers are the same over the lifetime of the DataLoaders instance, therefore
     //! they can be used for comparison.
@@ -41,22 +50,20 @@ public:
     //! Notify loader was recently used
     void setRecentlyUsedLoader(const AbstractDataLoader* loader);
 
-    //! Clone the loader and create a user defined loader with its current settings and the given
-    //! name
-    void cloneAsUserDefinedLoader(AbstractDataLoader* loader, const QString& name);
-
     //! Create loader from the given persistent name
     AbstractDataLoader1D* createFromPersistentName(const QString& persistentClassName);
 
 private:
+    DataLoaders1D() = default;
+
     //! create all default built in loaders
     void initBuiltInLoaders();
 
 private:
-    static DataLoaders1D* m_instance;
     QVector<AbstractDataLoader*> m_builtInLoaders;
     QVector<AbstractDataLoader*> m_recentlyUsedLoaders;
     QVector<AbstractDataLoader*> m_userDefinedLoaders;
+    std::map<QString,std::function<AbstractDataLoader1D*()>> m_createLoaders;
 };
 
 #endif // BORNAGAIN_GUI_MODELS_DATALOADERS1D_H
diff --git a/GUI/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp b/GUI/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp
index ee053348263..567b66cf33e 100644
--- a/GUI/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp
+++ b/GUI/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp
@@ -13,6 +13,7 @@
 //  ************************************************************************************************
 
 #include "GUI/Views/SpecularDataWidgets/SpecularDataImportWidget.h"
+#include "GUI/DataLoaders/DataLoaderUtil.h"
 #include "GUI/Models/AbstractDataLoaderResultModel.h"
 #include "GUI/Models/DataItemUtils.h"
 #include "GUI/Models/DataLoaders1D.h"
@@ -333,7 +334,7 @@ void SpecularDataImportWidget::onCreateNewFormatButton()
     if (!ok || name.isEmpty())
         return;
 
-    DataLoaders1D::instance().cloneAsUserDefinedLoader(m_loader, name);
+    cloneAsUserDefinedLoader(m_loader, name);
 
     fillLoaderCombo();
     m_ui->formatSelectionComboBox->setCurrentText(name);
-- 
GitLab