/* * Option.h * * Created on: 11.08.2013 * Author: Philipp Berger */ #ifndef STORM_SETTINGS_OPTION_H_ #define STORM_SETTINGS_OPTION_H_ #include #include #include #include #include #include #include #include #include "ArgumentType.h" #include "ArgumentBase.h" #include "Argument.h" #include "src/utility/StringHelper.h" #include "src/exceptions/IllegalArgumentException.h" #include "src/exceptions/OptionUnificationException.h" namespace storm { namespace settings { class Settings; class Option { public: friend class storm::settings::Settings; /* std::string longName; std::string shortName; std::string description; std::string moduleName; bool isRequired; bool hasBeenSet; std::vector> arguments; */ Option(std::string const& moduleName, std::string const& longOptionName, std::string const& shortOptionName, std::string const& optionDescription, bool isOptionRequired) : longName(longOptionName), shortName(shortOptionName), description(optionDescription), moduleName(moduleName), isRequired(isOptionRequired), hasBeenSet(false) { validateFields(); } Option(std::string const& moduleName, std::string const& longOptionName, std::string const& shortOptionName, std::string const& optionDescription, bool isOptionRequired, std::vector> const& optionArguments) : longName(longOptionName), shortName(shortOptionName), description(optionDescription), moduleName(moduleName), isRequired(isOptionRequired), hasBeenSet(false) { // Copy all Arguments this->arguments.reserve(optionArguments.size()); for (auto i = 0; i < optionArguments.size(); ++i) { // Clone gives a deep copy this->arguments.push_back(std::shared_ptr(optionArguments.at(i).get()->clone())); } isArgumentsVectorValid(this->arguments); validateFields(); } Option(Option const& other): longName(other.longName), shortName(other.shortName), description(other.description), moduleName(other.moduleName), isRequired(other.isRequired), hasBeenSet(other.hasBeenSet) { // Copy all Arguments this->arguments.reserve(other.arguments.size()); for (auto i = 0; i < other.arguments.size(); ++i) { // Clone gives a deep copy this->arguments.push_back(std::shared_ptr(other.arguments.at(i).get()->clone())); } isArgumentsVectorValid(this->arguments); validateFields(); } ~Option() { std::cout << "Destructing an Option." << std::endl; this->arguments.clear(); this->argumentNameMap.clear(); } Option* clone() const { return new Option(*this); } void unify(Option& other) { if (this->getLongName().compare(other.getLongName()) != 0) { // LOG throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Names are different (\"" << getLongName() << "\" vs. \"" << other.getLongName() << "\")!"; } if (this->getShortName().compare(other.getShortName()) != 0) { // LOG throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Shortnames are different (\"" << getShortName() << "\" vs. \"" << other.getShortName() << "\")!"; } if (this->getArgumentCount() != other.getArgumentCount()) { // LOG throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Argument Counts are different!"; } for(auto i = 0; i != this->arguments.size(); i++) { ArgumentBase* A = this->arguments.at(i).get(); ArgumentBase* B = other.arguments.at(i).get(); if (A->getArgumentType() != B->getArgumentType()) { // LOG throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Argument Types at Index " << i << " are different!"; } switch (A->getArgumentType()) { case ArgumentType::String: static_cast*>(A)->unify(*static_cast*>(B)); break; case ArgumentType::Integer: static_cast*>(A)->unify(*static_cast*>(B)); break; case ArgumentType::UnsignedInteger: static_cast*>(A)->unify(*static_cast*>(B)); break; case ArgumentType::Double: static_cast*>(A)->unify(*static_cast*>(B)); break; case ArgumentType::Boolean: static_cast*>(A)->unify(*static_cast*>(B)); break; default: // LOG throw storm::exceptions::InternalTypeErrorException() << "Error: Missing Case in ArgumentBuilder's switch/case Code."; } } if (this->getModuleName().compare(other.getModuleName()) != 0) { this->moduleName.append(", ").append(other.getModuleName()); } } uint_fast64_t getArgumentCount() const { return this->arguments.size(); } ArgumentBase& getArgument(uint_fast64_t argumentIndex) const { if (argumentIndex >= getArgumentCount()) { // LOG throw storm::exceptions::IllegalArgumentException() << "Error: Option::getArgument(): argumentIndex out of bounds!"; } return *this->arguments.at(argumentIndex).get(); } /*! * Returns a reference to the Argument with the specified longName. * Throws an Exception of Type IllegalArgumentException if there is no such Option. */ ArgumentBase const& getArgumentByName(std::string const& argumentName) const { auto argumentIterator = this->argumentNameMap.find(storm::utility::StringHelper::stringToLower(argumentName)); if (argumentIterator == this->argumentNameMap.end()) { // LOG throw storm::exceptions::IllegalArgumentException() << "The Option \"" << this->getLongName() << "\" does not contain an Argument with Name \"" << argumentName << "\"!"; } return *argumentIterator->second.get(); } std::string const& getLongName() const { return this->longName; } std::string const& getShortName() const { return this->shortName; } std::string const& getDescription() const { return this->description; } std::string const& getModuleName() const { return this->moduleName; } bool getIsRequired() const { return this->isRequired; } bool getHasOptionBeenSet() const { return this->hasBeenSet; } void setHasOptionBeenSet() { this->hasBeenSet = true; } private: std::string longName; std::string shortName; std::string description; std::string moduleName; bool isRequired; bool hasBeenSet; std::vector> arguments; std::unordered_map> argumentNameMap; void validateFields() const { if (longName.empty()) { throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with an empty longName field!"; } if (moduleName.empty()) { throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with an empty moduleName field!"; } bool longNameContainsNonAlpha = std::find_if(longName.begin(), longName.end(), [](char c) { return !std::isalpha(c); }) != longName.end(); bool shortNameContainsNonAlpha = std::find_if(shortName.begin(), shortName.end(), [](char c) { return !std::isalpha(c); }) != shortName.end(); if (longNameContainsNonAlpha) { throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with a longName that contains non-alpha characters!"; } if (shortNameContainsNonAlpha) { throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with a shortName that contains non-alpha characters!"; } } bool isArgumentsVectorValid(std::vector> const& arguments) { bool lastEntryWasOptional = false; std::unordered_set argumentNameSet; for (auto i = arguments.begin(); i != arguments.end(); ++i) { bool isCurrentArgumentOptional = i->get()->getIsOptional(); //if (!this->isRequired && !i->get()->getHasDefaultValue()) { // LOG // throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains an argument without a default value, but the containing option is optional and therefor requires all arguments to provide default values."; //} if (!isCurrentArgumentOptional && lastEntryWasOptional) { // LOG throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains a non-optional argument AFTER an optional argument."; } std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(i->get()->getArgumentName()); if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) { // LOG throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains two arguments with the same name."; } argumentNameSet.insert(lowerArgumentName); // This copies the Name to the Name Lookup Map argumentNameMap.insert(std::make_pair(lowerArgumentName, std::shared_ptr(*i))); lastEntryWasOptional = isCurrentArgumentOptional; } return true; } }; } } #endif // STORM_SETTINGS_OPTION_H_