Mantid
Loading...
Searching...
No Matches
NexusDescriptor.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 +
8
9// clang-format off
10#include <nexus/NeXusFile.hpp>
11#include <nexus/NeXusException.hpp>
12// clang-format on
13
14#include <Poco/File.h>
15#include <Poco/Path.h>
16
17#include <cstring>
18#include <string>
19
20namespace Mantid::Kernel {
21//---------------------------------------------------------------------------------------------------------------------------
22// static NexusDescriptor constants
23//---------------------------------------------------------------------------------------------------------------------------
25const size_t NexusDescriptor::HDFMagicSize = 4;
27const unsigned char NexusDescriptor::HDFMagic[4] = {'\016', '\003', '\023', '\001'}; // From HDF4::hfile.h
28
32const unsigned char NexusDescriptor::HDF5Signature[8] = {137, 'H', 'D', 'F', '\r', '\n', '\032', '\n'};
33
34namespace {
35//---------------------------------------------------------------------------------------------------------------------------
36// Anonymous helper methods to use isReadable methods to use an open file handle
37//---------------------------------------------------------------------------------------------------------------------------
38
49bool isHDFHandle(FILE *fileHandle, NexusDescriptor::Version version) {
50 if (!fileHandle)
51 throw std::invalid_argument("HierarchicalFileDescriptor::isHierarchical - Invalid file handle");
52
53 bool result(false);
54
55 // HDF4 check requires 4 bytes, HDF5 check requires 8 bytes
56 // Use same buffer and waste a few bytes if only checking HDF4
57 unsigned char buffer[8] = {'0', '0', '0', '0', '0', '0', '0', '0'};
59 std::fread(static_cast<void *>(&buffer), sizeof(unsigned char), NexusDescriptor::HDF5SignatureSize, fileHandle)) {
60 throw std::runtime_error("Error while reading file");
61 }
62
63 // Number of bytes read doesn't matter as if it is not enough then the memory
64 // simply won't match
65 // as the buffer has been "zeroed"
66 if (version == NexusDescriptor::Version5 || version == NexusDescriptor::AnyVersion) {
67 result = (std::memcmp(&buffer, &NexusDescriptor::HDF5Signature, NexusDescriptor::HDF5SignatureSize) == 0);
68 }
69 if (!result && (version == NexusDescriptor::Version4 || version == NexusDescriptor::AnyVersion)) {
70 result = (std::memcmp(&buffer, &NexusDescriptor::HDFMagic, NexusDescriptor::HDFMagicSize) == 0);
71 }
72
73 // Return file stream to start of file
74 std::rewind(fileHandle);
75 return result;
76}
77} // namespace
78
79//---------------------------------------------------------------------------------------------------------------------------
80// static NexusDescriptor methods
81//---------------------------------------------------------------------------------------------------------------------------
82
90bool NexusDescriptor::isReadable(const std::string &filename, const Version version) {
91 FILE *fd = fopen(filename.c_str(), "rb");
92 if (!fd) {
93 throw std::invalid_argument("HierarchicalFileDescriptor::isHierarchical - Unable to open file '" + filename + "'");
94 }
95 const bool result = isHDFHandle(fd, version); // use anonymous helper
96 fclose(fd);
97 return result;
98}
99
100//---------------------------------------------------------------------------------------------------------------------------
101// NexusDescriptor public methods
102//---------------------------------------------------------------------------------------------------------------------------
111NexusDescriptor::NexusDescriptor(const std::string &filename, const bool init)
112 : m_filename(), m_extension(), m_firstEntryNameType(), m_rootAttrs(), m_pathsToTypes(), m_file(nullptr) {
113 if (filename.empty()) {
114 throw std::invalid_argument("NexusDescriptor() - Empty filename '" + filename + "'");
115 }
116 if (!Poco::File(filename).exists()) {
117 throw std::invalid_argument("NexusDescriptor() - File '" + filename + "' does not exist");
118 }
119
120 if (init) {
121 try {
122 // this is very expesive as it walk the entire file
124 } catch (::NeXus::Exception &e) {
125 throw std::invalid_argument("NexusDescriptor::initialize - File '" + filename +
126 "' does not look like a HDF file.\n Error was: " + e.what());
127 }
128 }
129}
130
132
134const std::pair<std::string, std::string> &NexusDescriptor::firstEntryNameType() const { return m_firstEntryNameType; }
135
140bool NexusDescriptor::hasRootAttr(const std::string &name) const { return (m_rootAttrs.count(name) == 1); }
141
147bool NexusDescriptor::pathExists(const std::string &path) const {
148 return (m_pathsToTypes.find(path) != m_pathsToTypes.end());
149}
150
157bool NexusDescriptor::pathOfTypeExists(const std::string &path, const std::string &type) const {
158 auto it = m_pathsToTypes.find(path);
159 if (it != m_pathsToTypes.end()) {
160 return (it->second == type);
161 } else
162 return false;
163}
164
170std::string NexusDescriptor::pathOfType(const std::string &type) const {
171 auto iend = m_pathsToTypes.end();
172 for (auto it = m_pathsToTypes.begin(); it != iend; ++it) {
173 if (type == it->second)
174 return it->first;
175 }
176 return "";
177}
178
183bool NexusDescriptor::classTypeExists(const std::string &classType) const {
184 auto iend = m_pathsToTypes.end();
185 for (auto it = m_pathsToTypes.begin(); it != iend; ++it) {
186 if (classType == it->second)
187 return true;
188 }
189 return false;
190}
191
192//---------------------------------------------------------------------------------------------------------------------------
193// NexusDescriptor private methods
194//---------------------------------------------------------------------------------------------------------------------------
195
199void NexusDescriptor::initialize(const std::string &filename) {
201 m_extension = "." + Poco::Path(filename).getExtension();
202
203 m_file = std::make_unique<::NeXus::File>(this->filename());
204
205 m_file->openPath("/");
206 m_rootAttrs.clear();
207 m_pathsToTypes.clear();
208 walkFile(*m_file, "", "", m_pathsToTypes, 0);
209}
210
219void NexusDescriptor::walkFile(::NeXus::File &file, const std::string &rootPath, const std::string &className,
220 std::map<std::string, std::string> &pmap, int level) {
221 if (!rootPath.empty()) {
222 pmap.emplace(rootPath, className);
223 }
224 if (level == 0) {
225 auto attrInfos = file.getAttrInfos();
226 for (auto &attrInfo : attrInfos) {
227 m_rootAttrs.insert(attrInfo.name);
228 }
229 }
230
231 auto dirents = file.getEntries();
232 auto itend = dirents.end();
233 for (auto it = dirents.begin(); it != itend; ++it) {
234 const std::string &entryName = it->first;
235 const std::string &entryClass = it->second;
236 const std::string entryPath = std::string(rootPath).append("/").append(entryName);
237 if (entryClass == "SDS" || entryClass == "ILL_data_scan_vars" || entryClass == "NXill_data_scan_vars") {
238 pmap.emplace(entryPath, entryClass);
239 } else if (entryClass == "CDF0.0") {
240 // Do nothing with this
241 } else {
242 if (level == 0)
243 m_firstEntryNameType = (*it); // copy first entry name & type
244 file.openGroup(entryName, entryClass);
245 walkFile(file, entryPath, entryClass, pmap, level + 1);
246 }
247 }
248 file.closeGroup();
249}
250
251} // namespace Mantid::Kernel
std::map< std::string, std::string > m_pathsToTypes
Map of full path strings to types. Can check if path exists quickly.
std::unordered_set< std::string > m_rootAttrs
Root attributes.
bool hasRootAttr(const std::string &name) const
Query if the given attribute exists on the root node.
std::string m_filename
Full filename.
void walkFile(::NeXus::File &file, const std::string &rootPath, const std::string &className, std::map< std::string, std::string > &pmap, int level)
Walk the tree and cache the structure.
bool classTypeExists(const std::string &classType) const
Query if a given type exists somewhere in the file.
static const unsigned char HDF5Signature[8]
signature identifying a HDF5 file.
std::pair< std::string, std::string > m_firstEntryNameType
First entry name/type.
static bool isReadable(const std::string &filename, const Version version=AnyVersion)
Returns true if the file is considered to store data in a hierarchy.
Version
Enumerate HDF possible versions.
NexusDescriptor()=delete
Disable default constructor.
bool pathOfTypeExists(const std::string &path, const std::string &type) const
Query if a path exists of a given type.
void initialize(const std::string &filename)
Initialize object with filename.
static const unsigned char HDFMagic[4]
HDF cookie that is stored in the first 4 bytes of the file.
static const size_t HDFMagicSize
Size of HDF magic number.
const std::pair< std::string, std::string > & firstEntryNameType() const
Returns the name & type of the first entry in the file.
static size_t HDF5SignatureSize
Size of HDF5 signature.
std::unique_ptr<::NeXus::File > m_file
Open NeXus handle.
std::string m_extension
Extension.
bool pathExists(const std::string &path) const
Query if a path exists.
const std::string & filename() const
Access the filename.
std::string pathOfType(const std::string &type) const
return the path of a given type
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
Generate a tableworkspace to store the calibration results.