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 © 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 © 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