Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/UserStringParser.h"
#include "MantidKernel/StringTokenizer.h"
namespace Mantid {
namespace Kernel {
/**This method parses a given string of numbers and returns a vector of vector
*of numbers.
*@param userString - the string to parse
*@returns a vector containing vectors of numbers.
*/
std::vector<std::vector<unsigned int>>
UserStringParser::parse(const std::string &userString) {
std::vector<std::vector<unsigned int>> numbers;
// first separate commas
std::vector<std::string> commaseparatedstrings;
if (userString.find(',') != std::string::npos) {
commaseparatedstrings = separateComma(userString);
}
if (!commaseparatedstrings.empty()) {
std::vector<std::string>::const_iterator citr;
for (citr = commaseparatedstrings.begin();
citr != commaseparatedstrings.end(); ++citr) {
parse((*citr), numbers);
} else {
parse(userString, numbers);
}
return numbers;
}
/**This method parses a given string of numbers and returns a vector of vector
*of numbers.
*@param userString - the input string to parse
*@param numbers- a vector containing vectors of numbers.
*/
void UserStringParser::parse(const std::string &userString,
std::vector<std::vector<unsigned int>> &numbers) {
// look for separators
std::string separators("-+:");
// if input contains no separator string
if (userString.find_first_of(separators) == std::string::npos) {
numbers.emplace_back(std::vector<unsigned int>(1, toUInt(userString)));
} else if (Contains(userString, '-')) {
std::vector<unsigned int> value = separateDelimiters(userString, "-:");
if (!value.empty()) {
numbers.emplace_back(value);
} else if (Contains(userString, '+')) {
std::vector<unsigned int> value = separateDelimiters(userString, "+");
if (!value.empty()) {
numbers.emplace_back(value);
} else if (Contains(userString, ':')) {
std::vector<std::vector<unsigned int>> colonseparated =
separateColon(userString);
std::vector<std::vector<unsigned int>>::const_iterator citr1;
for (citr1 = colonseparated.begin(); citr1 != colonseparated.end();
++citr1) {
numbers.emplace_back((*citr1));
/** This method checks input string contains character ch
* @param input - the input string
* @param ch - character ch to search
* @returns - true if the string contains character ch.
*/
bool UserStringParser::Contains(const std::string &input, char ch) {
std::string::size_type pos = input.find(ch);
return (pos != std::string::npos);
/**This method parses a given string of numbers into comma separated tokens.
*@param input - the string to parse
*@returns a vector containing comma separated tokens.
*/
std::vector<std::string>
UserStringParser::separateComma(const std::string &input) {
using tokenizer = Mantid::Kernel::StringTokenizer;
tokenizer tokens(input, ",", Mantid::Kernel::StringTokenizer::TOK_TRIM);
/**This method parses a given string of numbers into colon separated tokens.
*@param input - the string to parse
*@returns a vector of vector containing colon separated tokens.
*/
std::vector<std::vector<unsigned int>>
UserStringParser::separateColon(const std::string &input) {
unsigned int startNum = 0;
unsigned int endNum = 0;
unsigned int step = 1;
std::vector<std::vector<unsigned int>> separatedValues;
Tokenize(input, ":", startNum, endNum, step);
for (unsigned int num = startNum; num <= endNum; num += step) {
separatedValues.emplace_back(std::vector<unsigned int>(1, num));
/**This method parses a given string of numbers into tokens using the separator
*character symbol.
*@param input - the string to parse
*@param delimiters - the string used as separator
*@returns a vector of vector containing colon separated tokens.
*/
std::vector<unsigned int>
UserStringParser::separateDelimiters(const std::string &input,
const std::string &delimiters) {
unsigned int startNum = 0;
unsigned int endNum = 0;
unsigned int step = 1;
std::vector<unsigned int> separatedValues;
Tokenize(input, delimiters, startNum, endNum, step);
for (unsigned int num = startNum; num <= endNum; num += step) {
separatedValues.emplace_back(num);
}
return separatedValues;
}
/**This method parses a given string of numbers into tokens based on the given
*separator
*returns numbers corresponding to separated tokens and separators if any
*@param input - the string to parse
*@param delimiter - character to separate
*@param start - a number corresponding to the string before delimiter
*@param end - a number corresponding to the string after delimiter
*@param step - number used to increment from the start num generate the series
*of numbers .
*/
void UserStringParser::Tokenize(const std::string &input,
const std::string &delimiter,
unsigned int &start, unsigned int &end,
unsigned int &step) {
using tokenizer = Mantid::Kernel::StringTokenizer;
if (!isValidStepSeparator(input, tokens.asVector())) {
throw std::runtime_error("Non supported format found in the input string " +
input + " Step string should be preceded by :");
}
// convert the parsed string to number
convertToNumbers(input, tokens.asVector(), start, end, step);
/**This method checks the separator preceded by the step string is valid
* colon ':' is valid separator for step string
*@param input - input string to validate
*@param tokens - vector containing separated values.
*@returns true if the input is valid
*/
bool UserStringParser::isValidStepSeparator(
const std::string &input, const std::vector<std::string> &tokens) {
std::string step_separator;
if (tokens.size() == 3) {
std::string step = tokens[2];
std::string::size_type index = input.rfind(step);
if (index != std::string::npos) {
step_separator = input.substr(index - 1, 1);
// step values must be preceded by colon ':'
/**This method checks the input string is valid format
*@param input - input string to validate
*@param tokens - vector containing separated values.
*@param start - start number
*@param end - end number
*@param step - step used for incrementing
*/
void UserStringParser::convertToNumbers(const std::string &input,
const std::vector<std::string> &tokens,
unsigned int &start, unsigned int &end,
unsigned int &step) {
if (tokens.empty()) {
return;
}
try {
start = toUInt(tokens.at(0));
end = toUInt(tokens.at(1));
if (tokens.size() == 3) {
step = toUInt(tokens.at(2));
if (end < start) {
throw std::runtime_error(
"Invalid Input String: End number " + tokens.at(1) +
" can not be lower than start number" + tokens.at(0));
if (start + step > end) {
throw std::runtime_error(
"Invalid Input String: End number " + tokens.at(1) +
" can not be lower than the sum of start number " + tokens.at(0) +
" and step number" + tokens.at(2));
}
} catch (std::runtime_error &e) {
throw std::runtime_error(e.what());
} catch (std::out_of_range &) {
throw std::runtime_error("Error when parsing the input string " + input);
/**This method converts a string to unsigned int
*@param input - the string to parse
*@returns an unsigned number equivalent of the input
*/
unsigned int UserStringParser::toUInt(const std::string &input) {
try {
return boost::lexical_cast<unsigned int>(input);
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("Error when parsing the input string " + input);
}
}
} // namespace Kernel
} // namespace Mantid