From 132675199cf21badd7d4952a78fe6b42c48c3bfb Mon Sep 17 00:00:00 2001
From: Martyn Gigg <martyn.gigg@stfc.ac.uk>
Date: Fri, 28 Jun 2013 16:47:16 +0100
Subject: [PATCH] Implement loader searching in FileLoaderRegistry.

The FileLoaderRegistry can now be asked to search its list of loaders
for the best that matches a given file. It passes along a *Descriptor
object to minimize the number of times the file is opened.

Refs #7263
---
 Code/Mantid/Framework/API/CMakeLists.txt      |  2 +
 .../API/inc/MantidAPI/FileLoaderRegistry.h    |  3 +
 .../Framework/API/inc/MantidAPI/IFileLoader.h | 46 ++++++++++
 .../API/inc/MantidAPI/IHDFFileLoader.h        | 47 ++++++++++
 .../Framework/API/src/FileLoaderRegistry.cpp  | 88 ++++++++++++++++++-
 .../Framework/API/src/FrameworkManager.cpp    |  3 +-
 .../API/test/FileLoaderRegistryTest.h         | 71 +++++++++++++--
 7 files changed, 249 insertions(+), 11 deletions(-)
 create mode 100644 Code/Mantid/Framework/API/inc/MantidAPI/IFileLoader.h
 create mode 100644 Code/Mantid/Framework/API/inc/MantidAPI/IHDFFileLoader.h

diff --git a/Code/Mantid/Framework/API/CMakeLists.txt b/Code/Mantid/Framework/API/CMakeLists.txt
index 59ef155b9bf..95810be6e9e 100644
--- a/Code/Mantid/Framework/API/CMakeLists.txt
+++ b/Code/Mantid/Framework/API/CMakeLists.txt
@@ -177,6 +177,7 @@ set ( INC_FILES
 	inc/MantidAPI/IDomainCreator.h
 	inc/MantidAPI/IEventList.h
 	inc/MantidAPI/IEventWorkspace.h
+	inc/MantidAPI/IFileLoader.h
 	inc/MantidAPI/IFuncMinimizer.h
 	inc/MantidAPI/IFunction.h
 	inc/MantidAPI/IFunction1D.h
@@ -184,6 +185,7 @@ set ( INC_FILES
 	inc/MantidAPI/IFunctionMW.h
 	inc/MantidAPI/IFunctionValues.h
 	inc/MantidAPI/IFunctionWithLocation.h
+	inc/MantidAPI/IHDFFileLoader.h
 	inc/MantidAPI/ILiveListener.h
 	inc/MantidAPI/ILocatedData.h
 	inc/MantidAPI/IMDEventWorkspace.h
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/FileLoaderRegistry.h b/Code/Mantid/Framework/API/inc/MantidAPI/FileLoaderRegistry.h
index 8c44bd0023c..3255cb9cc8e 100644
--- a/Code/Mantid/Framework/API/inc/MantidAPI/FileLoaderRegistry.h
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/FileLoaderRegistry.h
@@ -77,6 +77,9 @@ namespace Mantid
         m_log.debug() << "Registered '" << name << "' as file loader\n";
       }
 
+      /// Returns the name of an Algorithm that can load the given filename
+      const std::string chooseLoader(const std::string &filename) const;
+
     private:
       /// The list of names. The index pointed to by LoaderFormat defines a set for that format
       std::vector<std::set<std::string> > m_names;
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IFileLoader.h b/Code/Mantid/Framework/API/inc/MantidAPI/IFileLoader.h
new file mode 100644
index 00000000000..57781f4f5c4
--- /dev/null
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/IFileLoader.h
@@ -0,0 +1,46 @@
+#ifndef MANTID_API_IFILELOADER_H_
+#define MANTID_API_IFILELOADER_H_
+
+#include "MantidAPI/Algorithm.h"
+#include "MantidKernel/FileDescriptor.h"
+
+namespace Mantid
+{
+  namespace API
+  {
+
+    /**
+    Defines an interface to an algorithm that loads a file so that it can take part in
+    the automatic selection procedure provided by the FileLoaderRegistry.
+
+    Copyright &copy; 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    Mantid 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://github.com/mantidproject/mantid>
+    Code Documentation is available at: <http://doxygen.mantidproject.org>
+     */
+    class MANTID_API_DLL IFileLoader : public Algorithm
+    {
+    public:
+      /// Returns a confidence value that this algorithm can load a file
+      virtual int confidence(const Kernel::FileDescriptor & descriptor) const = 0;
+    };
+
+  } // namespace API
+} // namespace Mantid
+
+#endif  /* MANTID_API_IFILELOADER_H_ */
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IHDFFileLoader.h b/Code/Mantid/Framework/API/inc/MantidAPI/IHDFFileLoader.h
new file mode 100644
index 00000000000..d68e61867da
--- /dev/null
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/IHDFFileLoader.h
@@ -0,0 +1,47 @@
+#ifndef MANTID_API_IHDFFILELOADER_H_
+#define MANTID_API_IHDFFILELOADER_H_
+
+#include "MantidAPI/IFileLoader.h"
+#include "MantidKernel/FileDescriptor.h"
+#include "MantidKernel/HDFDescriptor.h"
+
+namespace Mantid
+{
+  namespace API
+  {
+    /**
+
+    Defines an interface to an algorithm that loads a file stored in a hierarchical data
+    format. This supports anything the HDFDescriptor can wrap, currently = NeXus, HDF5
+
+    Copyright &copy; 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    Mantid 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://github.com/mantidproject/mantid>
+    Code Documentation is available at: <http://doxygen.mantidproject.org>
+     */
+    class DLLExport IHDFFileLoader : public API::Algorithm
+    {
+    public:
+      /// Returns a confidence value that this algorithm can load a file
+      virtual int confidence(const Kernel::HDFDescriptor & descriptor) const = 0;
+    };
+
+  } // namespace API
+} // namespace Mantid
+
+#endif  /* MANTID_API_IHDFFILELOADER_H_ */
diff --git a/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp b/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp
index 59f59a69702..c62e909e42d 100644
--- a/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp
+++ b/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp
@@ -1,6 +1,7 @@
 #include "MantidAPI/FileLoaderRegistry.h"
-#include "MantidKernel/Exception.h"
-#include "MantidKernel/Logger.h"
+#include "MantidAPI/IFileLoader.h"
+#include "MantidAPI/IHDFFileLoader.h"
+#include "MantidKernel/FileDescriptor.h"
 
 #include <Poco/File.h>
 
@@ -8,6 +9,52 @@ namespace Mantid
 {
   namespace API
   {
+    namespace
+    {
+      //----------------------------------------------------------------------------------------------
+      // Anonymous namespace helpers
+      //----------------------------------------------------------------------------------------------
+      /**
+       * @param descriptor A descriptor object describing the file
+       * @param names The collection of names to search through
+       * @param logger A reference to a Mantid Logger object
+       * @return  A string containing the name of an algorithm to load the file, or an empty string if nothing
+       * was found
+       */
+      template<typename DescriptorType, typename FileLoaderType>
+      const std::string searchForLoader(const DescriptorType & descriptor,const std::set<std::string> & names,
+                                        Kernel::Logger & logger)
+      {
+        const auto & factory = AlgorithmFactory::Instance();
+        std::string bestLoader;
+        int maxConfidence(0);
+
+        auto iend = names.end();
+        for(auto it = names.begin(); it != iend; ++it)
+        {
+          const std::string & name = *it;
+          logger.debug() << "Checking " << name << std::endl;
+
+          auto alg = boost::static_pointer_cast<FileLoaderType>(factory.create(name, -1)); // highest version
+          try
+          {
+            const int confidence = alg->confidence(descriptor);
+            if(confidence > maxConfidence) // strictly greater
+            {
+              bestLoader = name;
+              maxConfidence = confidence;
+            }
+          }
+          catch(std::exception & exc)
+          {
+            logger.warning() << "Checking loader '" << name << "' raised an error: '" << exc.what() << "'. Loader skipped." << std::endl;
+          }
+        }
+        return bestLoader;
+      }
+    }// end anonymous
+
+
     //----------------------------------------------------------------------------------------------
     // Public members
     //----------------------------------------------------------------------------------------------
@@ -20,6 +67,43 @@ namespace Mantid
     {
     }
 
+    /**
+     * Queries each registered algorithm and asks it how confident it is that it can
+     * load the given file. The name of the one with the highest confidence is returned.
+     * @param filename A full file path pointing to an existing file
+     * @return A string containing the name of an algorithm to load the file
+     * @throws Exception::NotFoundError if an algorithm cannot be found
+     */
+    const std::string FileLoaderRegistry::chooseLoader(const std::string &filename) const
+    {
+      using Kernel::FileDescriptor;
+      using Kernel::HDFDescriptor;
+
+      m_log.debug() << "Trying to find loader for '" << filename << "'" << std::endl;
+
+      std::string bestLoader;
+      if(HDFDescriptor::isHDF(filename))
+      {
+        m_log.debug() << filename << " looks like a HDF file. Checking registered HDF loaders\n";
+        HDFDescriptor descriptor(filename);
+        bestLoader = searchForLoader<HDFDescriptor,IHDFFileLoader>(descriptor, m_names[HDF], m_log);
+      }
+      else
+      {
+        m_log.debug() << "Checking registered non-HDF loaders\n";
+        FileDescriptor descriptor(filename);
+        bestLoader = searchForLoader<FileDescriptor,IFileLoader>(descriptor, m_names[NonHDF], m_log);
+      }
+
+      if(bestLoader.empty())
+      {
+        throw Kernel::Exception::NotFoundError(filename, "Unable to find loader");
+      }
+      m_log.debug() << "Found loader " << bestLoader << " for file '" << filename << "'" << std::endl;
+      return bestLoader;
+    }
+
+
     //----------------------------------------------------------------------------------------------
     // Private members
     //----------------------------------------------------------------------------------------------
diff --git a/Code/Mantid/Framework/API/src/FrameworkManager.cpp b/Code/Mantid/Framework/API/src/FrameworkManager.cpp
index bb3e3801fc9..40f23e1d99f 100644
--- a/Code/Mantid/Framework/API/src/FrameworkManager.cpp
+++ b/Code/Mantid/Framework/API/src/FrameworkManager.cpp
@@ -42,7 +42,8 @@ namespace API
 
 
 /// Default constructor
-FrameworkManagerImpl::FrameworkManagerImpl() : g_log(Kernel::Logger::get("FrameworkManager"))
+FrameworkManagerImpl::FrameworkManagerImpl()
+  : m_fileLoaderRegistry(), g_log(Kernel::Logger::get("FrameworkManager"))
 #ifdef MPI_BUILD
       , m_mpi_environment()
 #endif
diff --git a/Code/Mantid/Framework/API/test/FileLoaderRegistryTest.h b/Code/Mantid/Framework/API/test/FileLoaderRegistryTest.h
index e6ae4cf087e..8325820893c 100644
--- a/Code/Mantid/Framework/API/test/FileLoaderRegistryTest.h
+++ b/Code/Mantid/Framework/API/test/FileLoaderRegistryTest.h
@@ -3,7 +3,10 @@
 
 #include <cxxtest/TestSuite.h>
 #include "MantidAPI/Algorithm.h"
+#include "MantidAPI/FileFinder.h"
 #include "MantidAPI/FileLoaderRegistry.h"
+#include "MantidAPI/IFileLoader.h"
+#include "MantidKernel/FileDescriptor.h"
 
 using Mantid::API::FileLoaderRegistry;
 
@@ -26,34 +29,86 @@ public:
   {
     FileLoaderRegistry registry;
 
-    TS_ASSERT_THROWS_NOTHING(registry.subscribe<StubAlgorithm>(FileLoaderRegistry::NonHDF));
+    TS_ASSERT_THROWS_NOTHING(registry.subscribe<StubNonLoader>(FileLoaderRegistry::NonHDF));
     TS_ASSERT_EQUALS(1, registry.size());
 
     // We can't mock the factory as it's a singleton so make sure we clean up
-    Mantid::API::AlgorithmFactory::Instance().unsubscribe("StubAlgorithm", 1);
+    Mantid::API::AlgorithmFactory::Instance().unsubscribe("StubNonLoader", 1);
+  }
+
+  void test_chooseLoader_Throws_For_NonExistant_File()
+  {
+    FileLoaderRegistry registry;
+
+    TS_ASSERT_THROWS(registry.chooseLoader("__not_a_file.txt"), std::invalid_argument);
+  }
+
+  void test_chooseLoader_Returns_Expected_Loader_Name_For_Given_File()
+  {
+    FileLoaderRegistry registry;
+    registry.subscribe<RawLoader>(FileLoaderRegistry::NonHDF);
+    registry.subscribe<TxtLoader>(FileLoaderRegistry::NonHDF);
+
+    const std::string filename = Mantid::API::FileFinder::Instance().getFullPath("AsciiExample.txt");
+
+    std::string algName;
+    TS_ASSERT_THROWS_NOTHING(algName = registry.chooseLoader(filename));
+    TS_ASSERT_EQUALS("TxtLoader", algName);
+
+    // We can't mock the factory as it's a singleton so make sure we clean up
+    Mantid::API::AlgorithmFactory::Instance().unsubscribe("RawLoader", 1);
+    Mantid::API::AlgorithmFactory::Instance().unsubscribe("TxtLoader", 1);
   }
 
   // ======================== Failure cases ===================================
   void test_Adding_Entry_That_Already_Exists_Throws_Error_And_Keeps_The_Size_The_Same()
   {
     FileLoaderRegistry registry;
-    registry.subscribe<StubAlgorithm>(FileLoaderRegistry::NonHDF);
+    registry.subscribe<StubNonLoader>(FileLoaderRegistry::NonHDF);
 
-    TS_ASSERT_THROWS(registry.subscribe<StubAlgorithm>(FileLoaderRegistry::NonHDF), std::runtime_error);
+    TS_ASSERT_THROWS(registry.subscribe<StubNonLoader>(FileLoaderRegistry::NonHDF), std::runtime_error);
     TS_ASSERT_EQUALS(1, registry.size());
 
     // We can't mock the factory as it's a singleton so make sure we clean up
-    Mantid::API::AlgorithmFactory::Instance().unsubscribe("StubAlgorithm", 1);
+    Mantid::API::AlgorithmFactory::Instance().unsubscribe("StubNonLoader", 1);
   }
 
-  private:
+private:
+  // Stub algorithm for test
+  struct StubNonLoader : Mantid::API::Algorithm
+  {
+    const std::string name() const { return "StubNonLoader"; }
+    int version() const { return 1; }
+    void init() {};
+    void exec() {};
+  };
+
+  // Stub algorithm for test
+  struct RawLoader : Mantid::API::IFileLoader
+  {
+    const std::string name() const { return "RawLoader"; }
+    int version() const { return 1; }
+    void init() {};
+    void exec() {};
+    int confidence(const Mantid::Kernel::FileDescriptor & descr) const
+    {
+      if(descr.extension() == ".raw") return 80;
+      else return 0;
+    }
+  };
+
   // Stub algorithm for test
-  struct StubAlgorithm : Mantid::API::Algorithm
+  struct TxtLoader : Mantid::API::IFileLoader
   {
-    const std::string name() const { return "StubAlgorithm"; }
+    const std::string name() const { return "TxtLoader"; }
     int version() const { return 1; }
     void init() {};
     void exec() {};
+    int confidence(const Mantid::Kernel::FileDescriptor & descr) const
+    {
+      if(descr.extension() == ".txt") return 80;
+      else return 0;
+    }
   };
 
 };
-- 
GitLab