Skip to content
Snippets Groups Projects
FileLoaderRegistry.cpp 8.29 KiB
Newer Older
// 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"
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
      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;
Hahn, Steven's avatar
Hahn, Steven committed
  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) {
        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