// Mantid Repository : https://github.com/mantidproject/mantid // // Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, // NScD Oak Ridge National Laboratory, European Spallation Source, // Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS // SPDX - License - Identifier: GPL - 3.0 + #include "MantidAPI/FileLoaderRegistry.h" #include "MantidAPI/IFileLoader.h" #include <Poco/File.h> namespace Mantid { namespace API { namespace { //---------------------------------------------------------------------------------------------- // Anonymous namespace helpers //---------------------------------------------------------------------------------------------- /// @cond template <typename T> struct DescriptorCallback { void apply(T & /*unused*/) {} // general one does nothing }; template <> struct DescriptorCallback<Kernel::FileDescriptor> { void apply(Kernel::FileDescriptor &descriptor) { descriptor.resetStreamToStart(); } }; /// @endcond /** * @param filename A string giving a filename * @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 IAlgorithm_sptr searchForLoader(const std::string &filename, const std::multimap<std::string, int> &names, Kernel::Logger &logger) { const auto &factory = AlgorithmFactory::Instance(); IAlgorithm_sptr bestLoader; int maxConfidence(0); DescriptorType descriptor(filename); DescriptorCallback<DescriptorType> callback; auto iend = names.end(); for (auto it = names.begin(); it != iend; ++it) { const std::string &name = it->first; const int version = it->second; logger.debug() << "Checking " << name << " version " << version << '\n'; // Use static cast for speed. Checks have been done at registration to check // the types auto alg = boost::static_pointer_cast<FileLoaderType>( factory.create(name, version)); // highest version try { const int confidence = alg->confidence(descriptor); logger.debug() << name << " returned with confidence=" << confidence << '\n'; if (confidence > maxConfidence) // strictly greater { bestLoader = alg; maxConfidence = confidence; } } catch (std::exception &exc) { logger.warning() << "Checking loader '" << name << "' raised an error: '" << exc.what() << "'. Loader skipped.\n"; } callback.apply(descriptor); } return bestLoader; } } // namespace //---------------------------------------------------------------------------------------------- // Public members //---------------------------------------------------------------------------------------------- /** * If the name does not exist then it does nothing * @param name Name of the algorithm to remove from the search list * @param version An optional version to remove. -1 indicates remove all * (Default=-1) */ void FileLoaderRegistryImpl::unsubscribe(const std::string &name, const int version) { auto iend = m_names.end(); for (auto it = m_names.begin(); it != iend; ++it) { removeAlgorithm(name, version, *it); } } /** * 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 boost::shared_ptr<IAlgorithm> FileLoaderRegistryImpl::chooseLoader(const std::string &filename) const { using Kernel::FileDescriptor; using Kernel::NexusDescriptor; using Kernel::NexusHDF5Descriptor; m_log.debug() << "Trying to find loader for '" << filename << "'\n"; IAlgorithm_sptr bestLoader; if (NexusDescriptor::isReadable(filename)) { m_log.debug() << filename << " looks like a Nexus file. Checking registered Nexus loaders\n"; // a large subset of NeXus files are actually HDF5 based if (NexusHDF5Descriptor::isReadable(filename)) { try { bestLoader = searchForLoader<NexusHDF5Descriptor, IFileLoader<NexusHDF5Descriptor>>( filename, m_names[NexusHDF5], m_log); } catch (const std::invalid_argument &e) { m_log.debug() << "Error in looking for HDF5 based NeXus files: " << e.what() << '\n'; } } // try generic nexus loaders if (!bestLoader) { bestLoader = searchForLoader<NexusDescriptor, IFileLoader<NexusDescriptor>>( filename, m_names[Nexus], m_log); } } else { m_log.debug() << "Checking registered non-HDF loaders\n"; bestLoader = searchForLoader<FileDescriptor, IFileLoader<FileDescriptor>>( filename, m_names[Generic], m_log); } if (!bestLoader) { throw Kernel::Exception::NotFoundError(filename, "Unable to find loader"); } m_log.debug() << "Found loader " << bestLoader->name() << " for file '" << filename << "'\n"; return bestLoader; } /** * Perform a check that that the given algorithm can load the file * @param algorithmName The name of the algorithm to check * @param filename The name of file to check * @returns True if the algorithm can load the file, false otherwise * @throws std::invalid_argument if the loader does not exist */ bool FileLoaderRegistryImpl::canLoad(const std::string &algorithmName, const std::string &filename) const { using Kernel::FileDescriptor; using Kernel::NexusDescriptor; using Kernel::NexusHDF5Descriptor; // Check if it is in one of our lists const bool nexus = (m_names[Nexus].find(algorithmName) != m_names[Nexus].end()); const bool nexusHDF5 = (m_names[NexusHDF5].find(algorithmName) != m_names[NexusHDF5].end()); const bool nonHDF = (m_names[Generic].find(algorithmName) != m_names[Generic].end()); if (!(nexus || nexusHDF5 || nonHDF)) throw std::invalid_argument( "FileLoaderRegistryImpl::canLoad - Algorithm '" + algorithmName + "' is not registered as a loader."); std::multimap<std::string, int> names{{algorithmName, -1}}; IAlgorithm_sptr loader; if (nexus) { if (NexusDescriptor::isReadable(filename)) { loader = searchForLoader<NexusDescriptor, IFileLoader<NexusDescriptor>>( filename, names, m_log); } } else if (nexusHDF5) { if (NexusHDF5Descriptor::isReadable(filename)) { try { loader = searchForLoader<NexusHDF5Descriptor, IFileLoader<NexusHDF5Descriptor>>( filename, names, m_log); } catch (const std::invalid_argument &e) { m_log.debug() << "Error in looking for HDF5 based NeXus files: " << e.what() << '\n'; } } } else if (nonHDF) { loader = searchForLoader<FileDescriptor, IFileLoader<FileDescriptor>>( filename, names, m_log); } return static_cast<bool>(loader); } //---------------------------------------------------------------------------------------------- // Private members //---------------------------------------------------------------------------------------------- /** * Creates an empty registry * * m_names is initialized in the header */ FileLoaderRegistryImpl::FileLoaderRegistryImpl() : m_totalSize(0), m_log("FileLoaderRegistry") {} FileLoaderRegistryImpl::~FileLoaderRegistryImpl() = default; /** * @param name A string containing the algorithm name * @param version The version to remove. -1 indicates all instances * @param typedLoaders A map of names to version numbers **/ void FileLoaderRegistryImpl::removeAlgorithm( const std::string &name, const int version, std::multimap<std::string, int> &typedLoaders) { if (version == -1) // remove all { typedLoaders.erase(name); } else // find the right version { auto range = typedLoaders.equal_range(name); for (auto ritr = range.first; ritr != range.second; ++ritr) { if (ritr->second == version) { typedLoaders.erase(ritr); break; } } } } } // namespace API } // namespace Mantid