You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

283 lines
12 KiB

#include "Settings.h"
#include <cstring>
#include <cctype>
#include <mutex>
#include "src/exceptions/OptionParserException.h"
// Static Inits
storm::settings::Destroyer storm::settings::SettingsManager::destroyer;
/*!
* @brief Create new instance.
*
* Creates a new Settings instance and passes the arguments to the constructor of Settings.
*
* @param argc should be argc passed to main function
* @param argv should be argv passed to main function
* @param filename either NULL or name of config file
* @return The new instance of Settings.
*/
void storm::settings::SettingsManager::parse(int const argc, char const * const argv[]) {
storm::settings::SettingsManager* myInstance = storm::settings::SettingsManager::getInstance();
myInstance->parseCommandLine(argc, argv);
}
bool storm::settings::SettingsManager::hasAssignment(std::string const& option) {
return (option.find_first_of('=', 0) != std::string::npos);
}
storm::settings::stringPair_t storm::settings::SettingsManager::splitOptionString(std::string const& option) {
size_t splitLocation = option.find_first_of('=', 0);
if (splitLocation == std::string::npos) {
// Second half is empty
return std::make_pair(option, "");
} else if (splitLocation + 1 >= option.length()) {
// Remove the = character
return std::make_pair(option.substr(0, option.size() - 1), "");
}
return std::make_pair(option.substr(0, splitLocation), option.substr(splitLocation + 1, std::string::npos));
}
void storm::settings::SettingsManager::handleAssignment(std::string const& longOptionName, std::vector<std::string> arguments) {
std::string optionName = storm::utility::StringHelper::stringToLower(longOptionName);
Option* option = this->getPtrByLongName(optionName);
// Mark as Set
option->setHasOptionBeenSet();
uint_fast64_t givenArgsCount = arguments.size();
if (givenArgsCount > option->getArgumentCount()) {
LOG4CPLUS_ERROR(logger, "SettingsManager::handleAssignment: Unable to parse arguments for option \"" << longOptionName << "\": " << arguments.size() << " arguments given, but expected no more than " << option->getArgumentCount() << ".");
throw storm::exceptions::OptionParserException() << "Unable to parse arguments for option \"" << longOptionName << "\": " << arguments.size() << " arguments given, but expected no more than " << option->getArgumentCount() << ".";
}
for (uint_fast64_t i = 0; i < option->getArgumentCount(); ++i) {
if (i < givenArgsCount) {
storm::settings::fromStringAssignmentResult_t assignmentResult = option->getArgument(i).fromStringValue(arguments.at(i));
if (!assignmentResult.first) {
LOG4CPLUS_ERROR(logger, "SettingsManager::handleAssignment: Unable to parse arguments for option \"" << longOptionName << "\": argument " << option->getArgument(i).getArgumentName() << " rejected the given value \"" << arguments.at(i) << "\" with message:\r\n" << assignmentResult.second << ".");
throw storm::exceptions::OptionParserException() << "Unable to parse arguments for option \"" << longOptionName << "\": argument " << option->getArgument(i).getArgumentName() << " rejected the given value \"" << arguments.at(i) << "\" with message:\r\n" << assignmentResult.second << ".";
}
} else {
// There is no given value for this argument, only optional
if (!option->getArgument(i).getIsOptional()) {
LOG4CPLUS_ERROR(logger, "Settings::handleAssignment: Unable to parse arguments for option \"" << longOptionName << "\": " << arguments.size() << " arguments given, but more arguments were expected.");
throw storm::exceptions::OptionParserException() << "Unable to parse arguments for option \"" << longOptionName << "\": " << arguments.size() << " arguments given, but more arguments were expected.";
} else {
option->getArgument(i).setFromDefaultValue();
}
}
}
}
std::vector<std::string> storm::settings::SettingsManager::argvToStringArray(int const argc, char const * const argv[]) {
// Ignore argv[0], it contains the program path and name
std::vector<std::string> result;
for (int i = 1; i < argc; ++i) {
result.push_back(std::string(argv[i]));
}
return result;
}
bool storm::settings::SettingsManager::checkArgumentSyntaxForOption(std::string const& argvString) {
if (argvString.size() < 2) {
return false;
}
if ((argvString.at(0) != '-') || ((argvString.at(1) != '-') && !isalpha(argvString.at(1)))) {
return false;
}
for (size_t i = 2; i < argvString.size(); ++i) {
if (!isalpha(argvString.at(i))) {
return false;
}
}
return true;
}
std::vector<bool> storm::settings::SettingsManager::scanForOptions(std::vector<std::string> const& arguments) {
std::vector<bool> result;
result.reserve(arguments.size());
for (auto it = arguments.cbegin(); it != arguments.cend(); ++it) {
result.push_back(checkArgumentSyntaxForOption(*it));
}
return result;
}
void storm::settings::SettingsManager::parseCommandLine(int const argc, char const * const argv[]) {
LOG4CPLUS_DEBUG(logger, "Settings::parseCommandLine: Parsing " << argc << " arguments.");
std::vector<std::string> stringArgv = argvToStringArray(argc, argv);
std::vector<bool> optionPositions = scanForOptions(stringArgv);
bool optionActive = false;
std::string longOptionName;
std::vector<std::string> argCache;
for (size_t i = 0; i <= stringArgv.size(); ++i) {
if (i == stringArgv.size()) {
if (optionActive) {
this->handleAssignment(longOptionName, argCache);
argCache.clear();
}
break;
} else if (optionPositions.at(i)) {
if (optionActive) {
this->handleAssignment(longOptionName, argCache);
argCache.clear();
}
std::string const& nextOption = stringArgv.at(i);
if (nextOption.at(0) == '-' && nextOption.at(1) != '-') {
// Short Option
std::string nextShortOptionName = storm::utility::StringHelper::stringToLower(nextOption.substr(1, nextOption.size() - 1));
if (!this->containsShortName(nextShortOptionName)) {
LOG4CPLUS_ERROR(logger, "Settings::parseCommandLine: Unknown option name \"" << nextShortOptionName << "\".");
throw storm::exceptions::OptionParserException() << "Unknown option name \"" << nextShortOptionName << "\".";
} else {
longOptionName = this->getByShortName(nextShortOptionName).getLongName();
optionActive = true;
}
} else {
// Long Option
std::string nextLongOptionName = storm::utility::StringHelper::stringToLower(nextOption.substr(2, nextOption.size() - 2));
if (!this->containsLongName(nextLongOptionName)) {
LOG4CPLUS_ERROR(logger, "Settings::parseCommandLine: Unknown option name \"" << nextLongOptionName << "\".");
throw storm::exceptions::OptionParserException() << "Unknown option name \"" << nextLongOptionName << "\".";
} else {
longOptionName = this->getByLongName(nextLongOptionName).getLongName();
optionActive = true;
}
}
} else if (optionActive) {
// Next argument for an Option found
argCache.push_back(stringArgv.at(i));
} else {
// No Option active and this is no option.
LOG4CPLUS_ERROR(logger, "Settings::parseCommandLine: Found stray argument while parsing a given configuration, \"" << stringArgv.at(i) << "\" is neither a known option nor preceeded by an option.");
throw storm::exceptions::OptionParserException() << "Found stray argument while parsing a given configuration; \"" << stringArgv.at(i) << "\" is neither a known option nor preceeded by an option.";
}
}
for (auto it = this->options.cbegin(); it != this->options.cend(); ++it) {
if (!it->second.get()->getHasOptionBeenSet()) {
if (it->second.get()->getIsRequired()) {
LOG4CPLUS_ERROR(logger, "Settings::parseCommandLine: Missing required option \"" << it->second.get()->getLongName() << "\".");
throw storm::exceptions::OptionParserException() << "Missing required option \"" << it->second.get()->getLongName() << "\".";
} else {
// Set defaults on optional values
for (uint_fast64_t i = 0; i < it->second.get()->getArgumentCount(); ++i) {
if (it->second.get()->getArgument(i).getHasDefaultValue()) {
it->second.get()->getArgument(i).setFromDefaultValue();
}
}
}
}
}
}
storm::settings::SettingsManager* storm::settings::SettingsManager::getInstance() {
// Usually, this would require double-checked locking.
// But since C++11, this is the way to go:
static storm::settings::SettingsManager pInstance;
return &pInstance;
}
storm::settings::SettingsManager& storm::settings::SettingsManager::addOption(Option* option) {
// For automatic management of option's lifetime
std::shared_ptr<Option> optionPtr(option);
std::string lowerLongName = storm::utility::StringHelper::stringToLower(option->getLongName());
std::string lowerShortName = storm::utility::StringHelper::stringToLower(option->getShortName());
auto longNameIterator = this->options.find(lowerLongName);
auto shortNameIterator = this->shortNames.find(lowerShortName);
if (longNameIterator == this->options.end()) {
// Not found
if (!(shortNameIterator == this->shortNames.end())) {
// There exists an option which uses the same shortname
//LOG4CPLUS_ERROR(logger, "Settings::addOption: Error: The Option \"" << shortNameIterator->second << "\" from Module \"" << this->options.find(shortNameIterator->second)->second.get()->getModuleName() << "\" uses the same ShortName as the Option \"" << option->getLongName() << "\" from Module \"" << option->getModuleName() << "\"!");
throw storm::exceptions::OptionUnificationException() << "Error: The option \"" << shortNameIterator->second << "\" of module \"" << this->options.find(shortNameIterator->second)->second.get()->getModuleName() << "\" uses the same name as option \"" << option->getLongName() << "\" of module \"" << option->getModuleName() << "\".";
}
// Copy Shared_ptr
this->options.insert(std::make_pair(lowerLongName, std::shared_ptr<Option>(optionPtr)));
this->optionPointers.push_back(std::shared_ptr<Option>(optionPtr));
// Ignore Options with empty shortName
if (!lowerShortName.empty()) {
this->shortNames.insert(std::make_pair(lowerShortName, lowerLongName));
}
} else {
// This will fail if the shortNames are not identical, so no additional checks here.
longNameIterator->second.get()->unify(*option);
}
return *this;
}
std::string storm::settings::SettingsManager::getHelpText() const {
// Copy all option names into a vector and sort it
std::vector<std::string> optionNames;
optionNames.reserve(this->options.size());
size_t longNameMaxSize = 0;
size_t shortNameMaxSize = 0;
size_t argumentNameMaxSize = 0;
// Get the maximum size of the long and short Names and copy the long names for sorting
std::for_each(this->options.cbegin(), this->options.cend(), [&] (std::pair<std::string, std::shared_ptr<storm::settings::Option>> const& it) -> void {
longNameMaxSize = std::max(longNameMaxSize, it.first.size());
shortNameMaxSize = std::max(shortNameMaxSize, it.second.get()->getShortName().size());
optionNames.push_back(it.first);
std::for_each(it.second.get()->arguments.cbegin(), it.second.get()->arguments.cend(), [&] (std::shared_ptr<ArgumentBase> const& arg) -> void {
argumentNameMaxSize = std::max(argumentNameMaxSize, arg.get()->getArgumentName().size());
});
});
// Sort the long names
std::sort(optionNames.begin(), optionNames.end(), [] (std::string const& a, std::string const& b) -> bool { return a.compare(b) < 0; });
std::stringstream ss;
/*
Layout:
--longName -shortName Description
ArgumentName (ArgumentType) ArgumentDescription
*/
const std::string delimiter = " ";
for (auto it = optionNames.cbegin(); it != optionNames.cend(); ++it) {
Option const& o = this->getByLongName(*it);
std::string const& longName = o.getLongName();
ss << delimiter << "--" << longName;
// Fill up the remaining space after the long Name
for (uint_fast64_t i = longName.size(); i < longNameMaxSize; ++i) {
ss << " ";
}
std::string const& shortName = o.getShortName();
ss << delimiter << "-" << shortName;
// Fill up the remaining space after the short Name
for (uint_fast64_t i = shortName.size(); i < shortNameMaxSize; ++i) {
ss << " ";
}
ss << delimiter << o.getDescription() << std::endl;
for (auto i = 0; i < o.getArgumentCount(); ++i) {
ArgumentBase const& a = o.getArgument(i);
std::string const& argumentName = a.getArgumentName();
ss << delimiter << delimiter << delimiter << delimiter << "argument <" << (i+1) << "> - " << argumentName;
// Fill up the remaining space after the argument Name
for (uint_fast64_t i = argumentName.size(); i < argumentNameMaxSize; ++i) {
ss << " ";
}
ss << "(" << ArgumentTypeHelper::toString(a.getArgumentType()) << ")" << delimiter << a.getArgumentDescription() << std::endl;
}
}
return ss.str();
}