Mantid
Loading...
Searching...
No Matches
FileFinder.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 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
18#include "MantidKernel/Glob.h"
21
23#include <boost/lexical_cast.hpp>
24#include <boost/regex.hpp>
25
26#include <algorithm>
27#include <cctype>
28
29#include <boost/algorithm/string.hpp>
30
31#include <filesystem>
32#include <json/value.h>
33
34namespace {
36Mantid::Kernel::Logger g_log("FileFinder");
37
46bool containsWildCard(const std::string &ext) { return std::string::npos != ext.find('*'); }
47
48bool isASCII(const std::string &str) {
49 return !std::any_of(str.cbegin(), str.cend(), [](char c) { return static_cast<unsigned char>(c) > 127; });
50}
51
52} // namespace
53
54namespace Mantid::API {
55using std::string;
56
57// this allowed string could be made into an array of allowed, currently used
58// only by the ISIS SANS group
59const std::string FileFinderImpl::ALLOWED_SUFFIX = "-add";
60//----------------------------------------------------------------------
61// Public member functions
62//----------------------------------------------------------------------
67 // Make sure plugins are loaded
68 FrameworkManager::Instance().loadPlugins();
69
70// determine from Mantid property how sensitive Mantid should be
71#ifdef _WIN32
73#else
74 setCaseSensitive(Kernel::ConfigService::Instance().getValue<bool>("filefinder.casesensitive").value_or(false));
75#endif
76}
77
88
95
106std::filesystem::path FileFinderImpl::getFullPath(const std::string &filename, const bool ignoreDirs) const {
107 return Kernel::ConfigService::Instance().getFullPath(filename, ignoreDirs, m_globOption);
108}
109
115std::string FileFinderImpl::extractAllowedSuffix(std::string &userString) const {
116 if (userString.find(ALLOWED_SUFFIX) == std::string::npos) {
117 // short cut processing as normally there is no suffix
118 return "";
119 }
120
121 // ignore any file extension in checking if a suffix is present
122 std::filesystem::path entry(userString);
123 std::string noExt(entry.stem().string());
124 const size_t repNumChars = ALLOWED_SUFFIX.size();
125 if (noExt.find(ALLOWED_SUFFIX) == noExt.size() - repNumChars) {
126 userString.replace(userString.size() - repNumChars, repNumChars, "");
127 return ALLOWED_SUFFIX;
128 }
129 return "";
130}
131
141 const bool returnDefaultIfNotFound,
142 const std::string &defaultInstrument) const {
143 if ((!hintstr.empty()) && (!isdigit(hintstr[0]))) {
144 // If hint contains path components, use only the filename part for instrument detection
145 std::string filename = toUpper(std::filesystem::path(hintstr).filename().string());
146
147 try {
148 std::string instrName = Kernel::ConfigService::Instance().findLongestInstrumentPrefix(filename);
149
150 // if still empty, throw not found
151 if (instrName.empty()) {
152 throw Kernel::Exception::NotFoundError("Instrument not found", hintstr);
153 }
154
155 return Kernel::ConfigService::Instance().getInstrument(instrName);
157 g_log.debug() << e.what() << "\n";
158 if (!returnDefaultIfNotFound) {
159 throw e;
160 }
161 }
162 }
163 return Kernel::ConfigService::Instance().getInstrument(defaultInstrument);
164}
165
173std::pair<std::string, std::string> FileFinderImpl::toInstrumentAndNumber(const std::string &hintstr,
174 const std::string &defaultInstrument) const {
175 Kernel::InstrumentInfo instr = this->getInstrument(hintstr, true, defaultInstrument);
176 return toInstrumentAndNumber(hintstr, instr);
177}
178
185std::pair<std::string, std::string> FileFinderImpl::toInstrumentAndNumber(const std::string &hintstr,
186 const Kernel::InstrumentInfo &instr) const {
187 g_log.debug() << "toInstrumentAndNumber(" << hintstr << ")\n";
188 std::string runPart;
189
190 if (hintstr.empty()) {
191 throw std::invalid_argument("Malformed hint: empty hint");
192 }
193
194 if (isdigit(hintstr[0])) {
195 runPart = hintstr;
196 } else {
197 const auto hintUpper = toUpper(hintstr);
198 std::string instrPart = instr.name();
199 if (!hintUpper.starts_with(instrPart)) {
200 instrPart = instr.shortName();
201 if (!hintUpper.starts_with(instrPart)) {
202 throw std::invalid_argument("Malformed hint: does not start with instrument name or short name");
203 }
204 }
205
206 // need to advance to the first digit after the instrument name to handle underscores, etc.
207 size_t nChars = instrPart.length();
208 while (nChars < hintstr.size() && !std::isdigit(static_cast<unsigned char>(hintstr[nChars])))
209 ++nChars;
210 if (nChars == hintstr.size())
211 throw std::invalid_argument("Malformed hint: no run number found");
212 runPart = hintstr.substr(nChars);
213 }
214
215 unsigned int irunPart(0);
216 try {
217 irunPart = boost::lexical_cast<unsigned int>(runPart);
218 } catch (boost::bad_lexical_cast &) {
219 std::ostringstream os;
220 os << "Cannot convert '" << runPart << "' to run number.";
221 throw std::invalid_argument(os.str());
222 }
223 size_t nZero = instr.zeroPadding(irunPart);
224 // remove any leading zeros in case there are too many of them
225 std::string::size_type i = runPart.find_first_not_of('0');
226 runPart.erase(0, i);
227 while (runPart.size() < nZero)
228 runPart.insert(0, "0");
229 if (runPart.size() > nZero && nZero != 0) {
230 throw std::invalid_argument("Run number does not match instrument's zero padding");
231 }
232
233 return std::make_pair(instr.filePrefix(irunPart), runPart);
234}
235
248std::string FileFinderImpl::makeFileName(const std::string &hintstr, const Kernel::InstrumentInfo &instrument) const {
249 if (hintstr.empty())
250 return "";
251
252 Kernel::InstrumentInfo instrToUse = instrument;
253 if (!isdigit(hintstr[0])) {
254 try {
255 std::string hintUpper = toUpper(hintstr);
256 std::string shortName = toUpper(instrument.shortName());
257 std::string name = toUpper(instrument.name());
258 if (hintUpper.rfind(shortName, 0) != 0 && hintUpper.rfind(name, 0) != 0) {
259 instrToUse = getInstrument(hintstr, false);
260 }
261 } catch (const std::exception &ex) {
262 g_log.debug() << "Failed to resolve instrument from hint '" << hintstr << "' in makeFileName: " << ex.what();
263 } catch (...) {
264 g_log.debug() << "Failed to resolve instrument from hint '" << hintstr
265 << "' in makeFileName due to an unknown exception.";
266 }
267 }
268
269 std::string filename(hintstr);
270 const std::string suffix = extractAllowedSuffix(filename);
271 const std::string shortName = instrToUse.shortName();
272 std::string delimiter = instrToUse.delimiter();
273
274 // see if starts with the provided instrument name
275 if (filename.substr(0, shortName.size()) == shortName) {
276 filename = filename.substr(shortName.size());
277 if ((!delimiter.empty()) && (filename.substr(0, delimiter.size()) == delimiter))
278 filename = filename.substr(delimiter.size());
279
280 filename = shortName + filename;
281 }
282
283 auto [instrumentName, runNumber] = toInstrumentAndNumber(filename, instrToUse);
284
285 // delimiter and suffix might be empty strings
286 filename = instrumentName + delimiter + runNumber + suffix;
287 return filename;
288}
289
299std::string FileFinderImpl::getExtension(const std::string &filename, const std::vector<std::string> &exts) const {
300 g_log.debug() << "getExtension(" << filename << ", exts[" << exts.size() << "])\n";
301
302 // go through the list of supplied extensions
303 for (const auto &ext : exts) {
304 std::string extension = toUpper(ext);
305 if (extension.rfind('*') == extension.size() - 1) // there is a wildcard at play
306 {
307 extension.resize(extension.rfind('*'));
308 }
309
310 std::size_t found = toUpper(filename).rfind(extension);
311 if (found != std::string::npos) {
312 g_log.debug() << "matched extension \"" << extension << "\" based on \"" << ext << "\"\n";
313 return filename.substr(found); // grab the actual extensions found
314 }
315 }
316
317 g_log.debug() << "Failed to find extension. Just using last \'.\'\n";
318 std::size_t pos = filename.find_last_of('.');
319 if (pos != std::string::npos) {
320 return filename.substr(pos);
321 }
322
323 // couldn't find an extension
324 return "";
325}
326
327std::vector<IArchiveSearch_sptr> FileFinderImpl::getArchiveSearch(const Kernel::FacilityInfo &facility) {
328 std::vector<IArchiveSearch_sptr> archs;
329
330 // get the searchive option from config service and format it
331 std::string archiveOpt = Kernel::ConfigService::Instance().getString("datasearch.searcharchive");
332 std::transform(archiveOpt.begin(), archiveOpt.end(), archiveOpt.begin(), tolower);
333
334 // if it is turned off, not specified, or the facility doesn't have
335 // IArchiveSearch defined, return an empty vector
336 if (archiveOpt.empty() || archiveOpt == "off" || facility.archiveSearch().empty())
337 return archs;
338
339 // determine if the user wants archive search for this facility
340 auto createArchiveSearch = bool(archiveOpt == "all");
341
342 // then see if the facility name appears in the list or if we just want the
343 // default facility
344 if (!createArchiveSearch) {
345 std::string faciltyName = facility.name();
346 std::transform(faciltyName.begin(), faciltyName.end(), faciltyName.begin(), tolower);
347 if (archiveOpt == "on") { // only default facilty
348 std::string defaultFacility = Kernel::ConfigService::Instance().getString("default.facility");
349 std::transform(defaultFacility.begin(), defaultFacility.end(), defaultFacility.begin(), tolower);
350 createArchiveSearch = bool(faciltyName == defaultFacility);
351 } else { // everything in the list
352 createArchiveSearch = bool(archiveOpt.find(faciltyName) != std::string::npos);
353 }
354 }
355
356 // put together the list of IArchiveSearch to use
357 if (createArchiveSearch) {
358 for (const auto &facilityname : facility.archiveSearch()) {
359 g_log.debug() << "get archive search for the facility..." << facilityname << "\n";
360 archs.emplace_back(ArchiveSearchFactory::Instance().create(facilityname));
361 }
362 }
363 return archs;
364}
365
379 const std::vector<std::string> &extensionsProvided,
380 const bool useOnlyExtensionsProvided,
381 const std::string &defaultInstrument) const {
382 std::string hint = Kernel::Strings::strip(hintstr);
383 if (hint.empty())
384 return API::Result<std::filesystem::path>("", "File not found.");
385
386 const Kernel::InstrumentInfo instrument = this->getInstrument(hint, true, defaultInstrument);
387
388 return findRun(hint, instrument, extensionsProvided, useOnlyExtensionsProvided);
389}
390
404 const Kernel::InstrumentInfo &instrument,
405 const std::vector<std::string> &extensionsProvided,
406 const bool useOnlyExtensionsProvided) const {
407 std::string hint = Kernel::Strings::strip(hintstr);
408 g_log.debug() << "vector findRun(\'" << hint << "\', exts[" << extensionsProvided.size() << "])\n";
409
410 // if partial filename or run number is not supplied, return here
411 if (hint.empty())
412 return API::Result<std::filesystem::path>("", "File not found.");
413
414 // if it looks like a full filename just do a quick search for it
415 std::filesystem::path hintPath(hint);
416 if (hintPath.has_extension()) {
417 // check in normal search locations
418 g_log.debug() << "hintPath is not empty, check in normal search locations"
419 << "\n";
420 auto path = getFullPath(hint);
421 if (!path.empty()) {
422 try {
423 if (std::filesystem::exists(path)) {
424 g_log.information() << "found path = " << path << '\n';
425 return path;
426 }
427 } catch (const std::exception &) {
428 }
429 } else {
430 g_log.debug() << "Unable to find files via directory search with the "
431 "filename that looks like a full filename"
432 << "\n";
433 }
434 }
435
436 // get facility extensions
437 const Kernel::FacilityInfo &facility = instrument.facility();
438 const std::vector<std::string> facilityExtensions = facility.extensions();
439
440 // Do we need to try and form a filename from our preset rules
441 std::string filename(hint);
442 std::string extension = getExtension(hint, facilityExtensions);
443 if (!facilityExtensions.empty())
444 filename = hint.substr(0, hint.rfind(extension));
445 if (hintPath.parent_path().empty()) {
446 try {
447 if (!facility.noFilePrefix()) {
448 filename = makeFileName(filename, instrument);
449 }
450 } catch (std::invalid_argument &) {
451 if (filename.length() >= hint.length()) {
452 g_log.information() << "Could not form filename from standard rules '" << filename << "'\n";
453 }
454 }
455 }
456
457 if (filename.empty())
458 return API::Result<std::filesystem::path>("", "File not found");
459
460 // Look first at the original filename then for case variations. This is
461 // important
462 // on platforms where file names ARE case sensitive.
463 // Sorry for the duplication, a last minute fix was required. Ticket #6419 is
464 // tasked with a redesign of
465 // the whole file finding concept.
466
467 std::set<std::string> filenames;
468 filenames.insert(filename);
469 if (!getCaseSensitive()) {
470 std::string transformed(filename);
471 std::transform(filename.begin(), filename.end(), transformed.begin(), toupper);
472 filenames.insert(transformed);
473 std::transform(filename.begin(), filename.end(), transformed.begin(), tolower);
474 filenames.insert(transformed);
475 }
476
477 // Merge the extensions & throw out duplicates
478 // On Windows throw out ones that only vary in case
479 std::vector<std::string> extensionsToSearch;
480 extensionsToSearch.reserve(1 + extensionsProvided.size() + facilityExtensions.size());
481
482 if (useOnlyExtensionsProvided) {
483 getUniqueExtensions(extensionsProvided, extensionsToSearch);
484
485 } else {
486 if (!extension.empty()) {
487 extensionsToSearch.emplace_back(extension);
488
489 } else {
490 getUniqueExtensions(extensionsProvided, extensionsToSearch);
491 getUniqueExtensions(facilityExtensions, extensionsToSearch);
492 }
493 }
494
495 // determine which archive search facilities to use
496 std::vector<IArchiveSearch_sptr> archs = getArchiveSearch(facility);
497
498 auto path = getPath(archs, filenames, extensionsToSearch);
499 if (path) {
500 g_log.information() << "Found path = " << path << '\n';
501 return path;
502 }
503 // Path not found
504
505 // If only looked for extension in filename
506 if (!useOnlyExtensionsProvided && extensionsToSearch.size() == 1) {
507
508 extensionsToSearch.pop_back(); // No need to search for missing extension again
509 getUniqueExtensions(extensionsProvided, extensionsToSearch);
510 getUniqueExtensions(facilityExtensions, extensionsToSearch);
511
512 g_log.warning() << "Extension ['" << extension << "'] not found.\n";
513 g_log.warning() << "Searching for other facility extensions." << std::endl;
514
515 path = getPath(archs, filenames, extensionsToSearch);
516 if (path) {
517 g_log.information() << "Found path = " << path << '\n';
518 return path;
519 }
520 }
521 g_log.information() << "Unable to find run with hint " << hint << "\n";
522 return API::Result<std::filesystem::path>("", path.errors());
523}
524
533void FileFinderImpl::getUniqueExtensions(const std::vector<std::string> &extensionsToAdd,
534 std::vector<std::string> &uniqueExts) const {
535 const bool isCaseSensitive = getCaseSensitive();
536 for (const auto &cit : extensionsToAdd) {
537 std::string transformed(cit);
538 if (!isCaseSensitive) {
539 std::transform(cit.begin(), cit.end(), transformed.begin(), tolower);
540 }
541 const auto searchItr = std::find(uniqueExts.begin(), uniqueExts.end(), transformed);
542 if (searchItr == uniqueExts.end()) {
543 uniqueExts.emplace_back(transformed);
544 }
545 }
546}
547
554std::string FileFinderImpl::validateRuns(const std::string &searchText) const {
555 if (!isASCII(searchText))
556 return "An unsupported non-ASCII character was found in the search text.";
557 return "";
558}
559
575std::vector<std::filesystem::path> FileFinderImpl::findRuns(const std::string &hintstr,
576 const std::vector<std::string> &extensionsProvided,
577 const bool useOnlyExtensionsProvided) const {
578 auto const error = validateRuns(hintstr);
579 if (!error.empty())
580 throw std::invalid_argument(error);
581
582 std::string hint = Kernel::Strings::strip(hintstr);
583 g_log.debug() << "findRuns hint = " << hint << "\n";
584 std::vector<std::filesystem::path> res;
587 static const boost::regex digits("[0-9]+");
588 auto h = hints.begin();
589
590 std::string instrSName; // cache the name to reuse for additional files
591 for (; h != hints.end(); ++h) {
592 // Quick check for a filename
593 bool fileSuspected = false;
594 // Assume if the hint contains either a "/" or "\" it is a filename..
595 if ((*h).find("\\") != std::string::npos) {
596 fileSuspected = true;
597 }
598 if ((*h).find("/") != std::string::npos) {
599 fileSuspected = true;
600 }
601 if ((*h).find(ALLOWED_SUFFIX) != std::string::npos) {
602 fileSuspected = true;
603 }
604
607 if ((range.count() > 2) && (!fileSuspected)) {
608 throw std::invalid_argument("Malformed range of runs: " + *h);
609 } else if ((range.count() == 2) && (!fileSuspected)) {
610 std::pair<std::string, std::string> p1 = toInstrumentAndNumber(range[0], instrSName);
611
612 // cache instrument short name for later use
613 instrSName = p1.first;
614 Kernel::InstrumentInfo cachedInstr = this->getInstrument("", true, instrSName);
615
616 std::string run = p1.second;
617 size_t nZero = run.size(); // zero padding
618 if (range[1].size() > nZero) {
619 throw std::invalid_argument("Malformed range of runs: " + *h +
620 ". The end of string value is longer than "
621 "the instrument's zero padding");
622 }
623 auto runNumber = boost::lexical_cast<int>(run);
624 std::string runEnd = run;
625 // Adds zero padding to end of range.
626 runEnd.replace(runEnd.end() - range[1].size(), runEnd.end(), range[1]);
627
628 // Throw if runEnd contains something else other than a digit.
629 if (!boost::regex_match(runEnd, digits))
630 throw std::invalid_argument("Malformed range of runs: Part of the run "
631 "has a non-digit character in it.");
632
633 auto runEndNumber = boost::lexical_cast<int>(runEnd);
634 if (runEndNumber < runNumber) {
635 throw std::invalid_argument("Malformed range of runs: " + *h);
636 }
637 std::string previousPath, previousExt;
638 for (int irun = runNumber; irun <= runEndNumber; ++irun) {
639 run = std::to_string(irun);
640 while (run.size() < nZero)
641 run.insert(0, "0");
642
643 // Quick check if file can be created from previous successfully found
644 // path/extension
645 if (!previousPath.empty() && !previousExt.empty()) {
646 try {
647 const std::filesystem::path file(previousPath + p1.first + run + previousExt);
648 if (std::filesystem::exists(file)) {
649 res.emplace_back(file.string());
650 continue;
651 }
652 } catch (...) {
653 // Clear cached path and extension
654 previousPath = previousExt = "";
655 }
656 }
657
658 auto path = findRun(p1.first + run, cachedInstr, extensionsProvided, useOnlyExtensionsProvided).result();
659
660 if (!path.empty()) {
661 // Cache successfully found path and extension
662 previousExt = path.extension().string();
663 // Append separator for string concatenation later
664 previousPath = path.parent_path().string() + std::string(1, std::filesystem::path::preferred_separator);
665 res.emplace_back(path);
666 } else {
667 throw Kernel::Exception::NotFoundError("Unable to find file:", std::move(run));
668 }
669 }
670 } else {
671 Kernel::InstrumentInfo instr = this->getInstrument(*h, true, instrSName);
672 // update instrument short name for later use
673 instrSName = instr.shortName();
674
675 auto path = findRun(*h, instr, extensionsProvided, useOnlyExtensionsProvided).result();
676
677 if (!path.empty()) {
678 res.emplace_back(path);
679 } else {
680 throw Kernel::Exception::NotFoundError("Unable to find file:", *h);
681 }
682 }
683 }
684
685 return res;
686}
687
689FileFinderImpl::getISISInstrumentDataCachePath(const std::string &cachePathToSearch,
690 const std::set<std::string> &hintstrs,
691 const std::vector<std::string> &exts) const {
692 std::string errors;
693 auto dataCache = API::ISISInstrumentDataCache(cachePathToSearch);
694
695 for (const auto &hint : hintstrs) {
696
697 std::string parentDirPath;
698
699 try {
700 parentDirPath = dataCache.getFileParentDirectoryPath(hint);
701
702 } catch (const std::invalid_argument &e) {
703 errors += "Data cache: " + std::string(e.what());
704 return API::Result<std::filesystem::path>("", errors);
705
706 } catch (const Json::Exception &e) {
707 errors += "Data cache: Failed parsing to JSON: " + std::string(e.what()) +
708 "Error likely due to accessing instrument index file while it was being updated on IDAaaS.";
709 return API::Result<std::filesystem::path>("", errors);
710 }
711
712 if (!std::filesystem::exists(parentDirPath)) {
713 errors += "Data cache: Directory not found: " + parentDirPath;
714 return API::Result<std::filesystem::path>("", errors);
715 }
716
717 for (const auto &ext : exts) {
718 std::filesystem::path filePath(parentDirPath + '/' + hint + ext);
719
720 try { // Catches error for permission denied
721 if (std::filesystem::exists(filePath)) {
722 return API::Result<std::filesystem::path>(filePath);
723 }
724 } catch (const std::filesystem::filesystem_error &e) {
725 errors += "Data cache: " + std::string(e.what());
726 return API::Result<std::filesystem::path>("", errors);
727 }
728 }
729 errors += "Data cache: " + hint + " not found in " + parentDirPath;
730 }
731 return API::Result<std::filesystem::path>("", errors);
732}
733
744const API::Result<std::filesystem::path> FileFinderImpl::getArchivePath(const std::vector<IArchiveSearch_sptr> &archs,
745 const std::set<std::string> &hintstrs,
746 const std::vector<std::string> &exts) const {
747 g_log.debug() << "getArchivePath([IArchiveSearch_sptr], [ ";
748 for (const auto &iter : hintstrs)
749 g_log.debug() << iter << " ";
750 g_log.debug() << "], [ ";
751 for (const auto &iter : exts)
752 g_log.debug() << iter << " ";
753 g_log.debug() << "])\n";
754
755 string errors = "";
756 for (const auto &arch : archs) {
757 try {
758 g_log.debug() << "Getting archive path for requested files\n";
759 auto path = arch->getArchivePath(hintstrs, exts);
760 if (path)
761 return path;
762 else
763 errors += path.errors();
764 } catch (...) {
765 }
766 }
767 return API::Result<std::filesystem::path>("", errors);
768}
769
781const API::Result<std::filesystem::path> FileFinderImpl::getPath(const std::vector<IArchiveSearch_sptr> &archs,
782 const std::set<std::string> &hintstrs,
783 const std::vector<std::string> &exts) const {
784 std::filesystem::path path;
785
786 std::vector<std::string> extensions;
787 extensions.assign(exts.begin(), exts.end());
788
789 // Remove wild cards.
790 extensions.erase(std::remove_if(extensions.begin(), extensions.end(), containsWildCard), extensions.end());
791
792 const std::vector<std::string> &searchPaths = Kernel::ConfigService::Instance().getDataSearchDirs();
793
794 // Before we try any globbing, make sure we exhaust all reasonable attempts at
795 // constructing the possible filename.
796 // Avoiding the globbing of getFullPath() for as long as possible will help
797 // performance when calling findRuns()
798 // with a large range of files, especially when searchPaths consists of
799 // folders containing a large number of runs.
800 for (const auto &extension : extensions) {
801 for (const auto &hint : hintstrs) {
802 for (const auto &searchPath : searchPaths) {
803 try {
804 const auto filePath = std::filesystem::path(searchPath) / (hint + extension);
805 if (std::filesystem::exists(filePath))
806 return filePath;
807
808 } catch (const std::exception &) { /* File does not exist, just carry on. */
809 }
810 }
811 }
812 }
813
814 for (const auto &extension : extensions) {
815 for (const auto &hint : hintstrs) {
816 path = getFullPath(hint + extension);
817 try {
818 if (!path.empty() && std::filesystem::exists(path)) {
819 g_log.debug() << "path returned from getFullPath() = " << path << '\n';
821 }
822 } catch (std::exception &e) {
823 g_log.error() << "Cannot open file " << path << ": " << e.what() << '\n';
824 return API::Result<std::filesystem::path>("", "Cannot open file.");
825 }
826 }
827 }
828
829 // Search data cache
830 string errors;
831 std::filesystem::path cachePathToSearch(Kernel::ConfigService::Instance().getString("datacachesearch.directory"));
832 // Only expect to find path to data cache on IDAaaS
833 if (std::filesystem::exists(cachePathToSearch)) {
834
835 auto cacheFilePath = getISISInstrumentDataCachePath(cachePathToSearch.string(), hintstrs, exts);
836
837 if (cacheFilePath) {
838 return cacheFilePath;
839 }
840 errors += cacheFilePath.errors();
841
842 } else {
843 g_log.debug() << "Data cache directory not found, proceeding with the search." << std::endl;
844 errors += "Could not find data cache directory: " + cachePathToSearch.string() + '\n';
845 }
846
847 // Search the archive
848 if (!archs.empty()) {
849 g_log.debug() << "Search the archives\n";
850 const auto archivePath = getArchivePath(archs, hintstrs, exts);
851 if (archivePath) {
852 try {
853 if (std::filesystem::exists(archivePath.result()))
854 return archivePath;
855 } catch (std::exception &e) {
856 g_log.error() << "Cannot open file " << archivePath << ": " << e.what() << '\n';
857 return API::Result<std::filesystem::path>("", "Cannot open file.");
858 }
859 } else
860 errors += archivePath.errors();
861
862 } // archs
863 return API::Result<std::filesystem::path>("", errors);
864}
865
866std::string FileFinderImpl::toUpper(const std::string &src) const {
867 std::string result = src;
868 std::transform(result.begin(), result.end(), result.begin(), toupper);
869 return result;
870}
871
872} // namespace Mantid::API
std::string name
Definition Run.cpp:60
double error
std::pair< std::string, std::string > toInstrumentAndNumber(const std::string &hintstr, const std::string &defaultInstrument="") const
Extracts the instrument name and run number from a hint.
std::string validateRuns(const std::string &searchText) const
A method that returns error messages if the provided runs are invalid.
const API::Result< std::filesystem::path > getPath(const std::vector< IArchiveSearch_sptr > &archs, const std::set< std::string > &hintstrs, const std::vector< std::string > &exts) const
Return the full path to the file given its name, checking local directories first.
static std::vector< IArchiveSearch_sptr > getArchiveSearch(const Kernel::FacilityInfo &facility)
bool getCaseSensitive() const
Option to get if file finder should be case sensitive.
std::string getExtension(const std::string &filename, const std::vector< std::string > &exts) const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
int m_globOption
glob option - set to case sensitive or insensitive
Definition FileFinder.h:90
std::string extractAllowedSuffix(std::string &userString) const
Run numbers can be followed by an allowed string.
const Kernel::InstrumentInfo getInstrument(const std::string &hintstr, const bool returnDefaultIfNotFound=true, const std::string &defaultInstrument="") const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
const API::Result< std::filesystem::path > getISISInstrumentDataCachePath(const std::string &cachePathToSearch, const std::set< std::string > &hintstrs, const std::vector< std::string > &exts) const
const API::Result< std::filesystem::path > findRun(const std::string &hintstr, const std::vector< std::string > &exts={}, const bool useExtsOnly=false, const std::string &defaultInstrument="") const
Find a path to a single file from a hint.
static const std::string ALLOWED_SUFFIX
a string that is allowed at the end of any run number
Definition FileFinder.h:73
std::filesystem::path getFullPath(const std::string &filename, const bool ignoreDirs=false) const
Return the full path to the file given its name.
FileFinderImpl()
Default constructor.
void getUniqueExtensions(const std::vector< std::string > &extensionsToAdd, std::vector< std::string > &uniqueExts) const
Given a set of already determined extensions and new extensions, create a set of all extensions.
std::string toUpper(const std::string &src) const
const API::Result< std::filesystem::path > getArchivePath(const std::vector< IArchiveSearch_sptr > &archs, const std::set< std::string > &hintstrs, const std::vector< std::string > &exts) const
Return the path to the file found in archive.
void setCaseSensitive(const bool cs)
Option to set if file finder should be case sensitive.
std::vector< std::filesystem::path > findRuns(const std::string &hintstr, const std::vector< std::string > &exts={}, const bool useExtsOnly=false) const
Find a list of files file given a hint.
std::string makeFileName(const std::string &hintstr, const Kernel::InstrumentInfo &instrument) const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
Exception for when an item is not found in a collection.
Definition Exception.h:145
const char * what() const noexcept override
Writes out the range and limits.
A class that holds information about a facility.
const std::vector< std::string > & archiveSearch() const
Return the archive search interface names.
const std::vector< std::string > & extensions() const
Returns a list of file extensions.
bool noFilePrefix() const
Returns a bool indicating whether prefix is required in file names.
const std::string & name() const
Return the name of the facility.
static constexpr int GLOB_DEFAULT
Glob option constants (compatible with Poco::Glob)
Definition Glob.h:29
static constexpr int GLOB_CASELESS
Definition Glob.h:30
A class that holds information about an instrument.
const std::string & shortName() const
Return the short name of the instrument.
std::string filePrefix(unsigned int runNumber) const
Returns file prefix for this instrument and a run number.
const FacilityInfo & facility() const
The facility to which this instrument belongs.
const std::string & delimiter() const
Returns the default delimiter between instrument name and run number.
const std::string & name() const
Return the name of the instrument.
int zeroPadding(unsigned int runNumber) const
Returns zero padding for this instrument and a run number.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Iterator begin()
Iterator referring to first element in the container.
@ TOK_IGNORE_EMPTY
ignore empty tokens
@ TOK_TRIM
remove leading and trailing whitespace from tokens
std::size_t size() const noexcept
Get the total number of tokens.
Iterator end()
Iterator referring to the past-the-end element in the container.
std::size_t count() const
Get the total number of tokens.
Kernel::Logger g_log("ExperimentInfo")
static logger object
Kernel::Logger g_log("DetermineSpinStateOrder")
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
Definition Strings.cpp:419
std::string to_string(const wide_integer< Bits, Signed > &n)