/** * Called by setValue in the case where a user has disabled multiple file *loading. * * @param propValue :: A string of the allowed format, indicating the user's *choice of files. * @return A string indicating the outcome of the attempt to set the property. *An empty string indicates success. */ std::string MultipleFileProperty::setValueAsSingleFile(const std::string &propValue) { // Use a slave FileProperty to do the job for us. FileProperty slaveFileProp("Slave", "", FileProperty::Load, m_exts, Direction::Input); std::string error = slaveFileProp.setValue(propValue); if (!error.empty()) return error; // Store. try { std::vector<std::vector<std::string>> result; toValue(slaveFileProp(), result, "", ""); PropertyWithValue<std::vector<std::vector<std::string>>>::operator=(result); } catch (std::invalid_argument &except) { g_log.debug() << "Could not set property " << name() << ": " << except.what(); return except.what(); } return ""; }
/** * Called by setValue in the case where a user has disabled multiple file *loading. * * @param propValue :: A string of the allowed format, indicating the user's *choice of files. * @return A string indicating the outcome of the attempt to set the property. *An empty string indicates success. */ std::string MultipleFileProperty::setValueAsSingleFile(const std::string &propValue) { // if value is unchanged use the cached version if ((propValue == m_oldPropValue) && (!m_oldFoundValue.empty())) { PropertyWithValue<std::vector<std::vector<std::string>>>::operator=( m_oldFoundValue); return SUCCESS; } // Use a slave FileProperty to do the job for us. FileProperty slaveFileProp("Slave", "", FileProperty::Load, m_exts, Direction::Input); std::string error = slaveFileProp.setValue(propValue); if (!error.empty()) return error; // Store. std::vector<std::vector<std::string>> foundFiles; try { toValue(slaveFileProp(), foundFiles, "", ""); PropertyWithValue<std::vector<std::vector<std::string>>>::operator=( foundFiles); } catch (std::invalid_argument &except) { g_log.debug() << "Could not set property " << name() << ": " << except.what(); return except.what(); } // cache the new version of things m_oldPropValue = propValue; m_oldFoundValue = std::move(foundFiles); return SUCCESS; }
/** * Called by setValue in the case where multiple file loading is enabled. * * NOTE: If multifile loading is enabled, then users make the concession that *they cannot use "," or "+" in * directory names; they are used as operators only. * * @param propValue :: A string of the allowed format, indicating the user's *choice of files. * @return A string indicating the outcome of the attempt to set the property. *An empty string indicates success. */ std::string MultipleFileProperty::setValueAsMultipleFiles(const std::string &propValue) { // Return error if there are any adjacent + or , operators. const std::string INVALID = "\\+\\+|,,|\\+,|,\\+"; boost::smatch invalid_substring; if (boost::regex_search(propValue.begin(), propValue.end(), invalid_substring, boost::regex(INVALID))) return "Unable to parse filename due to an empty token."; // Regular expressions that represent the allowed instances of + or , // operators. const std::string NUM_COMMA_ALPHA = "(?<=\\d)\\s*,\\s*(?=\\D)"; const std::string ALPHA_COMMA_ALPHA = "(?<=\\D)\\s*,\\s*(?=\\D)"; const std::string NUM_PLUS_ALPHA = "(?<=\\d)\\s*\\+\\s*(?=\\D)"; const std::string ALPHA_PLUS_ALPHA = "(?<=\\D)\\s*\\+\\s*(?=\\D)"; const std::string COMMA_OPERATORS = NUM_COMMA_ALPHA + "|" + ALPHA_COMMA_ALPHA; const std::string PLUS_OPERATORS = NUM_PLUS_ALPHA + "|" + ALPHA_PLUS_ALPHA; std::stringstream errorMsg; std::vector<std::vector<std::string>> fileNames; // Tokenise on allowed comma operators, and iterate over each token. boost::sregex_token_iterator end; boost::sregex_token_iterator commaToken(propValue.begin(), propValue.end(), boost::regex(COMMA_OPERATORS), -1); for (; commaToken != end; ++commaToken) { const std::string commaTokenString = commaToken->str(); // Tokenise on allowed plus operators. boost::sregex_token_iterator plusToken( commaTokenString.begin(), commaTokenString.end(), boost::regex(PLUS_OPERATORS, boost::regex_constants::perl), -1); std::vector<std::vector<std::string>> temp; // Put the tokens into a vector before iterating over it this time, // so we can see how many we have. std::vector<std::string> plusTokenStrings; for (; plusToken != end; ++plusToken) plusTokenStrings.push_back(plusToken->str()); for (auto plusTokenString = plusTokenStrings.begin(); plusTokenString != plusTokenStrings.end(); ++plusTokenString) { try { m_parser.parse(*plusTokenString); } catch (const std::range_error &re) { g_log.error(re.what()); throw; } catch (const std::runtime_error &re) { errorMsg << "Unable to parse run(s): \"" << re.what(); } std::vector<std::vector<std::string>> f = m_parser.fileNames(); // If there are no files, then we should keep this token as it was passed // to the property, // in its untampered form. This will enable us to deal with the case where // a user is trying to // load a single (and possibly existing) file within a token, but which // has unexpected zero // padding, or some other anomaly. if (VectorHelper::flattenVector(f).empty()) f.push_back(std::vector<std::string>(1, *plusTokenString)); if (plusTokenStrings.size() > 1) { // See [3] in header documentation. Basically, for reasons of // ambiguity, we cant add // together plusTokens if they contain a range of files. So throw on // any instances of this // when there is more than plusToken. if (f.size() > 1) return "Adding a range of files to another file(s) is not currently " "supported."; if (temp.empty()) temp.push_back(f[0]); else { for (auto parsedFile = f[0].begin(); parsedFile != f[0].end(); ++parsedFile) temp[0].push_back(*parsedFile); } } else { temp.insert(temp.end(), f.begin(), f.end()); } } fileNames.insert(fileNames.end(), temp.begin(), temp.end()); } std::vector<std::vector<std::string>> allUnresolvedFileNames = fileNames; std::vector<std::vector<std::string>> allFullFileNames; // First, find the default extension. Flatten all the unresolved filenames // first, to make this easier. std::vector<std::string> flattenedAllUnresolvedFileNames = VectorHelper::flattenVector(allUnresolvedFileNames); std::string defaultExt; auto unresolvedFileName = flattenedAllUnresolvedFileNames.begin(); for (; unresolvedFileName != flattenedAllUnresolvedFileNames.end(); ++unresolvedFileName) { try { // Check for an extension. Poco::Path path(*unresolvedFileName); if (!path.getExtension().empty()) { defaultExt = "." + path.getExtension(); break; } } catch (Poco::Exception &) { // Safe to ignore? Need a better understanding of the circumstances under // which // this throws. } } // Cycle through each vector of unresolvedFileNames in allUnresolvedFileNames. // Remember, each vector contains files that are to be added together. auto unresolvedFileNames = allUnresolvedFileNames.begin(); for (; unresolvedFileNames != allUnresolvedFileNames.end(); ++unresolvedFileNames) { // Check for the existance of wild cards. (Instead of iterating over all the // filenames just join them together // and search for "*" in the result.) if (std::string::npos != boost::algorithm::join(*unresolvedFileNames, "").find("*")) return "Searching for files by wildcards is not currently supported."; std::vector<std::string> fullFileNames; for (auto &unresolvedFileName : *unresolvedFileNames) { bool useDefaultExt; try { // Check for an extension. Poco::Path path(unresolvedFileName); useDefaultExt = path.getExtension().empty(); } catch (Poco::Exception &) { // Just shove the problematic filename straight into FileProperty and // see // if we have any luck. useDefaultExt = false; } std::string fullyResolvedFile; if (!useDefaultExt) { FileProperty slaveFileProp("Slave", "", FileProperty::Load, m_exts, Direction::Input); std::string error = slaveFileProp.setValue(unresolvedFileName); // If an error was returned then pass it along. if (!error.empty()) { throw std::runtime_error(error); } fullyResolvedFile = slaveFileProp(); } else { // If a default ext has been specified/found, then use it. if (!defaultExt.empty()) { fullyResolvedFile = FileFinder::Instance().findRun( unresolvedFileName, std::vector<std::string>(1, defaultExt)); } else { fullyResolvedFile = FileFinder::Instance().findRun(unresolvedFileName, m_exts); } if (fullyResolvedFile.empty()) throw std::runtime_error( "Unable to find file matching the string \"" + unresolvedFileName + "\", even after appending suggested file extensions."); } // Append the file name to result. fullFileNames.push_back(fullyResolvedFile); } allFullFileNames.push_back(fullFileNames); } // Now re-set the value using the full paths found. return PropertyWithValue<std::vector<std::vector<std::string>>>::setValue( toString(allFullFileNames)); }