3#include <boost/algorithm/string.hpp>
4#include <boost/io/ios_state.hpp>
51SettingsManager::SettingsManager() : modules(), longNameToOptions(), shortNameToOptions(), moduleOptions() {}
53SettingsManager::~SettingsManager() {
59 return settingsManager;
62void SettingsManager::setName(std::string
const& name, std::string
const& executableName) {
64 this->executableName = executableName;
67void SettingsManager::setFromCommandLine(
int const argc,
char const*
const argv[]) {
70 std::vector<std::string> argumentVector(argc - 1);
71 for (
int i = 1; i < argc; ++i) {
72 argumentVector[i - 1] = std::string(argv[i]);
75 this->setFromExplodedString(argumentVector);
78void SettingsManager::setFromString(std::string
const& commandLineString) {
79 if (commandLineString.empty()) {
80 this->setFromExplodedString({});
82 std::vector<std::string> argumentVector;
83 boost::split(argumentVector, commandLineString, boost::is_any_of(
"\t "));
84 this->setFromExplodedString(argumentVector);
88void SettingsManager::handleUnknownOption(std::string
const& optionName,
bool isShort)
const {
89 std::string optionNameWithDashes = (isShort ?
"-" :
"--") + optionName;
91 std::map<std::string, std::vector<std::string>> similarOptionNames;
92 for (
auto const& longOption : longNameToOptions) {
93 if (similarStrings.
add(
"--" + longOption.first)) {
94 similarOptionNames[
"--" + longOption.first].push_back(longOption.first);
97 for (
auto const& shortOption : shortNameToOptions) {
98 if (similarStrings.
add(
"-" + shortOption.first)) {
99 for (
auto const& option : shortOption.second) {
100 similarOptionNames[
"-" + shortOption.first].push_back(option->getLongName());
104 std::string errorMessage =
"Unknown option '" + optionNameWithDashes +
"'.";
105 if (!similarOptionNames.empty()) {
107 std::vector<std::string> sortedSimilarOptionNames;
108 auto similarStringsList = similarStrings.
toList();
109 for (
auto const& s : similarStringsList) {
110 for (
auto const& longOptionName : similarOptionNames.at(s)) {
111 sortedSimilarOptionNames.push_back(longOptionName);
114 errorMessage += getHelpForSelection({}, sortedSimilarOptionNames,
"",
"##### Suggested options:");
116 STORM_LOG_THROW(
false, storm::exceptions::OptionParserException, errorMessage);
119void SettingsManager::setFromExplodedString(std::vector<std::string>
const& commandLineArguments) {
121 bool optionActive =
false;
122 bool activeOptionIsShortName =
false;
123 std::string activeOptionName =
"";
124 std::vector<std::string> argumentCache;
127 for (uint_fast64_t i = 0; i < commandLineArguments.size(); ++i) {
128 std::string
const& currentArgument = commandLineArguments[i];
131 if (!currentArgument.empty() && currentArgument.at(0) ==
'-') {
135 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
138 argumentCache.clear();
143 if (currentArgument.at(1) ==
'-') {
146 std::string optionName = currentArgument.substr(2);
147 auto optionIterator = this->longNameToOptions.find(optionName);
148 if (optionIterator == this->longNameToOptions.end()) {
149 handleUnknownOption(optionName,
false);
151 activeOptionIsShortName =
false;
152 activeOptionName = optionName;
156 std::string optionName = currentArgument.substr(1);
157 auto optionIterator = this->shortNameToOptions.find(optionName);
158 if (optionIterator == this->shortNameToOptions.end()) {
159 handleUnknownOption(optionName,
true);
161 activeOptionIsShortName =
true;
162 activeOptionName = optionName;
164 }
else if (optionActive) {
166 argumentCache.push_back(currentArgument);
169 "Found stray argument '" << currentArgument <<
"' that is not preceeded by a matching option.");
175 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
185 this->finalizeAllModules();
188void SettingsManager::setFromConfigurationFile(std::string
const& configFilename) {
189 std::map<std::string, std::vector<std::string>> configurationFileSettings = parseConfigFile(configFilename);
191 for (
auto const& optionArgumentsPair : configurationFileSettings) {
192 auto options = this->longNameToOptions.find(optionArgumentsPair.first);
198 for (
auto option : options->second) {
199 if (option->getHasOptionBeenSet()) {
202 STORM_LOG_WARN(
"The option '" << option->getLongName() <<
"' of module '" << option->getModuleName()
203 <<
"' has been set in the configuration file '" << configFilename
204 <<
"', but was overwritten on the command line.\n");
208 setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second);
213 this->finalizeAllModules();
216void SettingsManager::printHelp(std::string
const& filter)
const {
217 STORM_PRINT(
"usage: " << executableName <<
" [options]\n\n");
219 if (filter ==
"frequent" || filter ==
"all") {
220 bool includeAdvanced = (filter ==
"all");
222 uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced);
224 std::vector<std::string> invisibleModules;
225 uint64_t numHidden = 0;
226 for (
auto const& moduleName : this->moduleNames) {
229 STORM_PRINT(getHelpForModule(moduleName, maxLength, includeAdvanced));
231 if (!includeAdvanced) {
232 auto moduleIterator = moduleOptions.find(moduleName);
233 if (moduleIterator != this->moduleOptions.end()) {
234 bool allAdvanced =
true;
235 for (
auto const& option : moduleIterator->second) {
236 if (!option->getIsAdvanced()) {
242 if (!moduleIterator->second.empty() && allAdvanced) {
243 invisibleModules.push_back(moduleName);
249 if (!includeAdvanced) {
250 if (numHidden == 1) {
255 if (!invisibleModules.empty()) {
256 if (invisibleModules.size() == 1) {
257 STORM_PRINT(invisibleModules.size() <<
" hidden module (" << boost::join(invisibleModules,
", ") <<
").\n");
259 STORM_PRINT(invisibleModules.size() <<
" hidden modules (" << boost::join(invisibleModules,
", ") <<
").\n");
262 STORM_PRINT(
"\nType '" + executableName +
" --help modulename' to display all options of a specific module.\n");
263 STORM_PRINT(
"Type '" + executableName +
" --help all' to display a complete list of options.\n");
267 std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase);
270 std::vector<std::string> matchingModuleNames;
271 for (
auto const& moduleName : this->moduleNames) {
272 if (std::regex_search(moduleName, hintRegex)) {
274 matchingModuleNames.push_back(moduleName);
280 std::vector<std::string> matchingOptionNames;
281 for (
auto const& optionName : this->longOptionNames) {
282 if (std::regex_search(optionName, hintRegex)) {
283 matchingOptionNames.push_back(optionName);
287 std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames,
288 "Matching modules for filter '" + filter +
"':",
"Matching options for filter '" + filter +
"':");
289 if (optionList.empty()) {
290 STORM_PRINT(
"Filter '" << filter <<
"' did not match any modules or options.\n");
297std::string SettingsManager::getHelpForSelection(std::vector<std::string>
const& selectedModuleNames, std::vector<std::string>
const& selectedLongOptionNames,
298 std::string modulesHeader, std::string optionsHeader)
const {
299 std::stringstream stream;
302 std::set<std::shared_ptr<Option>> printedOptions;
305 uint_fast64_t maxLengthModules = 0;
306 for (
auto const& moduleName : selectedModuleNames) {
307 maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName,
true));
309 auto optionIterator = this->moduleOptions.find(moduleName);
310 STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(),
"Unable to find selected module " << moduleName <<
".");
311 printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end());
315 std::vector<std::shared_ptr<Option>> matchingOptions;
316 uint_fast64_t maxLengthOptions = 0;
317 for (
auto const& optionName : selectedLongOptionNames) {
318 auto optionIterator = this->longNameToOptions.find(optionName);
319 STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(),
"Unable to find selected option " << optionName <<
".");
320 for (
auto const& option : optionIterator->second) {
323 if (printedOptions.find(option) == printedOptions.end()) {
324 maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength());
325 matchingOptions.push_back(option);
326 printedOptions.insert(option);
332 uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions);
333 if (selectedModuleNames.size() > 0) {
334 if (modulesHeader !=
"") {
335 stream << modulesHeader <<
'\n';
337 for (
auto const& matchingModuleName : selectedModuleNames) {
338 stream << getHelpForModule(matchingModuleName, maxLength,
true);
343 if (matchingOptions.size() > 0) {
344 if (optionsHeader !=
"") {
345 stream << optionsHeader <<
'\n';
347 for (
auto const& option : matchingOptions) {
348 stream << std::setw(maxLength) << std::left << *option <<
'\n';
354std::string SettingsManager::getHelpForModule(std::string
const& moduleName, uint_fast64_t maxLength,
bool includeAdvanced)
const {
355 auto moduleIterator = moduleOptions.find(moduleName);
356 if (moduleIterator == this->moduleOptions.end()) {
363 uint64_t numOfOptions = 0;
364 for (
auto const& option : moduleIterator->second) {
365 if (includeAdvanced || !option->getIsAdvanced()) {
370 std::stringstream stream;
371 if (numOfOptions > 0) {
372 std::string displayedModuleName =
"'" + moduleName +
"'";
373 if (!includeAdvanced) {
374 displayedModuleName +=
" (" + std::to_string(numOfOptions) +
"/" + std::to_string(moduleIterator->second.size()) +
" shown)";
376 stream <<
"##### Module " << displayedModuleName <<
" " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14),
'#') <<
'\n';
380 boost::io::ios_flags_saver out(std::cout);
382 for (
auto const& option : moduleIterator->second) {
383 if (includeAdvanced || !option->getIsAdvanced()) {
384 stream << std::setw(maxLength) << std::left << *option <<
'\n';
392uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(
bool includeAdvanced)
const {
393 uint_fast64_t length = 0;
394 for (
auto const& moduleName : this->moduleNames) {
395 length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length);
400uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string
const& moduleName,
bool includeAdvanced)
const {
401 auto moduleIterator = modules.find(moduleName);
402 STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException,
403 "Unable to retrieve option length of unknown module '" << moduleName <<
"'.");
404 return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced);
407void SettingsManager::addModule(std::unique_ptr<modules::ModuleSettings>&& moduleSettings,
bool doRegister) {
408 auto moduleIterator = this->modules.find(moduleSettings->getModuleName());
409 STORM_LOG_THROW(moduleIterator == this->modules.end(), storm::exceptions::IllegalFunctionCallException,
410 "Unable to register module '" << moduleSettings->getModuleName() <<
"' because a module with the same name already exists.");
413 std::string moduleName = moduleSettings->getModuleName();
414 this->moduleNames.push_back(moduleName);
415 this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings));
416 auto iterator = this->modules.find(moduleName);
417 std::unique_ptr<modules::ModuleSettings>
const& settings = iterator->second;
420 this->moduleOptions.emplace(moduleName, std::vector<std::shared_ptr<Option>>());
422 for (
auto const& option : settings->getOptions()) {
423 this->addOption(option);
428void SettingsManager::addOption(std::shared_ptr<Option>
const& option) {
430 auto moduleOptionIterator = this->moduleOptions.find(option->getModuleName());
431 STORM_LOG_THROW(moduleOptionIterator != this->moduleOptions.end(), storm::exceptions::IllegalFunctionCallException,
432 "Cannot add option for unknown module '" << option->getModuleName() <<
"'.");
433 moduleOptionIterator->second.emplace_back(option);
438 if (!option->getRequiresModulePrefix()) {
439 bool isCompatible = this->isCompatible(option, option->getLongName(), this->longNameToOptions);
440 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
441 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
442 addOptionToMap(option->getLongName(), option, this->longNameToOptions);
445 addOptionToMap(option->getModuleName() +
":" + option->getLongName(), option, this->longNameToOptions);
446 longOptionNames.push_back(option->getModuleName() +
":" + option->getLongName());
448 if (option->getHasShortName()) {
449 if (!option->getRequiresModulePrefix()) {
450 bool isCompatible = this->isCompatible(option, option->getShortName(), this->shortNameToOptions);
451 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
452 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
453 addOptionToMap(option->getShortName(), option, this->shortNameToOptions);
455 addOptionToMap(option->getModuleName() +
":" + option->getShortName(), option, this->shortNameToOptions);
459bool SettingsManager::hasModule(std::string
const& moduleName,
bool checkHidden)
const {
461 return this->moduleOptions.find(moduleName) != this->moduleOptions.end();
463 return this->modules.find(moduleName) != this->modules.end();
468 auto moduleIterator = this->modules.find(moduleName);
469 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
470 "Cannot retrieve unknown module '" << moduleName <<
"'.");
471 return *moduleIterator->second;
475 auto moduleIterator = this->modules.find(moduleName);
476 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
477 "Cannot retrieve unknown module '" << moduleName <<
"'.");
478 return *moduleIterator->second;
481bool SettingsManager::isCompatible(std::shared_ptr<Option>
const& option, std::string
const& optionName,
482 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap) {
483 auto optionIterator = optionMap.find(optionName);
484 if (optionIterator != optionMap.end()) {
485 for (
auto const& otherOption : optionIterator->second) {
486 bool locallyCompatible = option->isCompatibleWith(*otherOption);
487 if (!locallyCompatible) {
495void SettingsManager::setOptionArguments(std::string
const& optionName, std::shared_ptr<Option> option, std::vector<std::string>
const& argumentCache) {
496 STORM_LOG_THROW(argumentCache.size() <= option->getArgumentCount(), storm::exceptions::OptionParserException,
497 "Too many arguments for option '" << optionName <<
"'.");
498 STORM_LOG_THROW(!option->getHasOptionBeenSet(), storm::exceptions::OptionParserException,
"Option '" << optionName <<
"' is set multiple times.");
501 for (uint_fast64_t i = 0;
i < argumentCache.size(); ++
i) {
502 ArgumentBase& argument = option->getArgument(i);
503 bool conversionOk = argument.setFromStringValue(argumentCache[i]);
504 STORM_LOG_THROW(conversionOk, storm::exceptions::OptionParserException,
505 "Value '" << argumentCache[i] <<
"' is invalid for argument <" << argument.getName() <<
"> of option:\n"
510 for (uint_fast64_t i = argumentCache.size();
i < option->getArgumentCount(); ++
i) {
511 ArgumentBase& argument = option->getArgument(i);
512 STORM_LOG_THROW(argument.getIsOptional(), storm::exceptions::OptionParserException,
513 "Non-optional argument <" << argument.getName() <<
"> of option:\n"
515 argument.setFromDefaultValue();
518 option->setHasOptionBeenSet();
519 if (optionName != option->getLongName() && optionName != option->getShortName() && boost::starts_with(optionName, option->getModuleName())) {
520 option->setHasOptionBeenSetWithModulePrefix();
524void SettingsManager::setOptionsArguments(std::string
const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap,
525 std::vector<std::string>
const& argumentCache) {
526 auto optionIterator = optionMap.find(optionName);
527 STORM_LOG_THROW(optionIterator != optionMap.end(), storm::exceptions::OptionParserException,
"Unknown option '" << optionName <<
"'.");
530 for (
auto& option : optionIterator->second) {
531 setOptionArguments(optionName, option, argumentCache);
535void SettingsManager::addOptionToMap(std::string
const& name, std::shared_ptr<Option>
const& option,
536 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>& optionMap) {
537 auto optionIterator = optionMap.find(name);
538 if (optionIterator == optionMap.end()) {
539 std::vector<std::shared_ptr<Option>> optionVector;
540 optionVector.push_back(option);
541 optionMap.emplace(name, optionVector);
543 optionIterator->second.push_back(option);
547void SettingsManager::finalizeAllModules() {
548 for (
auto const& nameModulePair : this->modules) {
549 nameModulePair.second->finalize();
550 nameModulePair.second->check();
554std::map<std::string, std::vector<std::string>> SettingsManager::parseConfigFile(std::string
const& filename)
const {
555 std::map<std::string, std::vector<std::string>> result;
560 bool globalScope =
true;
561 std::string activeModule =
"";
562 uint_fast64_t lineNumber = 1;
566 if (line.at(0) ==
'[') {
568 line.at(0) ==
'[' && line.find(
"]") == line.length() - 1 && line.find(
"[", 1) == line.npos, storm::exceptions::OptionParserException,
569 "Illegal module name header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber)
570 <<
". Expected [<module>] where <module> is a placeholder for a known module.");
573 std::string moduleName = line.substr(1, line.length() - 2);
574 STORM_LOG_THROW(moduleName !=
"" && (moduleName ==
"global" || (this->modules.find(moduleName) != this->modules.end())),
575 storm::exceptions::OptionParserException,
576 "Module header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber) <<
" refers to unknown module '"
577 << moduleName <<
".");
580 if (moduleName ==
"global") {
583 activeModule = moduleName;
589 std::size_t assignmentSignIndex = line.find(
"=");
590 bool containsAssignment =
false;
591 if (assignmentSignIndex != line.npos) {
592 containsAssignment =
true;
595 std::string optionName;
596 if (containsAssignment) {
597 optionName = line.substr(0, assignmentSignIndex);
603 STORM_LOG_THROW(this->longNameToOptions.find(optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException,
604 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
605 << optionName <<
"'.");
607 STORM_LOG_THROW(this->longNameToOptions.find(activeModule +
":" + optionName) != this->longNameToOptions.end(),
608 storm::exceptions::OptionParserException,
609 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
610 << activeModule <<
":" << optionName <<
"'.");
613 std::string fullOptionName = (!globalScope ? activeModule +
":" :
"") + optionName;
614 STORM_LOG_WARN_COND(result.find(fullOptionName) == result.end(),
"Option '" << fullOptionName <<
"' is set in line " << lineNumber
615 <<
" of configuration file " << filename
616 <<
", but has been set before.");
620 if (containsAssignment) {
621 std::string assignedValues = line.substr(assignmentSignIndex + 1);
622 std::vector<std::string> argumentCache;
626 std::regex argumentRegex(
"\"(([^\\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])*)\"|(([^ \\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])+)");
627 boost::algorithm::trim_left(assignedValues);
629 while (!assignedValues.empty()) {
631 bool hasMatch = std::regex_search(assignedValues, match, argumentRegex);
635 hasMatch, storm::exceptions::OptionParserException,
636 "Parsing error in configuration file '" << filename <<
"' in line " << lineNumber <<
". Unexpected input '" << assignedValues <<
"'.");
639 std::string matchedArgument = std::string(match[0].first, match[0].second);
640 if (matchedArgument.at(0) ==
'"') {
641 matchedArgument = matchedArgument.substr(1, matchedArgument.length() - 2);
643 argumentCache.push_back(matchedArgument);
645 assignedValues = assignedValues.substr(match.length());
646 boost::algorithm::trim_left(assignedValues);
650 result.emplace(fullOptionName, argumentCache);
653 result.emplace(fullOptionName, std::vector<std::string>());
663 return SettingsManager::manager();
667 return SettingsManager::manager();
678void initializeAll(std::string
const& name, std::string
const& executableName) {
Provides the central API for the registration of command line options and parsing the options from th...
void setName(std::string const &name, std::string const &executableName)
Sets the name of the tool.
modules::ModuleSettings const & getModule(std::string const &moduleName) const
Retrieves the settings of the module with the given name.
This class represents the settings for the abstraction procedures.
static const std::string moduleName
static const std::string moduleName
This is the base class of the settings for a particular module.
bool add(std::string const &string)
Adds the given string to the set of similar strings (if it is similar)
std::string toDidYouMeanString() const
Returns a "Did you mean abc?" string.
std::vector< std::string > toList() const
Gets a list of all added strings that are similar to the reference string.
#define STORM_LOG_WARN(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_PRINT(message)
Define the macros that print information and optionally also log it.
std::basic_istream< CharT, Traits > & getline(std::basic_istream< CharT, Traits > &input, std::basic_string< CharT, Traits, Allocator > &str)
Overloaded getline function which handles different types of newline ( and \r).
void closeFile(std::ofstream &stream)
Close the given file after writing.
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
storm::settings::modules::BuildSettings & mutableBuildSettings()
Retrieves the build settings in a mutable form.
storm::settings::modules::AbstractionSettings & mutableAbstractionSettings()
Retrieves the abstraction settings in a mutable form.
bool hasModule()
Returns true if the given module is registered.
SettingsType const & getModule()
Get module.
void initializeAll(std::string const &name, std::string const &executableName)
Initialize the settings manager with all available modules.
SettingsManager const & manager()
Retrieves the settings manager.
SettingsManager & mutableManager()
Retrieves the settings manager.