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 <Poco/Exception.h>
24#include <Poco/File.h>
25#include <Poco/Path.h>
26#include <boost/lexical_cast.hpp>
27#include <boost/regex.hpp>
28
29#include <algorithm>
30#include <cctype>
31
32#include <boost/algorithm/string.hpp>
33
34#include <filesystem>
35#include <json/value.h>
36
37namespace {
39Mantid::Kernel::Logger g_log("FileFinder");
40
49bool containsWildCard(const std::string &ext) { return std::string::npos != ext.find('*'); }
50
51bool isASCII(const std::string &str) {
52 return !std::any_of(str.cbegin(), str.cend(), [](char c) { return static_cast<unsigned char>(c) > 127; });
53}
54
55} // namespace
56
57namespace Mantid::API {
58using std::string;
59
60// this allowed string could be made into an array of allowed, currently used
61// only by the ISIS SANS group
62const std::string FileFinderImpl::ALLOWED_SUFFIX = "-add";
63//----------------------------------------------------------------------
64// Public member functions
65//----------------------------------------------------------------------
70 // Make sure plugins are loaded
71 FrameworkManager::Instance().loadPlugins();
72
73// determine from Mantid property how sensitive Mantid should be
74#ifdef _WIN32
76#else
77 setCaseSensitive(Kernel::ConfigService::Instance().getValue<bool>("filefinder.casesensitive").value_or(false));
78#endif
79}
80
91
98
109std::string FileFinderImpl::getFullPath(const std::string &filename, const bool ignoreDirs) const {
110 return Kernel::ConfigService::Instance().getFullPath(filename, ignoreDirs, m_globOption);
111}
112
118std::string FileFinderImpl::extractAllowedSuffix(std::string &userString) const {
119 if (userString.find(ALLOWED_SUFFIX) == std::string::npos) {
120 // short cut processing as normally there is no suffix
121 return "";
122 }
123
124 // ignore any file extension in checking if a suffix is present
125 Poco::Path entry(userString);
126 std::string noExt(entry.getBaseName());
127 const size_t repNumChars = ALLOWED_SUFFIX.size();
128 if (noExt.find(ALLOWED_SUFFIX) == noExt.size() - repNumChars) {
129 userString.replace(userString.size() - repNumChars, repNumChars, "");
130 return ALLOWED_SUFFIX;
131 }
132 return "";
133}
134
143 const bool returnDefaultIfNotFound) const {
144 if ((!hint.empty()) && (!isdigit(hint[0]))) {
145 string instrName(hint);
146 Poco::Path path(instrName);
147 instrName = path.getFileName();
148 if (instrName.starts_with("PG3") || instrName.starts_with("pg3")) {
149 instrName = "PG3";
150 }
151 // We're extending this nasty hack to accomodate data archive searching for
152 // SANS2D.
153 // While this certainly shouldn't be considered good practice, #7515 exists
154 // to
155 // completely redesign FileFinder -- this quick fix will have to do until
156 // all this
157 // code gets an overhaul as part of that ticket. Please think twice before
158 // adding
159 // any more instruments to this list.
160 else if (instrName.starts_with("SANS2D") || instrName.starts_with("sans2d")) {
161 instrName = "SANS2D";
162 } else {
163 // go forwards looking for the run number to start
164 {
165 const auto it = std::find_if(instrName.begin(), instrName.end(), isdigit);
166 const auto nChars = std::distance(instrName.begin(), it);
167 instrName.resize(nChars);
168 }
169
170 // go backwards looking for the instrument name to end - gets around
171 // delimiters
172 if (!instrName.empty()) {
173 const auto it = std::find_if(instrName.rbegin(), instrName.rend(), isalpha);
174 const auto nChars = std::distance(it, instrName.rend());
175 instrName.resize(nChars);
176 }
177 }
178 try {
179 const Kernel::InstrumentInfo instrument = Kernel::ConfigService::Instance().getInstrument(instrName);
180 return instrument;
182 g_log.debug() << e.what() << "\n";
183 if (!returnDefaultIfNotFound) {
184 throw e;
185 }
186 }
187 }
188 return Kernel::ConfigService::Instance().getInstrument();
189}
190
196std::pair<std::string, std::string> FileFinderImpl::toInstrumentAndNumber(const std::string &hint) const {
197 g_log.debug() << "toInstrumentAndNumber(" << hint << ")\n";
198 std::string instrPart;
199 std::string runPart;
200
201 if (isdigit(hint[0])) {
202 instrPart = Kernel::ConfigService::Instance().getInstrument().shortName();
203 runPart = hint;
204 } else {
206 std::string::const_reverse_iterator it = std::find_if(hint.rbegin(), hint.rend(), std::not_fn(isdigit));
207 // No non-digit or all non-digits
208 if (it == hint.rend() || it == hint.rbegin()) {
209 throw std::invalid_argument("Malformed hint to FileFinderImpl::makeFileName: " + hint);
210 }
211 std::string::size_type nChars = std::distance(it, hint.rend());
212
213 // Add in special test for PG3
214 if (boost::algorithm::istarts_with(hint, "PG3")) {
215 instrPart = "PG3";
216 nChars = instrPart.length();
217 }
218 // Another nasty check for SANS2D. Will do until FileFinder redesign.
219 else if (boost::algorithm::istarts_with(hint, "SANS2D")) {
220 instrPart = "SANS2D";
221 nChars = instrPart.length();
222 } else {
223 instrPart = hint.substr(0, nChars);
224 }
225
226 runPart = hint.substr(nChars);
227 }
228
229 unsigned int irunPart(0);
230 try {
231 irunPart = boost::lexical_cast<unsigned int>(runPart);
232 } catch (boost::bad_lexical_cast &) {
233 std::ostringstream os;
234 os << "Cannot convert '" << runPart << "' to run number.";
235 throw std::invalid_argument(os.str());
236 }
237 Kernel::InstrumentInfo instr = Kernel::ConfigService::Instance().getInstrument(instrPart);
238 size_t nZero = instr.zeroPadding(irunPart);
239 // remove any leading zeros in case there are too many of them
240 std::string::size_type i = runPart.find_first_not_of('0');
241 runPart.erase(0, i);
242 while (runPart.size() < nZero)
243 runPart.insert(0, "0");
244 if (runPart.size() > nZero && nZero != 0) {
245 throw std::invalid_argument("Run number does not match instrument's zero padding");
246 }
247
248 instrPart = instr.filePrefix(irunPart);
249
250 return std::make_pair(instrPart, runPart);
251}
252
265std::string FileFinderImpl::makeFileName(const std::string &hint, const Kernel::InstrumentInfo &instrument) const {
266 if (hint.empty())
267 return "";
268
269 std::string filename(hint);
270 const std::string suffix = extractAllowedSuffix(filename);
271 const std::string shortName = instrument.shortName();
272 std::string delimiter = instrument.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);
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
377const API::Result<std::string> FileFinderImpl::findRun(const std::string &hintstr,
378 const std::vector<std::string> &extensionsProvided,
379 const bool useOnlyExtensionsProvided) const {
380 std::string hint = Kernel::Strings::strip(hintstr);
381 g_log.debug() << "vector findRun(\'" << hint << "\', exts[" << extensionsProvided.size() << "])\n";
382
383 // if partial filename or run number is not supplied, return here
384 if (hint.empty())
385 return API::Result<std::string>("", "File not found.");
386
387 // if it looks like a full filename just do a quick search for it
388 Poco::Path hintPath(hint);
389 if (!hintPath.getExtension().empty()) {
390 // check in normal search locations
391 g_log.debug() << "hintPath is not empty, check in normal search locations"
392 << "\n";
393 std::string path = getFullPath(hint);
394 if (!path.empty()) {
395 try {
396 if (Poco::File(path).exists()) {
397 g_log.information() << "found path = " << path << '\n';
398 return API::Result<std::string>(path);
399 }
400 } catch (Poco::Exception &) {
401 }
402 } else {
403 g_log.debug() << "Unable to find files via directory search with the "
404 "filename that looks like a full filename"
405 << "\n";
406 }
407 }
408
409 // get instrument and facility
410 const Kernel::InstrumentInfo instrument = this->getInstrument(hint);
411 const Kernel::FacilityInfo &facility = instrument.facility();
412 // get facility extensions
413 const std::vector<std::string> facilityExtensions = facility.extensions();
414
415 // Do we need to try and form a filename from our preset rules
416 std::string filename(hint);
417 std::string extension = getExtension(hint, facilityExtensions);
418 if (!facilityExtensions.empty())
419 filename = hint.substr(0, hint.rfind(extension));
420 if (hintPath.depth() == 0) {
421 try {
422 if (!facility.noFilePrefix()) {
423 filename = makeFileName(filename, instrument);
424 }
425 } catch (std::invalid_argument &) {
426 if (filename.length() >= hint.length()) {
427 g_log.information() << "Could not form filename from standard rules '" << filename << "'\n";
428 }
429 }
430 }
431
432 if (filename.empty())
433 return API::Result<std::string>("", "File not found");
434
435 // Look first at the original filename then for case variations. This is
436 // important
437 // on platforms where file names ARE case sensitive.
438 // Sorry for the duplication, a last minute fix was required. Ticket #6419 is
439 // tasked with a redesign of
440 // the whole file finding concept.
441
442 std::set<std::string> filenames;
443 filenames.insert(filename);
444 if (!getCaseSensitive()) {
445 std::string transformed(filename);
446 std::transform(filename.begin(), filename.end(), transformed.begin(), toupper);
447 filenames.insert(transformed);
448 std::transform(filename.begin(), filename.end(), transformed.begin(), tolower);
449 filenames.insert(transformed);
450 }
451
452 // Merge the extensions & throw out duplicates
453 // On Windows throw out ones that only vary in case
454 std::vector<std::string> extensionsToSearch;
455 extensionsToSearch.reserve(1 + extensionsProvided.size() + facilityExtensions.size());
456
457 if (useOnlyExtensionsProvided) {
458 getUniqueExtensions(extensionsProvided, extensionsToSearch);
459
460 } else {
461 if (!extension.empty()) {
462 extensionsToSearch.emplace_back(extension);
463
464 } else {
465 getUniqueExtensions(extensionsProvided, extensionsToSearch);
466 getUniqueExtensions(facilityExtensions, extensionsToSearch);
467 }
468 }
469
470 // determine which archive search facilities to use
471 std::vector<IArchiveSearch_sptr> archs = getArchiveSearch(facility);
472
473 auto path = getPath(archs, filenames, extensionsToSearch);
474 if (path) {
475 g_log.information() << "Found path = " << path << '\n';
476 return path;
477 }
478 // Path not found
479
480 // If only looked for extension in filename
481 if (!useOnlyExtensionsProvided && extensionsToSearch.size() == 1) {
482
483 extensionsToSearch.pop_back(); // No need to search for missing extension again
484 getUniqueExtensions(extensionsProvided, extensionsToSearch);
485 getUniqueExtensions(facilityExtensions, extensionsToSearch);
486
487 g_log.warning() << "Extension ['" << extension << "'] not found.\n";
488 g_log.warning() << "Searching for other facility extensions." << std::endl;
489
490 path = getPath(archs, filenames, extensionsToSearch);
491 if (path) {
492 g_log.information() << "Found path = " << path << '\n';
493 return path;
494 }
495 }
496 g_log.information() << "Unable to find run with hint " << hint << "\n";
497 return API::Result<std::string>("", path.errors());
498}
499
508void FileFinderImpl::getUniqueExtensions(const std::vector<std::string> &extensionsToAdd,
509 std::vector<std::string> &uniqueExts) const {
510 const bool isCaseSensitive = getCaseSensitive();
511 for (const auto &cit : extensionsToAdd) {
512 std::string transformed(cit);
513 if (!isCaseSensitive) {
514 std::transform(cit.begin(), cit.end(), transformed.begin(), tolower);
515 }
516 const auto searchItr = std::find(uniqueExts.begin(), uniqueExts.end(), transformed);
517 if (searchItr == uniqueExts.end()) {
518 uniqueExts.emplace_back(transformed);
519 }
520 }
521}
522
529std::string FileFinderImpl::validateRuns(const std::string &searchText) const {
530 if (!isASCII(searchText))
531 return "An unsupported non-ASCII character was found in the search text.";
532 return "";
533}
534
550std::vector<std::string> FileFinderImpl::findRuns(const std::string &hintstr,
551 const std::vector<std::string> &extensionsProvided,
552 const bool useOnlyExtensionsProvided) const {
553 auto const error = validateRuns(hintstr);
554 if (!error.empty())
555 throw std::invalid_argument(error);
556
557 std::string hint = Kernel::Strings::strip(hintstr);
558 g_log.debug() << "findRuns hint = " << hint << "\n";
559 std::vector<std::string> res;
562 static const boost::regex digits("[0-9]+");
563 auto h = hints.begin();
564
565 std::string instrSName;
566 for (; h != hints.end(); ++h) {
567 // Quick check for a filename
568 bool fileSuspected = false;
569 // Assume if the hint contains either a "/" or "\" it is a filename..
570 if ((*h).find("\\") != std::string::npos) {
571 fileSuspected = true;
572 }
573 if ((*h).find("/") != std::string::npos) {
574 fileSuspected = true;
575 }
576 if ((*h).find(ALLOWED_SUFFIX) != std::string::npos) {
577 fileSuspected = true;
578 }
579
582 if ((range.count() > 2) && (!fileSuspected)) {
583 throw std::invalid_argument("Malformed range of runs: " + *h);
584 } else if ((range.count() == 2) && (!fileSuspected)) {
585 std::pair<std::string, std::string> p1 = toInstrumentAndNumber(range[0]);
586 if (boost::algorithm::istarts_with(hint, "PG3")) {
587 instrSName = "PG3";
588 }
589 std::string run = p1.second;
590 size_t nZero = run.size(); // zero padding
591 if (range[1].size() > nZero) {
592 throw std::invalid_argument("Malformed range of runs: " + *h +
593 ". The end of string value is longer than "
594 "the instrument's zero padding");
595 }
596 auto runNumber = boost::lexical_cast<int>(run);
597 std::string runEnd = run;
598 // Adds zero padding to end of range.
599 runEnd.replace(runEnd.end() - range[1].size(), runEnd.end(), range[1]);
600
601 // Throw if runEnd contains something else other than a digit.
602 if (!boost::regex_match(runEnd, digits))
603 throw std::invalid_argument("Malformed range of runs: Part of the run "
604 "has a non-digit character in it.");
605
606 auto runEndNumber = boost::lexical_cast<int>(runEnd);
607 if (runEndNumber < runNumber) {
608 throw std::invalid_argument("Malformed range of runs: " + *h);
609 }
610 std::string previousPath, previousExt;
611 for (int irun = runNumber; irun <= runEndNumber; ++irun) {
612 run = std::to_string(irun);
613 while (run.size() < nZero)
614 run.insert(0, "0");
615
616 // Quick check if file can be created from previous successfully found
617 // path/extension
618 if (!previousPath.empty() && !previousExt.empty()) {
619 try {
620 const Poco::File file(previousPath + p1.first + run + previousExt);
621 if (file.exists()) {
622 res.emplace_back(file.path());
623 continue;
624 }
625 } catch (...) {
626 // Clear cached path and extension
627 previousPath = previousExt = "";
628 }
629 }
630
631 std::string path;
632 if (boost::algorithm::istarts_with(hint, "PG3")) {
633 path = findRun(instrSName + run, extensionsProvided, useOnlyExtensionsProvided).result();
634 } else {
635 path = findRun(p1.first + run, extensionsProvided, useOnlyExtensionsProvided).result();
636 }
637
638 if (!path.empty()) {
639 // Cache successfully found path and extension
640 auto tempPath = Poco::Path(path);
641 previousExt = "." + tempPath.getExtension();
642 previousPath = tempPath.makeParent().toString();
643 res.emplace_back(path);
644 } else {
645 throw Kernel::Exception::NotFoundError("Unable to find file:", std::move(run));
646 }
647 }
648 } else {
649 std::string path;
650 // Special check for "PG3", to cope with situation like '48314,48316'.
651 if (boost::algorithm::istarts_with(hint, "PG3")) {
652 if (h == hints.begin()) {
653 instrSName = "PG3";
654 path = findRun(*h, extensionsProvided, useOnlyExtensionsProvided).result();
655 } else {
656 path = findRun(instrSName + *h, extensionsProvided, useOnlyExtensionsProvided).result();
657 }
658 } else {
659 path = findRun(*h, extensionsProvided, useOnlyExtensionsProvided).result();
660 }
661 if (!path.empty()) {
662 res.emplace_back(path);
663 } else {
664 throw Kernel::Exception::NotFoundError("Unable to find file:", *h);
665 }
666 }
667 }
668
669 return res;
670}
671
673FileFinderImpl::getISISInstrumentDataCachePath(const std::string &cachePathToSearch,
674 const std::set<std::string> &filenames,
675 const std::vector<std::string> &exts) const {
676 std::string errors;
677 auto dataCache = API::ISISInstrumentDataCache(cachePathToSearch);
678
679 for (const auto &filename : filenames) {
680
681 std::string parentDirPath;
682
683 try {
684 parentDirPath = dataCache.getFileParentDirectoryPath(filename);
685
686 } catch (const std::invalid_argument &e) {
687 errors += "Data cache: " + std::string(e.what());
688 return API::Result<std::string>("", errors);
689
690 } catch (const Json::Exception &e) {
691 errors += "Data cache: Failed parsing to JSON: " + std::string(e.what()) +
692 "Error likely due to accessing instrument index file while it was being updated on IDAaaS.";
693 return API::Result<std::string>("", errors);
694 }
695
696 if (!std::filesystem::exists(parentDirPath)) {
697 errors += "Data cache: Directory not found: " + parentDirPath;
698 return API::Result<std::string>("", errors);
699 }
700
701 for (const auto &ext : exts) {
702 std::filesystem::path filePath(parentDirPath + '/' + filename + ext);
703
704 try { // Catches error for permission denied
705 if (std::filesystem::exists(filePath)) {
706 return API::Result<std::string>(filePath.string());
707 }
708 } catch (const std::filesystem::filesystem_error &e) {
709 errors += "Data cache: " + std::string(e.what());
710 return API::Result<std::string>("", errors);
711 }
712 }
713 errors += "Data cache: " + filename + " not found in " + parentDirPath;
714 }
715 return API::Result<std::string>("", errors);
716}
717
728const API::Result<std::string> FileFinderImpl::getArchivePath(const std::vector<IArchiveSearch_sptr> &archs,
729 const std::set<std::string> &filenames,
730 const std::vector<std::string> &exts) const {
731 g_log.debug() << "getArchivePath([IArchiveSearch_sptr], [ ";
732 for (const auto &iter : filenames)
733 g_log.debug() << iter << " ";
734 g_log.debug() << "], [ ";
735 for (const auto &iter : exts)
736 g_log.debug() << iter << " ";
737 g_log.debug() << "])\n";
738
739 string errors = "";
740 for (const auto &arch : archs) {
741 try {
742 g_log.debug() << "Getting archive path for requested files\n";
743 auto path = arch->getArchivePath(filenames, exts);
744 if (path)
745 return path;
746 else
747 errors += path.errors();
748 } catch (...) {
749 }
750 }
751 return API::Result<std::string>("", errors);
752}
753
765const API::Result<std::string> FileFinderImpl::getPath(const std::vector<IArchiveSearch_sptr> &archs,
766 const std::set<std::string> &filenames,
767 const std::vector<std::string> &exts) const {
768 std::string path;
769
770 std::vector<std::string> extensions;
771 extensions.assign(exts.begin(), exts.end());
772
773 // Remove wild cards.
774 extensions.erase(std::remove_if(extensions.begin(), extensions.end(), containsWildCard), extensions.end());
775
776 const std::vector<std::string> &searchPaths = Kernel::ConfigService::Instance().getDataSearchDirs();
777
778 // Before we try any globbing, make sure we exhaust all reasonable attempts at
779 // constructing the possible filename.
780 // Avoiding the globbing of getFullPath() for as long as possible will help
781 // performance when calling findRuns()
782 // with a large range of files, especially when searchPaths consists of
783 // folders containing a large number of runs.
784 for (const auto &extension : extensions) {
785 for (const auto &filename : filenames) {
786 for (const auto &searchPath : searchPaths) {
787 try {
788 const Poco::Path filePath(searchPath, filename + extension);
789 const Poco::File file(filePath);
790 if (file.exists())
791 return API::Result<std::string>(filePath.toString());
792
793 } catch (Poco::Exception &) { /* File does not exist, just carry on. */
794 }
795 }
796 }
797 }
798
799 for (const auto &extension : extensions) {
800 for (const auto &filename : filenames) {
801 path = getFullPath(filename + extension);
802 try {
803 if (!path.empty() && Poco::File(path).exists()) {
804 g_log.debug() << "path returned from getFullPath() = " << path << '\n';
805 return API::Result<std::string>(path);
806 }
807 } catch (std::exception &e) {
808 g_log.error() << "Cannot open file " << path << ": " << e.what() << '\n';
809 return API::Result<std::string>("", "Cannot open file.");
810 }
811 }
812 }
813
814 // Search data cache
815 string errors;
816 std::filesystem::path cachePathToSearch(Kernel::ConfigService::Instance().getString("datacachesearch.directory"));
817 // Only expect to find path to data cache on IDAaaS
818 if (std::filesystem::exists(cachePathToSearch)) {
819
820 API::Result<std::string> cacheFilePath =
821 getISISInstrumentDataCachePath(cachePathToSearch.string(), filenames, exts);
822
823 if (cacheFilePath) {
824 return cacheFilePath;
825 }
826 errors += cacheFilePath.errors();
827
828 } else {
829 g_log.debug() << "Data cache directory not found, proceeding with the search." << std::endl;
830 errors += "Could not find data cache directory: " + cachePathToSearch.string() + '\n';
831 }
832
833 // Search the archive
834 if (!archs.empty()) {
835 g_log.debug() << "Search the archives\n";
836 const auto archivePath = getArchivePath(archs, filenames, exts);
837 if (archivePath) {
838 try {
839 if (Poco::File(archivePath.result()).exists())
840 return archivePath;
841 } catch (std::exception &e) {
842 g_log.error() << "Cannot open file " << archivePath << ": " << e.what() << '\n';
843 return API::Result<std::string>("", "Cannot open file.");
844 }
845 } else
846 errors += archivePath.errors();
847
848 } // archs
849 return API::Result<std::string>("", errors);
850}
851
852std::string FileFinderImpl::toUpper(const std::string &src) const {
853 std::string result = src;
854 std::transform(result.begin(), result.end(), result.begin(), toupper);
855 return result;
856}
857
858} // 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")
bool exists(Nexus::File &file, const std::string &name)
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)