Skip to content

Commit

Permalink
Implement loader searching in FileLoaderRegistry.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
martyngigg committed Jul 4, 2013
1 parent 367b623 commit 1326751
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Code/Mantid/Framework/API/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,15 @@ 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
inc/MantidAPI/IFunctionMD.h
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
Expand Down
3 changes: 3 additions & 0 deletions Code/Mantid/Framework/API/inc/MantidAPI/FileLoaderRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
46 changes: 46 additions & 0 deletions Code/Mantid/Framework/API/inc/MantidAPI/IFileLoader.h
Original file line number Diff line number Diff line change
@@ -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_ */
47 changes: 47 additions & 0 deletions Code/Mantid/Framework/API/inc/MantidAPI/IHDFFileLoader.h
Original file line number Diff line number Diff line change
@@ -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_ */
88 changes: 86 additions & 2 deletions Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,60 @@
#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>

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
//----------------------------------------------------------------------------------------------
Expand All @@ -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
//----------------------------------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion Code/Mantid/Framework/API/src/FrameworkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 63 additions & 8 deletions Code/Mantid/Framework/API/test/FileLoaderRegistryTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -26,34 +29,86 @@ class FileLoaderRegistryTest : public CxxTest::TestSuite
{
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;
}
};

};
Expand Down

0 comments on commit 1326751

Please sign in to comment.