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::string 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
140 const bool returnDefaultIfNotFound) const {
141 if ((!hint.empty()) && (!isdigit(hint[0]))) {
142 string instrName(hint);
143 std::filesystem::path path(instrName);
144 instrName = path.filename().string();
145 if (instrName.starts_with("PG3") || instrName.starts_with("pg3")) {
146 instrName = "PG3";
147 }
148 // We're extending this nasty hack to accomodate data archive searching for
149 // SANS2D.
150 // While this certainly shouldn't be considered good practice, #7515 exists
151 // to
152 // completely redesign FileFinder -- this quick fix will have to do until
153 // all this
154 // code gets an overhaul as part of that ticket. Please think twice before
155 // adding
156 // any more instruments to this list.
157 else if (instrName.starts_with("SANS2D") || instrName.starts_with("sans2d")) {
158 instrName = "SANS2D";
159 } else {
160 // go forwards looking for the run number to start
161 {
162 const auto it = std::find_if(instrName.begin(), instrName.end(), isdigit);
163 const auto nChars = std::distance(instrName.begin(), it);
164 instrName.resize(nChars);
165 }
166
167 // go backwards looking for the instrument name to end - gets around
168 // delimiters
169 if (!instrName.empty()) {
170 const auto it = std::find_if(instrName.rbegin(), instrName.rend(), isalpha);
171 const auto nChars = std::distance(it, instrName.rend());
172 instrName.resize(nChars);
173 }
174 }
175 try {
176 const Kernel::InstrumentInfo instrument = Kernel::ConfigService::Instance().getInstrument(instrName);
177 return instrument;
179 g_log.debug() << e.what() << "\n";
180 if (!returnDefaultIfNotFound) {
181 throw e;
182 }
183 }
184 }
185 return Kernel::ConfigService::Instance().getInstrument();
186}
187
193std::pair<std::string, std::string> FileFinderImpl::toInstrumentAndNumber(const std::string &hint) const {
194 g_log.debug() << "toInstrumentAndNumber(" << hint << ")\n";
195 std::string instrPart;
196 std::string runPart;
197
198 if (isdigit(hint[0])) {
199 instrPart = Kernel::ConfigService::Instance().getInstrument().shortName();
200 runPart = hint;
201 } else {
203 std::string::const_reverse_iterator it = std::find_if(hint.rbegin(), hint.rend(), std::not_fn(isdigit));
204 // No non-digit or all non-digits
205 if (it == hint.rend() || it == hint.rbegin()) {
206 throw std::invalid_argument("Malformed hint to FileFinderImpl::makeFileName: " + hint);
207 }
208 std::string::size_type nChars = std::distance(it, hint.rend());
209
210 // Add in special test for PG3
211 if (boost::algorithm::istarts_with(hint, "PG3")) {
212 instrPart = "PG3";
213 nChars = instrPart.length();
214 }
215 // Another nasty check for SANS2D. Will do until FileFinder redesign.
216 else if (boost::algorithm::istarts_with(hint, "SANS2D")) {
217 instrPart = "SANS2D";
218 nChars = instrPart.length();
219 } else {
220 instrPart = hint.substr(0, nChars);
221 }
222
223 runPart = hint.substr(nChars);
224 }
225
226 unsigned int irunPart(0);
227 try {
228 irunPart = boost::lexical_cast<unsigned int>(runPart);
229 } catch (boost::bad_lexical_cast &) {
230 std::ostringstream os;
231 os << "Cannot convert '" << runPart << "' to run number.";
232 throw std::invalid_argument(os.str());
233 }
234 Kernel::InstrumentInfo instr = Kernel::ConfigService::Instance().getInstrument(instrPart);
235 size_t nZero = instr.zeroPadding(irunPart);
236 // remove any leading zeros in case there are too many of them
237 std::string::size_type i = runPart.find_first_not_of('0');
238 runPart.erase(0, i);
239 while (runPart.size() < nZero)
240 runPart.insert(0, "0");
241 if (runPart.size() > nZero && nZero != 0) {
242 throw std::invalid_argument("Run number does not match instrument's zero padding");
243 }
244
245 instrPart = instr.filePrefix(irunPart);
246
247 return std::make_pair(instrPart, runPart);
248}
249
262std::string FileFinderImpl::makeFileName(const std::string &hint, const Kernel::InstrumentInfo &instrument) const {
263 if (hint.empty())
264 return "";
265
266 std::string filename(hint);
267 const std::string suffix = extractAllowedSuffix(filename);
268 const std::string shortName = instrument.shortName();
269 std::string delimiter = instrument.delimiter();
270
271 // see if starts with the provided instrument name
272 if (filename.substr(0, shortName.size()) == shortName) {
273 filename = filename.substr(shortName.size());
274 if ((!delimiter.empty()) && (filename.substr(0, delimiter.size()) == delimiter))
275 filename = filename.substr(delimiter.size());
276
277 filename = shortName + filename;
278 }
279
280 auto [instrumentName, runNumber] = toInstrumentAndNumber(filename);
281
282 // delimiter and suffix might be empty strings
283 filename = instrumentName + delimiter + runNumber + suffix;
284 return filename;
285}
286
296std::string FileFinderImpl::getExtension(const std::string &filename, const std::vector<std::string> &exts) const {
297 g_log.debug() << "getExtension(" << filename << ", exts[" << exts.size() << "])\n";
298
299 // go through the list of supplied extensions
300 for (const auto &ext : exts) {
301 std::string extension = toUpper(ext);
302 if (extension.rfind('*') == extension.size() - 1) // there is a wildcard at play
303 {
304 extension.resize(extension.rfind('*'));
305 }
306
307 std::size_t found = toUpper(filename).rfind(extension);
308 if (found != std::string::npos) {
309 g_log.debug() << "matched extension \"" << extension << "\" based on \"" << ext << "\"\n";
310 return filename.substr(found); // grab the actual extensions found
311 }
312 }
313
314 g_log.debug() << "Failed to find extension. Just using last \'.\'\n";
315 std::size_t pos = filename.find_last_of('.');
316 if (pos != std::string::npos) {
317 return filename.substr(pos);
318 }
319
320 // couldn't find an extension
321 return "";
322}
323
324std::vector<IArchiveSearch_sptr> FileFinderImpl::getArchiveSearch(const Kernel::FacilityInfo &facility) {
325 std::vector<IArchiveSearch_sptr> archs;
326
327 // get the searchive option from config service and format it
328 std::string archiveOpt = Kernel::ConfigService::Instance().getString("datasearch.searcharchive");
329 std::transform(archiveOpt.begin(), archiveOpt.end(), archiveOpt.begin(), tolower);
330
331 // if it is turned off, not specified, or the facility doesn't have
332 // IArchiveSearch defined, return an empty vector
333 if (archiveOpt.empty() || archiveOpt == "off" || facility.archiveSearch().empty())
334 return archs;
335
336 // determine if the user wants archive search for this facility
337 auto createArchiveSearch = bool(archiveOpt == "all");
338
339 // then see if the facility name appears in the list or if we just want the
340 // default facility
341 if (!createArchiveSearch) {
342 std::string faciltyName = facility.name();
343 std::transform(faciltyName.begin(), faciltyName.end(), faciltyName.begin(), tolower);
344 if (archiveOpt == "on") { // only default facilty
345 std::string defaultFacility = Kernel::ConfigService::Instance().getString("default.facility");
346 std::transform(defaultFacility.begin(), defaultFacility.end(), defaultFacility.begin(), tolower);
347 createArchiveSearch = bool(faciltyName == defaultFacility);
348 } else { // everything in the list
349 createArchiveSearch = bool(archiveOpt.find(faciltyName) != std::string::npos);
350 }
351 }
352
353 // put together the list of IArchiveSearch to use
354 if (createArchiveSearch) {
355 for (const auto &facilityname : facility.archiveSearch()) {
356 g_log.debug() << "get archive search for the facility..." << facilityname << "\n";
357 archs.emplace_back(ArchiveSearchFactory::Instance().create(facilityname));
358 }
359 }
360 return archs;
361}
362
374const API::Result<std::string> FileFinderImpl::findRun(const std::string &hintstr,
375 const std::vector<std::string> &extensionsProvided,
376 const bool useOnlyExtensionsProvided) const {
377 std::string hint = Kernel::Strings::strip(hintstr);
378 g_log.debug() << "vector findRun(\'" << hint << "\', exts[" << extensionsProvided.size() << "])\n";
379
380 // if partial filename or run number is not supplied, return here
381 if (hint.empty())
382 return API::Result<std::string>("", "File not found.");
383
384 // if it looks like a full filename just do a quick search for it
385 std::filesystem::path hintPath(hint);
386 if (hintPath.has_extension()) {
387 // check in normal search locations
388 g_log.debug() << "hintPath is not empty, check in normal search locations"
389 << "\n";
390 std::string path = getFullPath(hint);
391 if (!path.empty()) {
392 try {
393 if (std::filesystem::exists(path)) {
394 g_log.information() << "found path = " << path << '\n';
395 return API::Result<std::string>(path);
396 }
397 } catch (const std::exception &) {
398 }
399 } else {
400 g_log.debug() << "Unable to find files via directory search with the "
401 "filename that looks like a full filename"
402 << "\n";
403 }
404 }
405
406 // get instrument and facility
407 const Kernel::InstrumentInfo instrument = this->getInstrument(hint);
408 const Kernel::FacilityInfo &facility = instrument.facility();
409 // get facility extensions
410 const std::vector<std::string> facilityExtensions = facility.extensions();
411
412 // Do we need to try and form a filename from our preset rules
413 std::string filename(hint);
414 std::string extension = getExtension(hint, facilityExtensions);
415 if (!facilityExtensions.empty())
416 filename = hint.substr(0, hint.rfind(extension));
417 if (hintPath.parent_path().empty()) {
418 try {
419 if (!facility.noFilePrefix()) {
420 filename = makeFileName(filename, instrument);
421 }
422 } catch (std::invalid_argument &) {
423 if (filename.length() >= hint.length()) {
424 g_log.information() << "Could not form filename from standard rules '" << filename << "'\n";
425 }
426 }
427 }
428
429 if (filename.empty())
430 return API::Result<std::string>("", "File not found");
431
432 // Look first at the original filename then for case variations. This is
433 // important
434 // on platforms where file names ARE case sensitive.
435 // Sorry for the duplication, a last minute fix was required. Ticket #6419 is
436 // tasked with a redesign of
437 // the whole file finding concept.
438
439 std::set<std::string> filenames;
440 filenames.insert(filename);
441 if (!getCaseSensitive()) {
442 std::string transformed(filename);
443 std::transform(filename.begin(), filename.end(), transformed.begin(), toupper);
444 filenames.insert(transformed);
445 std::transform(filename.begin(), filename.end(), transformed.begin(), tolower);
446 filenames.insert(transformed);
447 }
448
449 // Merge the extensions & throw out duplicates
450 // On Windows throw out ones that only vary in case
451 std::vector<std::string> extensionsToSearch;
452 extensionsToSearch.reserve(1 + extensionsProvided.size() + facilityExtensions.size());
453
454 if (useOnlyExtensionsProvided) {
455 getUniqueExtensions(extensionsProvided, extensionsToSearch);
456
457 } else {
458 if (!extension.empty()) {
459 extensionsToSearch.emplace_back(extension);
460
461 } else {
462 getUniqueExtensions(extensionsProvided, extensionsToSearch);
463 getUniqueExtensions(facilityExtensions, extensionsToSearch);
464 }
465 }
466
467 // determine which archive search facilities to use
468 std::vector<IArchiveSearch_sptr> archs = getArchiveSearch(facility);
469
470 auto path = getPath(archs, filenames, extensionsToSearch);
471 if (path) {
472 g_log.information() << "Found path = " << path << '\n';
473 return path;
474 }
475 // Path not found
476
477 // If only looked for extension in filename
478 if (!useOnlyExtensionsProvided && extensionsToSearch.size() == 1) {
479
480 extensionsToSearch.pop_back(); // No need to search for missing extension again
481 getUniqueExtensions(extensionsProvided, extensionsToSearch);
482 getUniqueExtensions(facilityExtensions, extensionsToSearch);
483
484 g_log.warning() << "Extension ['" << extension << "'] not found.\n";
485 g_log.warning() << "Searching for other facility extensions." << std::endl;
486
487 path = getPath(archs, filenames, extensionsToSearch);
488 if (path) {
489 g_log.information() << "Found path = " << path << '\n';
490 return path;
491 }
492 }
493 g_log.information() << "Unable to find run with hint " << hint << "\n";
494 return API::Result<std::string>("", path.errors());
495}
496
505void FileFinderImpl::getUniqueExtensions(const std::vector<std::string> &extensionsToAdd,
506 std::vector<std::string> &uniqueExts) const {
507 const bool isCaseSensitive = getCaseSensitive();
508 for (const auto &cit : extensionsToAdd) {
509 std::string transformed(cit);
510 if (!isCaseSensitive) {
511 std::transform(cit.begin(), cit.end(), transformed.begin(), tolower);
512 }
513 const auto searchItr = std::find(uniqueExts.begin(), uniqueExts.end(), transformed);
514 if (searchItr == uniqueExts.end()) {
515 uniqueExts.emplace_back(transformed);
516 }
517 }
518}
519
526std::string FileFinderImpl::validateRuns(const std::string &searchText) const {
527 if (!isASCII(searchText))
528 return "An unsupported non-ASCII character was found in the search text.";
529 return "";
530}
531
547std::vector<std::string> FileFinderImpl::findRuns(const std::string &hintstr,
548 const std::vector<std::string> &extensionsProvided,
549 const bool useOnlyExtensionsProvided) const {
550 auto const error = validateRuns(hintstr);
551 if (!error.empty())
552 throw std::invalid_argument(error);
553
554 std::string hint = Kernel::Strings::strip(hintstr);
555 g_log.debug() << "findRuns hint = " << hint << "\n";
556 std::vector<std::string> res;
559 static const boost::regex digits("[0-9]+");
560 auto h = hints.begin();
561
562 std::string instrSName;
563 for (; h != hints.end(); ++h) {
564 // Quick check for a filename
565 bool fileSuspected = false;
566 // Assume if the hint contains either a "/" or "\" it is a filename..
567 if ((*h).find("\\") != std::string::npos) {
568 fileSuspected = true;
569 }
570 if ((*h).find("/") != std::string::npos) {
571 fileSuspected = true;
572 }
573 if ((*h).find(ALLOWED_SUFFIX) != std::string::npos) {
574 fileSuspected = true;
575 }
576
579 if ((range.count() > 2) && (!fileSuspected)) {
580 throw std::invalid_argument("Malformed range of runs: " + *h);
581 } else if ((range.count() == 2) && (!fileSuspected)) {
582 std::pair<std::string, std::string> p1 = toInstrumentAndNumber(range[0]);
583 if (boost::algorithm::istarts_with(hint, "PG3")) {
584 instrSName = "PG3";
585 }
586 std::string run = p1.second;
587 size_t nZero = run.size(); // zero padding
588 if (range[1].size() > nZero) {
589 throw std::invalid_argument("Malformed range of runs: " + *h +
590 ". The end of string value is longer than "
591 "the instrument's zero padding");
592 }
593 auto runNumber = boost::lexical_cast<int>(run);
594 std::string runEnd = run;
595 // Adds zero padding to end of range.
596 runEnd.replace(runEnd.end() - range[1].size(), runEnd.end(), range[1]);
597
598 // Throw if runEnd contains something else other than a digit.
599 if (!boost::regex_match(runEnd, digits))
600 throw std::invalid_argument("Malformed range of runs: Part of the run "
601 "has a non-digit character in it.");
602
603 auto runEndNumber = boost::lexical_cast<int>(runEnd);
604 if (runEndNumber < runNumber) {
605 throw std::invalid_argument("Malformed range of runs: " + *h);
606 }
607 std::string previousPath, previousExt;
608 for (int irun = runNumber; irun <= runEndNumber; ++irun) {
609 run = std::to_string(irun);
610 while (run.size() < nZero)
611 run.insert(0, "0");
612
613 // Quick check if file can be created from previous successfully found
614 // path/extension
615 if (!previousPath.empty() && !previousExt.empty()) {
616 try {
617 const std::filesystem::path file(previousPath + p1.first + run + previousExt);
618 if (std::filesystem::exists(file)) {
619 res.emplace_back(file.string());
620 continue;
621 }
622 } catch (...) {
623 // Clear cached path and extension
624 previousPath = previousExt = "";
625 }
626 }
627
628 std::string path;
629 if (boost::algorithm::istarts_with(hint, "PG3")) {
630 path = findRun(instrSName + run, extensionsProvided, useOnlyExtensionsProvided).result();
631 } else {
632 path = findRun(p1.first + run, extensionsProvided, useOnlyExtensionsProvided).result();
633 }
634
635 if (!path.empty()) {
636 // Cache successfully found path and extension
637 std::filesystem::path tempPath(path);
638 previousExt = tempPath.extension().string();
639 // Append separator for string concatenation later
640 previousPath = tempPath.parent_path().string() + std::string(1, std::filesystem::path::preferred_separator);
641 res.emplace_back(path);
642 } else {
643 throw Kernel::Exception::NotFoundError("Unable to find file:", std::move(run));
644 }
645 }
646 } else {
647 std::string path;
648 // Special check for "PG3", to cope with situation like '48314,48316'.
649 if (boost::algorithm::istarts_with(hint, "PG3")) {
650 if (h == hints.begin()) {
651 instrSName = "PG3";
652 path = findRun(*h, extensionsProvided, useOnlyExtensionsProvided).result();
653 } else {
654 path = findRun(instrSName + *h, extensionsProvided, useOnlyExtensionsProvided).result();
655 }
656 } else {
657 path = findRun(*h, extensionsProvided, useOnlyExtensionsProvided).result();
658 }
659 if (!path.empty()) {
660 res.emplace_back(path);
661 } else {
662 throw Kernel::Exception::NotFoundError("Unable to find file:", *h);
663 }
664 }
665 }
666
667 return res;
668}
669
671FileFinderImpl::getISISInstrumentDataCachePath(const std::string &cachePathToSearch,
672 const std::set<std::string> &filenames,
673 const std::vector<std::string> &exts) const {
674 std::string errors;
675 auto dataCache = API::ISISInstrumentDataCache(cachePathToSearch);
676
677 for (const auto &filename : filenames) {
678
679 std::string parentDirPath;
680
681 try {
682 parentDirPath = dataCache.getFileParentDirectoryPath(filename);
683
684 } catch (const std::invalid_argument &e) {
685 errors += "Data cache: " + std::string(e.what());
686 return API::Result<std::string>("", errors);
687
688 } catch (const Json::Exception &e) {
689 errors += "Data cache: Failed parsing to JSON: " + std::string(e.what()) +
690 "Error likely due to accessing instrument index file while it was being updated on IDAaaS.";
691 return API::Result<std::string>("", errors);
692 }
693
694 if (!std::filesystem::exists(parentDirPath)) {
695 errors += "Data cache: Directory not found: " + parentDirPath;
696 return API::Result<std::string>("", errors);
697 }
698
699 for (const auto &ext : exts) {
700 std::filesystem::path filePath(parentDirPath + '/' + filename + ext);
701
702 try { // Catches error for permission denied
703 if (std::filesystem::exists(filePath)) {
704 return API::Result<std::string>(filePath.string());
705 }
706 } catch (const std::filesystem::filesystem_error &e) {
707 errors += "Data cache: " + std::string(e.what());
708 return API::Result<std::string>("", errors);
709 }
710 }
711 errors += "Data cache: " + filename + " not found in " + parentDirPath;
712 }
713 return API::Result<std::string>("", errors);
714}
715
726const API::Result<std::string> FileFinderImpl::getArchivePath(const std::vector<IArchiveSearch_sptr> &archs,
727 const std::set<std::string> &filenames,
728 const std::vector<std::string> &exts) const {
729 g_log.debug() << "getArchivePath([IArchiveSearch_sptr], [ ";
730 for (const auto &iter : filenames)
731 g_log.debug() << iter << " ";
732 g_log.debug() << "], [ ";
733 for (const auto &iter : exts)
734 g_log.debug() << iter << " ";
735 g_log.debug() << "])\n";
736
737 string errors = "";
738 for (const auto &arch : archs) {
739 try {
740 g_log.debug() << "Getting archive path for requested files\n";
741 auto path = arch->getArchivePath(filenames, exts);
742 if (path)
743 return path;
744 else
745 errors += path.errors();
746 } catch (...) {
747 }
748 }
749 return API::Result<std::string>("", errors);
750}
751
763const API::Result<std::string> FileFinderImpl::getPath(const std::vector<IArchiveSearch_sptr> &archs,
764 const std::set<std::string> &filenames,
765 const std::vector<std::string> &exts) const {
766 std::string path;
767
768 std::vector<std::string> extensions;
769 extensions.assign(exts.begin(), exts.end());
770
771 // Remove wild cards.
772 extensions.erase(std::remove_if(extensions.begin(), extensions.end(), containsWildCard), extensions.end());
773
774 const std::vector<std::string> &searchPaths = Kernel::ConfigService::Instance().getDataSearchDirs();
775
776 // Before we try any globbing, make sure we exhaust all reasonable attempts at
777 // constructing the possible filename.
778 // Avoiding the globbing of getFullPath() for as long as possible will help
779 // performance when calling findRuns()
780 // with a large range of files, especially when searchPaths consists of
781 // folders containing a large number of runs.
782 for (const auto &extension : extensions) {
783 for (const auto &filename : filenames) {
784 for (const auto &searchPath : searchPaths) {
785 try {
786 const std::filesystem::path filePath = std::filesystem::path(searchPath) / (filename + extension);
787 if (std::filesystem::exists(filePath))
788 return API::Result<std::string>(filePath.string());
789
790 } catch (const std::exception &) { /* File does not exist, just carry on. */
791 }
792 }
793 }
794 }
795
796 for (const auto &extension : extensions) {
797 for (const auto &filename : filenames) {
798 path = getFullPath(filename + extension);
799 try {
800 if (!path.empty() && std::filesystem::exists(path)) {
801 g_log.debug() << "path returned from getFullPath() = " << path << '\n';
802 return API::Result<std::string>(path);
803 }
804 } catch (std::exception &e) {
805 g_log.error() << "Cannot open file " << path << ": " << e.what() << '\n';
806 return API::Result<std::string>("", "Cannot open file.");
807 }
808 }
809 }
810
811 // Search data cache
812 string errors;
813 std::filesystem::path cachePathToSearch(Kernel::ConfigService::Instance().getString("datacachesearch.directory"));
814 // Only expect to find path to data cache on IDAaaS
815 if (std::filesystem::exists(cachePathToSearch)) {
816
817 API::Result<std::string> cacheFilePath =
818 getISISInstrumentDataCachePath(cachePathToSearch.string(), filenames, exts);
819
820 if (cacheFilePath) {
821 return cacheFilePath;
822 }
823 errors += cacheFilePath.errors();
824
825 } else {
826 g_log.debug() << "Data cache directory not found, proceeding with the search." << std::endl;
827 errors += "Could not find data cache directory: " + cachePathToSearch.string() + '\n';
828 }
829
830 // Search the archive
831 if (!archs.empty()) {
832 g_log.debug() << "Search the archives\n";
833 const auto archivePath = getArchivePath(archs, filenames, exts);
834 if (archivePath) {
835 try {
836 if (std::filesystem::exists(archivePath.result()))
837 return archivePath;
838 } catch (std::exception &e) {
839 g_log.error() << "Cannot open file " << archivePath << ": " << e.what() << '\n';
840 return API::Result<std::string>("", "Cannot open file.");
841 }
842 } else
843 errors += archivePath.errors();
844
845 } // archs
846 return API::Result<std::string>("", errors);
847}
848
849std::string FileFinderImpl::toUpper(const std::string &src) const {
850 std::string result = src;
851 std::transform(result.begin(), result.end(), result.begin(), toupper);
852 return result;
853}
854
855} // namespace Mantid::API
double error
std::string validateRuns(const std::string &searchText) const
A method that returns error messages if the provided runs are invalid.
std::pair< std::string, std::string > toInstrumentAndNumber(const std::string &hint) const
Extracts the instrument name and run number from a hint.
std::string makeFileName(const std::string &hint, const Kernel::InstrumentInfo &instrument) const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
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.
const API::Result< std::string > findRun(const std::string &hintstr, const std::vector< std::string > &exts={}, const bool useExtsOnly=false) const
Find a path to a single file from a hint.
std::string getFullPath(const std::string &filename, const bool ignoreDirs=false) const
Return the full path to the file given its name.
int m_globOption
glob option - set to case sensitive or insensitive
Definition FileFinder.h:80
std::string extractAllowedSuffix(std::string &userString) const
Run numbers can be followed by an allowed string.
std::vector< std::string > 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.
static const std::string ALLOWED_SUFFIX
a string that is allowed at the end of any run number
Definition FileFinder.h:63
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
void setCaseSensitive(const bool cs)
Option to set if file finder should be case sensitive.
const API::Result< std::string > getPath(const std::vector< IArchiveSearch_sptr > &archs, const std::set< std::string > &filenames, const std::vector< std::string > &exts) const
Return the full path to the file given its name, checking local directories first.
const API::Result< std::string > getArchivePath(const std::vector< IArchiveSearch_sptr > &archs, const std::set< std::string > &filenames, const std::vector< std::string > &exts) const
Return the path to the file found in archive.
const API::Result< std::string > getISISInstrumentDataCachePath(const std::string &cachePathToSearch, const std::set< std::string > &filenames, const std::vector< std::string > &exts) const
const Kernel::InstrumentInfo getInstrument(const std::string &hint, const bool returnDefaultIfNotFound=true) const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
const std::string & errors() const
Definition Result.h:20
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.
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)