Mantid
Loading...
Searching...
No Matches
FileLoaderRegistry.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10
11#include <H5Cpp.h>
12#include <Poco/File.h>
13#include <algorithm>
14
15namespace Mantid::API {
16namespace {
17//----------------------------------------------------------------------------------------------
18// Anonymous namespace helpers
19//----------------------------------------------------------------------------------------------
21template <typename T> struct DescriptorCallback {
22 void apply(std::shared_ptr<T> & /*unused*/) {} // general one does nothing
23};
24template <> struct DescriptorCallback<Kernel::FileDescriptor> {
25 void apply(std::shared_ptr<Kernel::FileDescriptor> &descriptor) { descriptor->resetStreamToStart(); }
26};
28
30template <typename T> struct DescriptorSetter {
31 // general one does nothing
32 void apply(std::shared_ptr<NexusFileLoader> & /*unused*/, std::shared_ptr<T> & /*unused*/) {}
33};
34template <> struct DescriptorSetter<Nexus::NexusDescriptor> {
35 void apply(std::shared_ptr<NexusFileLoader> &loader, const std::shared_ptr<Nexus::NexusDescriptor> &descriptor) {
36 loader->setFileInfo(descriptor);
37 }
38};
40
49template <typename DescriptorType, typename FileLoaderType>
50std::pair<IAlgorithm_sptr, int> searchForLoader(const std::string &filename,
51 const std::multimap<std::string, int> &names, Kernel::Logger &logger) {
52 const auto &factory = AlgorithmFactory::Instance();
53 IAlgorithm_sptr bestLoader;
54 int maxConfidence(0);
55 auto descriptor = std::make_shared<DescriptorType>(filename);
56 DescriptorCallback<DescriptorType> callback;
57 DescriptorSetter<DescriptorType> setdescriptor;
58
59 auto iend = names.end();
60 for (auto it = names.begin(); it != iend; ++it) {
61 const std::string &name = it->first;
62 const int version = it->second;
63 logger.debug() << "Checking " << name << " version " << version << '\n';
64
65 // Use static cast for speed. Checks have been done at registration to check
66 // the types
67 auto alg = std::static_pointer_cast<FileLoaderType>(factory.create(name, version)); // highest version
68 try {
69 const int confidence = alg->confidence(*(descriptor.get()));
70 logger.debug() << name << " returned with confidence=" << confidence << '\n';
71 if (confidence > maxConfidence) // strictly greater
72 {
73 bestLoader = alg;
74 maxConfidence = confidence;
75 }
76 } catch (std::exception &exc) {
77 logger.warning() << "Checking loader '" << name << "' raised an error: '" << exc.what() << "'. Loader skipped.\n";
78 }
79 callback.apply(descriptor);
80 }
81
82 auto nxsLoader = std::dynamic_pointer_cast<NexusFileLoader>(bestLoader);
83 if (nxsLoader)
84 setdescriptor.apply(nxsLoader, descriptor);
85
86 return {bestLoader, maxConfidence};
87}
88
92bool isHDF4(std::string const &filename) {
93 // the HDF4 file signature, as per HDF's documentation
94 std::array<char, 4> constexpr hdf4_sig{0x0E, 0x03, 0x13, 0x01};
95 // read the signature in the first four bytes of this file
96 std::array<char, 4> signature;
97 std::ifstream file(filename, std::ios::binary);
98 if (!file.read(signature.data(), 4)) {
99 return false;
100 }
101 return signature == hdf4_sig;
102}
103} // namespace
104
105//----------------------------------------------------------------------------------------------
106// Public members
107//----------------------------------------------------------------------------------------------
108
115void FileLoaderRegistryImpl::unsubscribe(const std::string &name, const int version) {
116 auto iend = m_names.end();
117 for (auto it = m_names.begin(); it != iend; ++it) {
118 removeAlgorithm(name, version, *it);
119 }
120}
121
130const std::shared_ptr<IAlgorithm> FileLoaderRegistryImpl::chooseLoader(const std::string &filename) const {
134 m_log.debug() << "Trying to find loader for '" << filename << "'\n";
135
136 IAlgorithm_sptr bestLoader;
137
138 if (H5::H5File::isHdf5(filename)) {
139
140 // first, try search with Nexus loaders
141 std::pair<IAlgorithm_sptr, int> NexusResult =
142 searchForLoader<NexusDescriptorLazy, IFileLoader<NexusDescriptorLazy>>(filename, m_names[Nexus], m_log);
143 if (NexusResult.second < 80) {
144 // must also try LegacyNexusDescriptor algorithms because LoadMuonNexus can load both HDF4 and HDF5 files
145 // but only need to do this if confidence is less than 80, i.e. not loaded by LoadEventNexus,
146 // LoadNexusProcessed, LoadMuonNexusV2 or LoadMD
147 std::pair<IAlgorithm_sptr, int> LegacyNexusResult =
148 searchForLoader<LegacyNexusDescriptor, IFileLoader<LegacyNexusDescriptor>>(filename, m_names[LegacyNexus],
149 m_log);
150 // select best loader of Nexus and Legacy
151 bestLoader = std::max({NexusResult, LegacyNexusResult}, [](const auto &a, const auto &b) {
152 return a.second < b.second;
153 }).first;
154 } else {
155 bestLoader = NexusResult.first;
156 }
157 } else if (isHDF4(filename)) {
158 // If the file is HDF4, then it can only be loaded by the legacy nexus loaders
159 try {
160 bestLoader = searchForLoader<LegacyNexusDescriptor, IFileLoader<LegacyNexusDescriptor>>(
161 filename, m_names[LegacyNexus], m_log)
162 .first;
163 } catch (std::exception const &e) {
164 m_log.debug() << "Error in looking for NeXus files: " << e.what() << '\n';
165 }
166 }
167
168 if (!bestLoader)
169 bestLoader = searchForLoader<FileDescriptor, IFileLoader<FileDescriptor>>(filename, m_names[Generic], m_log).first;
170
171 if (!bestLoader) {
172 throw Kernel::Exception::NotFoundError(filename, "Unable to find loader");
173 }
174 m_log.debug() << "Found loader " << bestLoader->name() << " for file '" << filename << "'\n";
175 return bestLoader;
176}
177
185bool FileLoaderRegistryImpl::canLoad(const std::string &algorithmName, const std::string &filename) const {
189
190 // Check if it is in one of our lists
191 const bool legacynexus = (m_names[LegacyNexus].find(algorithmName) != m_names[LegacyNexus].end());
192 const bool nexus = (m_names[Nexus].find(algorithmName) != m_names[Nexus].end());
193 const bool nonHDF = (m_names[Generic].find(algorithmName) != m_names[Generic].end());
194
195 if (!(legacynexus || nexus || nonHDF))
196 throw std::invalid_argument("FileLoaderRegistryImpl::canLoad - Algorithm '" + algorithmName +
197 "' is not registered as a loader.");
198
199 std::multimap<std::string, int> names{{algorithmName, -1}};
200 IAlgorithm_sptr loader;
201 if (legacynexus) {
202 try {
203 loader = searchForLoader<LegacyNexusDescriptor, IFileLoader<LegacyNexusDescriptor>>(filename, names, m_log).first;
204 } catch (std::exception const &e) {
205 m_log.debug() << "Error in looking for NeXus files: " << e.what() << '\n';
206 }
207 } else if (nexus) {
208 if (H5::H5File::isHdf5(filename)) {
209 try {
210 loader = searchForLoader<NexusDescriptorLazy, IFileLoader<NexusDescriptorLazy>>(filename, names, m_log).first;
211 } catch (const std::invalid_argument &e) {
212 m_log.debug() << "Error in looking for HDF5 based NeXus files: " << e.what() << '\n';
213 }
214 }
215 } else if (nonHDF) {
216 loader = searchForLoader<FileDescriptor, IFileLoader<FileDescriptor>>(filename, names, m_log).first;
217 }
218 return static_cast<bool>(loader);
219}
220
221//----------------------------------------------------------------------------------------------
222// Private members
223//----------------------------------------------------------------------------------------------
229FileLoaderRegistryImpl::FileLoaderRegistryImpl() : m_totalSize(0), m_log("FileLoaderRegistry") {}
230
232
238void FileLoaderRegistryImpl::removeAlgorithm(const std::string &name, const int version,
239 std::multimap<std::string, int> &typedLoaders) {
240 if (version == -1) // remove all
241 {
242 typedLoaders.erase(name);
243 } else // find the right version
244 {
245 auto range = typedLoaders.equal_range(name);
246 for (auto ritr = range.first; ritr != range.second; ++ritr) {
247 if (ritr->second == version) {
248 typedLoaders.erase(ritr);
249 break;
250 }
251 }
252 }
253}
254
255} // namespace Mantid::API
std::string name
Definition Run.cpp:60
std::array< std::multimap< std::string, int >, enum_count > m_names
The list of names.
const std::shared_ptr< IAlgorithm > chooseLoader(const std::string &filename) const
Returns the name of an Algorithm that can load the given filename.
FileLoaderRegistryImpl()
Default constructor (for singleton)
void unsubscribe(const std::string &name, const int version=-1)
Unsubscribe a named algorithm and version from the loader registration.
bool canLoad(const std::string &algorithmName, const std::string &filename) const
Checks whether the given algorithm can load the file.
void removeAlgorithm(const std::string &name, const int version, std::multimap< std::string, int > &typedLoaders)
Remove a named algorithm & version from the given map.
Kernel::Logger m_log
Reference to a logger.
Exception for when an item is not found in a collection.
Definition Exception.h:145
Defines a wrapper around an open file.
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm