22#include <Poco/Exception.h>
25#include <boost/lexical_cast.hpp>
26#include <boost/regex.hpp>
31#include <boost/algorithm/string.hpp>
45bool containsWildCard(
const std::string &ext) {
return std::string::npos != ext.find(
'*'); }
47bool isASCII(
const std::string &str) {
48 return !std::any_of(str.cbegin(), str.cend(), [](
char c) { return static_cast<unsigned char>(c) > 127; });
121 Poco::Path entry(userString);
122 std::string noExt(entry.getBaseName());
125 userString.replace(userString.size() - repNumChars, repNumChars,
"");
138 if ((!hint.empty()) && (!isdigit(hint[0]))) {
139 string instrName(hint);
140 Poco::Path path(instrName);
141 instrName = path.getFileName();
142 if ((instrName.find(
"PG3") == 0) || (instrName.find(
"pg3") == 0)) {
154 else if ((instrName.find(
"SANS2D") == 0) || (instrName.find(
"sans2d") == 0)) {
155 instrName =
"SANS2D";
159 const auto it = std::find_if(instrName.begin(), instrName.end(), isdigit);
160 const auto nChars = std::distance(instrName.begin(), it);
161 instrName = instrName.substr(0, nChars);
166 if (!instrName.empty()) {
167 const auto it = std::find_if(instrName.rbegin(), instrName.rend(), isalpha);
168 const auto nChars = std::distance(it, instrName.rend());
169 instrName = instrName.substr(0, nChars);
189 std::string instrPart;
192 if (isdigit(hint[0])) {
197 std::string::const_reverse_iterator it = std::find_if(hint.rbegin(), hint.rend(), std::not_fn(isdigit));
199 if (it == hint.rend() || it == hint.rbegin()) {
200 throw std::invalid_argument(
"Malformed hint to FileFinderImpl::makeFileName: " + hint);
202 std::string::size_type nChars = std::distance(it, hint.rend());
205 if (boost::algorithm::istarts_with(hint,
"PG3")) {
207 nChars = instrPart.length();
210 else if (boost::algorithm::istarts_with(hint,
"SANS2D")) {
211 instrPart =
"SANS2D";
212 nChars = instrPart.length();
214 instrPart = hint.substr(0, nChars);
217 runPart = hint.substr(nChars);
220 unsigned int irunPart(0);
222 irunPart = boost::lexical_cast<unsigned int>(runPart);
223 }
catch (boost::bad_lexical_cast &) {
224 std::ostringstream os;
225 os <<
"Cannot convert '" << runPart <<
"' to run number.";
226 throw std::invalid_argument(os.str());
231 std::string::size_type i = runPart.find_first_not_of(
'0');
233 while (runPart.size() < nZero)
234 runPart.insert(0,
"0");
235 if (runPart.size() > nZero && nZero != 0) {
236 throw std::invalid_argument(
"Run number does not match instrument's zero padding");
241 return std::make_pair(instrPart, runPart);
262 std::string filename(hint);
264 const std::string shortName = instrument.
shortName();
265 std::string delimiter = instrument.
delimiter();
268 if (filename.substr(0, shortName.size()) == shortName) {
269 filename = filename.substr(shortName.size());
270 if ((!delimiter.empty()) && (filename.substr(0, delimiter.size()) == delimiter))
271 filename = filename.substr(delimiter.size());
273 filename = shortName + filename;
279 if (!delimiter.empty()) {
280 filename += delimiter;
282 filename += p.second;
284 if (!suffix.empty()) {
301 g_log.
debug() <<
"getExtension(" << filename <<
", exts[" << exts.size() <<
"])\n";
304 for (
const auto &ext : exts) {
305 std::string extension =
toUpper(ext);
306 if (extension.rfind(
'*') == extension.size() - 1)
308 extension = extension.substr(0, extension.rfind(
'*'));
311 std::size_t found =
toUpper(filename).rfind(extension);
312 if (found != std::string::npos) {
313 g_log.
debug() <<
"matched extension \"" << extension <<
"\" based on \"" << ext <<
"\"\n";
314 return filename.substr(found);
318 g_log.
debug() <<
"Failed to find extension. Just using last \'.\'\n";
319 std::size_t pos = filename.find_last_of(
'.');
320 if (pos != std::string::npos) {
321 return filename.substr(pos);
329 std::vector<IArchiveSearch_sptr> archs;
333 std::transform(archiveOpt.begin(), archiveOpt.end(), archiveOpt.begin(), tolower);
337 if (archiveOpt.empty() || archiveOpt ==
"off" || facility.
archiveSearch().empty())
341 auto createArchiveSearch = bool(archiveOpt ==
"all");
345 if (!createArchiveSearch) {
346 std::string faciltyName = facility.
name();
347 std::transform(faciltyName.begin(), faciltyName.end(), faciltyName.begin(), tolower);
348 if (archiveOpt ==
"on") {
350 std::transform(defaultFacility.begin(), defaultFacility.end(), defaultFacility.begin(), tolower);
351 createArchiveSearch = bool(faciltyName == defaultFacility);
353 createArchiveSearch = bool(archiveOpt.find(faciltyName) != std::string::npos);
358 if (createArchiveSearch) {
360 g_log.
debug() <<
"get archive search for the facility..." << facilityname <<
"\n";
368 const bool useExtsOnly)
const {
370 g_log.
debug() <<
"vector findRun(\'" << hint <<
"\', exts[" << exts.size() <<
"])\n";
377 Poco::Path hintPath(hint);
378 if (!hintPath.getExtension().empty()) {
380 g_log.
debug() <<
"hintPath is not empty, check in normal search locations"
385 if (Poco::File(path).
exists()) {
389 }
catch (Poco::Exception &) {
392 g_log.
debug() <<
"Unable to find files via directory search with the "
393 "filename that looks like a full filename"
402 const std::vector<std::string> facility_extensions = facility.
extensions();
404 std::vector<std::string> extensions;
406 g_log.
debug() <<
"Add facility extensions defined in the Facility.xml file"
408 extensions.assign(facility_extensions.begin(), facility_extensions.end());
411 std::string filename(hint);
413 if (!extensions.empty())
414 filename = hint.substr(0, hint.rfind(extension));
415 if (hintPath.depth() == 0) {
420 }
catch (std::invalid_argument &) {
421 if (filename.length() >= hint.length()) {
422 g_log.
information() <<
"Could not form filename from standard rules '" << filename <<
"'\n";
427 if (filename.empty())
437 std::set<std::string> filenames;
438 filenames.insert(filename);
440 std::string transformed(filename);
441 std::transform(filename.begin(), filename.end(), transformed.begin(), toupper);
442 filenames.insert(transformed);
443 std::transform(filename.begin(), filename.end(), transformed.begin(), tolower);
444 filenames.insert(transformed);
449 std::vector<std::string> uniqueExts;
450 uniqueExts.reserve(1 + exts.size() + extensions.size());
451 if (!extension.empty())
452 uniqueExts.emplace_back(extension);
457 if (exts.empty() || !useExtsOnly) {
464 std::string path =
getPath(archs, filenames, uniqueExts);
486 std::vector<std::string> &uniqueExts)
const {
488 for (
const auto &cit : extensionsToAdd) {
489 std::string transformed(cit);
490 if (!isCaseSensitive) {
491 std::transform(cit.begin(), cit.end(), transformed.begin(), tolower);
493 const auto searchItr = std::find(uniqueExts.begin(), uniqueExts.end(), transformed);
494 if (searchItr == uniqueExts.end()) {
495 uniqueExts.emplace_back(transformed);
507 if (!isASCII(searchText))
508 return "An unsupported non-ASCII character was found in the search text.";
528 const bool useExtsOnly)
const {
531 throw std::invalid_argument(
error);
534 g_log.
debug() <<
"findRuns hint = " << hint <<
"\n";
535 std::vector<std::string> res;
538 static const boost::regex digits(
"[0-9]+");
539 auto h = hints.
begin();
541 std::string instrSName;
542 for (; h != hints.
end(); ++h) {
544 bool fileSuspected =
false;
546 if ((*h).find(
"\\") != std::string::npos) {
547 fileSuspected =
true;
549 if ((*h).find(
"/") != std::string::npos) {
550 fileSuspected =
true;
553 fileSuspected =
true;
558 if ((range.
count() > 2) && (!fileSuspected)) {
559 throw std::invalid_argument(
"Malformed range of runs: " + *h);
560 }
else if ((range.
count() == 2) && (!fileSuspected)) {
562 if (boost::algorithm::istarts_with(hint,
"PG3")) {
565 std::string run = p1.second;
566 size_t nZero = run.size();
567 if (range[1].size() > nZero) {
568 throw std::invalid_argument(
"Malformed range of runs: " + *h +
569 ". The end of string value is longer than "
570 "the instrument's zero padding");
572 auto runNumber = boost::lexical_cast<int>(run);
573 std::string runEnd = run;
575 runEnd.replace(runEnd.end() - range[1].
size(), runEnd.end(), range[1]);
578 if (!boost::regex_match(runEnd, digits))
579 throw std::invalid_argument(
"Malformed range of runs: Part of the run "
580 "has a non-digit character in it.");
582 auto runEndNumber = boost::lexical_cast<int>(runEnd);
583 if (runEndNumber < runNumber) {
584 throw std::invalid_argument(
"Malformed range of runs: " + *h);
586 std::string previousPath, previousExt;
587 for (
int irun = runNumber; irun <= runEndNumber; ++irun) {
589 while (run.size() < nZero)
594 if (!previousPath.empty() && !previousExt.empty()) {
596 const Poco::File file(previousPath + p1.first + run + previousExt);
598 res.emplace_back(file.path());
603 previousPath = previousExt =
"";
608 if (boost::algorithm::istarts_with(hint,
"PG3")) {
609 path =
findRun(instrSName + run, exts, useExtsOnly);
611 path =
findRun(p1.first + run, exts, useExtsOnly);
616 auto tempPath = Poco::Path(path);
617 previousExt =
"." + tempPath.getExtension();
618 previousPath = tempPath.makeParent().toString();
619 res.emplace_back(path);
627 if (boost::algorithm::istarts_with(hint,
"PG3")) {
628 if (h == hints.
begin()) {
630 path =
findRun(*h, exts, useExtsOnly);
632 path =
findRun(instrSName + *h, exts, useExtsOnly);
635 path =
findRun(*h, exts, useExtsOnly);
638 res.emplace_back(path);
659 const std::set<std::string> &filenames,
660 const std::vector<std::string> &exts)
const {
661 g_log.
debug() <<
"getArchivePath([IArchiveSearch_sptr], [ ";
662 for (
const auto &iter : filenames)
665 for (
const auto &iter : exts)
670 for (
const auto &arch : archs) {
672 g_log.
debug() <<
"Getting archive path for requested files\n";
673 path = arch->getArchivePath(filenames, exts);
695 const std::set<std::string> &filenames,
696 const std::vector<std::string> &exts)
const {
699 std::vector<std::string> extensions;
700 extensions.assign(exts.begin(), exts.end());
703 extensions.erase(std::remove_if(extensions.begin(), extensions.end(), containsWildCard), extensions.end());
713 for (
auto &extension : extensions) {
714 for (
const auto &filename : filenames) {
715 for (
const auto &searchPath : searchPaths) {
717 const Poco::Path filePath(searchPath, filename + extension);
718 const Poco::File file(filePath);
720 return filePath.toString();
722 }
catch (Poco::Exception &) {
728 for (
const auto &extension : extensions) {
729 for (
const auto &filename : filenames) {
732 if (!path.empty() && Poco::File(path).exists()) {
733 g_log.
debug() <<
"path returned from getFullPath() = " << path <<
'\n';
736 }
catch (std::exception &e) {
737 g_log.
error() <<
"Cannot open file " << path <<
": " << e.what() <<
'\n';
744 if (!archs.empty()) {
746 const std::string archivePath =
getArchivePath(archs, filenames, exts);
748 if (!archivePath.empty() && Poco::File(archivePath).exists()) {
751 }
catch (std::exception &e) {
752 g_log.
error() <<
"Cannot open file " << archivePath <<
": " << e.what() <<
'\n';
762 std::string result = src;
763 std::transform(result.begin(), result.end(), result.begin(), toupper);
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.
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.
std::string findRun(const std::string &hintstr, const std::vector< std::string > &exts={}, const bool useExtsOnly=false) const
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
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.
std::vector< IArchiveSearch_sptr > getArchiveSearch(const Kernel::FacilityInfo &facility) const
static const std::string ALLOWED_SUFFIX
a string that is allowed at the end of any run number
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.
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 Kernel::InstrumentInfo getInstrument(const std::string &hint) const
DO NOT USE! MADE PUBLIC FOR TESTING ONLY.
void setCaseSensitive(const bool cs)
Option to set if file finder should be case sensitive.
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.
Exception for when an item is not found in a collection.
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 > extensions() const
Returns a list of file extensions.
const std::vector< std::string > & archiveSearch() const
Return the archive search interface names.
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.
A class that holds information about an 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.
int zeroPadding(unsigned int runNumber) const
Returns zero padding for this instrument and a run number.
std::string delimiter() const
Returns the default delimiter between instrument name and run number.
const std::string shortName() const
Return the short name of the instrument.
The Logger class is in charge of the publishing messages from the framework through various channels.
void debug(const std::string &msg)
Logs at debug level.
void error(const std::string &msg)
Logs at error level.
void information(const std::string &msg)
Logs at information level.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
std::string to_string(const wide_integer< Bits, Signed > &n)