From 18c0ee1f1411cf26d55be5f518e06ce0a04bb0e8 Mon Sep 17 00:00:00 2001 From: dehnert Date: Fri, 19 Sep 2014 20:57:53 +0200 Subject: [PATCH] Further big refactoring of option system. Former-commit-id: 0567aa44219070915c293240e6e3d88cff69c34f --- src/settings/Argument.h | 358 +++++++++++-------------- src/settings/ArgumentBase.h | 232 ++++++++++------ src/settings/ArgumentBuilder.h | 305 ++++++++++----------- src/settings/ArgumentType.h | 67 ++--- src/settings/ArgumentValidators.h | 171 +++++++----- src/settings/InternalOptionMemento.h | 70 ----- src/settings/InternalSettingsMemento.h | 61 +++++ src/settings/Option.h | 328 +++++++++++----------- src/settings/OptionBuilder.h | 143 +++++----- src/utility/StringHelper.h | 63 ----- 10 files changed, 859 insertions(+), 939 deletions(-) delete mode 100644 src/settings/InternalOptionMemento.h create mode 100644 src/settings/InternalSettingsMemento.h delete mode 100644 src/utility/StringHelper.h diff --git a/src/settings/Argument.h b/src/settings/Argument.h index 6e431e197..5a138bf89 100644 --- a/src/settings/Argument.h +++ b/src/settings/Argument.h @@ -14,153 +14,112 @@ #include "src/settings/ArgumentBase.h" #include "src/settings/ArgumentType.h" #include "src/settings/ArgumentTypeInferationHelper.h" -#include "src/utility/StringHelper.h" +#include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/ArgumentUnificationException.h" #include "src/exceptions/IllegalArgumentException.h" #include "src/exceptions/IllegalArgumentValueException.h" #include "src/exceptions/IllegalFunctionCallException.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - namespace storm { namespace settings { - + + /*! + * This class subclasses the argument base to actually implement the pure virtual functions. This construction + * is necessary so that it becomes easy to store a vector of arguments later despite variing template types, by + * keeping a vector of pointers to the base class. + */ template class Argument : public ArgumentBase { public: - typedef std::function userValidationFunction_t; - - typedef T argumentType_t; - - /* - T argumentValue; - ArgumentType argumentType; - - std::vector userValidationFunction; - - T defaultValue; - bool hasDefaultValue; - */ - - Argument(std::string const& argumentName, std::string const& argumentDescription, std::vector const& validationFunctions, bool isOptional): ArgumentBase(argumentName, argumentDescription, isOptional), argumentType(ArgumentTypeInferation::inferToEnumType()), userValidationFunction(validationFunctions), hasDefaultValue(false) { - if (isOptional) { - LOG4CPLUS_ERROR(logger, "Argument::Argument: The Argument \"" << argumentName << "\" is flaged as optional but no default value was given!"); - throw storm::exceptions::IllegalArgumentException() << "The Argument \"" << argumentName << "\" is flaged as optional but no default value was given!"; - } - } - - Argument(std::string const& argumentName, std::string const& argumentDescription, std::vector const& validationFunctions, bool isOptional, T defaultValue): ArgumentBase(argumentName, argumentDescription, isOptional), argumentType(ArgumentTypeInferation::inferToEnumType()), userValidationFunction(validationFunctions), defaultValue(defaultValue), hasDefaultValue(true) { - - } - - Argument(Argument const& other) : ArgumentBase(other.argumentName, other.argumentDescription, other.isOptional), argumentType(other.argumentType), defaultValue(other.defaultValue), hasDefaultValue(other.hasDefaultValue) { - this->userValidationFunction.reserve(other.userValidationFunction.size()); - for (size_t i = 0; i < other.userValidationFunction.size(); ++i) { - this->userValidationFunction.push_back(userValidationFunction_t(other.userValidationFunction.at(i))); - } - } - - virtual ~Argument() { - //LOG4CPLUS_DEBUG(logger, "Argument::~Argument: Destructing Argument \"" << this->getArgumentName() << "\" of Type " << ArgumentTypeHelper::toString(this->getArgumentType())); - - this->userValidationFunction.clear(); - this->argumentType = ArgumentType::Invalid; - } - - virtual ArgumentBase* clone() const override { - return new Argument(*this); - } - - assignmentResult_t fromStringValue(std::string const& fromStringValue) override { + // Introduce shortcuts for validation functions. + typedef std::function userValidationFunction_t; + + /*! + * Creates a new argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @param validationFunctions A vector of validation functions that are to be executed upon assigning a value + * to this argument. + * @param isOptional A flag indicating whether the argument is optional. + */ + Argument(std::string const& name, std::string const& description, std::vector const& validationFunctions): ArgumentBase(name, description), argumentValue(), argumentType(ArgumentTypeInferation::inferToEnumType()), validationFunctions(validationFunctions), isOptional(false), defaultValue(), hasDefaultValue(false) { + // Intentionally left empty. + } + + /*! + * Creates a new argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @param validationFunctions A vector of validation functions that are to be executed upon assigning a value + * to this argument. + * @param isOptional A flag indicating whether the argument is optional. + */ + Argument(std::string const& name, std::string const& description, std::vector const& validationFunctions, bool isOptional, T defaultValue): ArgumentBase(argumentName, argumentDescription), argumentValue(), argumentType(ArgumentTypeInferation::inferToEnumType()), validationFunctions(validationFunctions), isOptional(isOptional), defaultValue(defaultValue), hasDefaultValue(true) { + // Intentionally left empty. + } + + virtual bool getIsOptional() const override { + return this->isOptional; + } + + bool setFromStringValue(std::string const& fromStringValue) override { bool conversionOk = false; - T newValue = this->convertFromString(fromStringValue, &conversionOk); + T newValue = ArgumentBase::convertFromString(fromStringValue, conversionOk); if (!conversionOk) { - std::string message("Could not convert the given String into ArgumentType Format (\""); - message.append(ArgumentTypeHelper::toString(this->argumentType)); - message.append("\")!"); - return std::make_pair(false, message); + return false; } return this->fromTypeValue(newValue); } - - assignmentResult_t fromTypeValue(T const& newValue) { - std::string errorText = ""; - if (!this->validateForEach(newValue, errorText)) { - // The Conversion failed or a user defined Validation Function was given and it rejected the Input. - return std::make_pair(false, errorText); + + bool setFromTypeValue(T const& newValue) { + if (!this->validate(newValue)) { + return false; } this->argumentValue = newValue; this->hasBeenSet = true; - return std::make_pair(true, "Success"); + return true; } - - virtual ArgumentType getArgumentType() const override { + + virtual ArgumentType getType() const override { return this->argumentType; } - + + /*! + * Checks whether the given argument is compatible with the current one. If not, an exception is thrown. + * + * @param other The other argument with which to check compatibility. + * @return True iff the given argument is compatible with the current one. + */ template - void unify(Argument &rhs) { - if (this->getArgumentType() != rhs.getArgumentType()) { - LOG4CPLUS_ERROR(logger, "Argument::unify: While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Types do not match (\"" << ArgumentTypeHelper::toString(this->getArgumentType()) << "\" and \"" << ArgumentTypeHelper::toString(rhs.getArgumentType()) << "\")."); - throw storm::exceptions::ArgumentUnificationException() << "While unifying Argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Types do not match (\"" << ArgumentTypeHelper::toString(this->getArgumentType()) << "\" and \"" << ArgumentTypeHelper::toString(rhs.getArgumentType()) << "\")."; - } - - if (this->getIsOptional() != rhs.getIsOptional()) { - LOG4CPLUS_ERROR(logger, "Argument::unify: While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Both must either be optional or non-optional."); - throw storm::exceptions::ArgumentUnificationException() << "While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Both must either be optional or non-optional."; - } - - if (this->getHasDefaultValue() != rhs.getHasDefaultValue()) { - LOG4CPLUS_ERROR(logger, "Argument::unify: While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Mismatching default values."); - throw storm::exceptions::ArgumentUnificationException() << "While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Mismatching default values."; - } - - if (this->getArgumentDescription().compare(rhs.getArgumentDescription()) != 0) { - LOG4CPLUS_WARN(logger, "Argument::unify: While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Mismatching descriptions."); - } - - if (this->getArgumentName().compare(rhs.getArgumentName()) != 0) { - LOG4CPLUS_WARN(logger, "Argument::unify: While unifying argument \"" << this->getArgumentName() << "\" and argument \"" << rhs.getArgumentName() << "\": Mismatching descriptions."); - } - - // Add Validation functions - userValidationFunction.insert(userValidationFunction.end(), rhs.userValidationFunction.begin(), rhs.userValidationFunction.end()); - - // Re-Add the Default Value to check for errors - this->setDefaultValue(this->defaultValue); - } - - T getArgumentValue() const { - if (!this->getHasBeenSet()) { - LOG4CPLUS_ERROR(logger, "Argument::getArgumentValue: Unable to retrieve argument of option \"" << this->getArgumentName() << "\", because it was never set and does not specify a default value."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve argument of option \"" << this->getArgumentName() << "\", because it was never set and does not specify a default value."; - } + bool isCompatibleWith(Argument const& other) { + LOG_THROW(this->getArgumentType() == other.getArgumentType(), storm::exceptions::ArgumentUnificationException, "Unable to unify the arguments " << this->getArgumentName() << " and " << other.getArgumentName() << ", because they have different types."); + LOG_THROW(this->getIsOptional() != other.getIsOptional(), storm::exceptions::ArgumentUnificationException, "Unable to unify the arguments " << this->getArgumentName() << " and " << other.getArgumentName() << ", because one of them is optional and the other one is not."); + LOG_THROW(this->getHasDefaultValue() != other.getHasDefaultValue(), storm::exceptions::ArgumentUnificationException, "Unable to unify the arguments " << this->getArgumentName() << " and " << other.getArgumentName() << ", because one of them has a default value and the other one does not."); + return true; + } + + /*! + * Retrieves the value of the argument if any has been set. Otherwise, an exception is thrown. + * + * @return The value of the argument. + */ + T const& getArgumentValue() const { + LOG_THROW(this->getHasBeenSet(), storm::exceptions::IllegalFunctionCallException, "Unable to retrieve value of argument, because it was not set."); return this->argumentValue; } - + virtual bool getHasDefaultValue() const override { return this->hasDefaultValue; } - - std::string const& getDefaultValue() { - return this->defaultValue; - } - + void setFromDefaultValue() override { - if (!this->hasDefaultValue) { - LOG4CPLUS_ERROR(logger, "Argument::setFromDefaultValue: Unable to retrieve default value for argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << ")."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve default value for argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << ")."; - } - // this call also sets the hasBeenSet flag - assignmentResult_t result = this->fromTypeValue(this->defaultValue); - if (!result.first) { - LOG4CPLUS_ERROR(logger, "Argument::setFromDefaultValue: Unable to assign default value to argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << "), because default value was rejected (" << result.second << ")."); - throw storm::exceptions::IllegalArgumentValueException() << "Unable to assign default value to argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << "), because default value was rejected (" << result.second << ")."; - } + LOG_THROW(this->hasDefaultValue, storm::exceptions::IllegalFunctionCallException, "Unable to set value from default value, because the argument has none."); + bool result = this->setFromTypeValue(this->defaultValue); + LOG_THROW(result, storm::exceptions::IllegalArgumentValueException, "Unable to assign default value to argument, because it was rejected."); } - + virtual std::string getValueAsString() const override { switch (this->argumentType) { case ArgumentType::String: @@ -172,11 +131,8 @@ namespace storm { } else { return "false"; } - } - default: - return this->convertToString(this->argumentValue); - + default: return ArgumentBase::convertToString(this->argumentValue); } } @@ -184,91 +140,79 @@ namespace storm { switch (this->argumentType) { case ArgumentType::Integer: return ArgumentTypeInferation::inferToInteger(ArgumentType::Integer, this->getArgumentValue()); - default: { - LOG4CPLUS_ERROR(logger, "Argument::getValueAsInteger(): Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as integer."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as integer."; - } - } - } - - virtual uint_fast64_t getValueAsUnsignedInteger() const override { - switch (this->argumentType) { - case ArgumentType::UnsignedInteger: - return ArgumentTypeInferation::inferToUnsignedInteger(ArgumentType::UnsignedInteger, this->getArgumentValue()); - default: { - LOG4CPLUS_ERROR(logger, "Argument::getValueAsUnsignedInteger(): Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as unsigned integer."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as unsigned integer."; - } - } - } - - virtual double getValueAsDouble() const override { - switch (this->argumentType) { - case ArgumentType::Double: - return ArgumentTypeInferation::inferToDouble(ArgumentType::Double, this->getArgumentValue()); - default: { - LOG4CPLUS_ERROR(logger, "Argument::getValueAsDouble(): Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as double."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as double."; - } - } - } - - virtual bool getValueAsBoolean() const override { - switch (this->argumentType) { - case ArgumentType::Boolean: - return ArgumentTypeInferation::inferToBoolean(ArgumentType::Boolean, this->getArgumentValue()); - default: { - LOG4CPLUS_ERROR(logger, "Argument::getValueAsBoolean(): Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as boolean."); - throw storm::exceptions::IllegalFunctionCallException() << "Unable to retrieve value of argument \"" << getArgumentName() << "\" of type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\" as boolean."; - } - } - } - private: - T argumentValue; - ArgumentType argumentType; - - std::vector userValidationFunction; - - T defaultValue; - bool hasDefaultValue; - - void setDefaultValue(T const& newDefault) { - std::string errorText = ""; - if (!this->validateForEach(newDefault, errorText)) { - // A user defined Validation Function was given and it rejected the Input. - LOG4CPLUS_ERROR(logger, "Argument::setDefaultValue: Illegal default value for argument \"" << this->getArgumentName() << "\"." << std::endl << "The validation function rejected the value (" << errorText << ")."); - throw storm::exceptions::IllegalArgumentValueException() << "Illegal default value for argument \"" << this->getArgumentName() << "\". The validation function rejected the value (" << errorText << ")."; - } - this->defaultValue = newDefault; - this->hasDefaultValue = true; - } - - void unsetDefaultValue() { - this->hasDefaultValue = false; - } - - std::string convertToString(T const& t) const { - std::ostringstream stream; - stream << t; - return stream.str(); - } - - T convertFromString(std::string const& s, bool* ok = nullptr) const { - return storm::settings::ArgumentBase::ArgumentHelper::convertFromString(s, ok); - } - - bool validateForEach(T const& value, std::string& errorMessageTarget) const { - bool result = true; - - for (auto lambda: this->userValidationFunction) { - result = result && lambda(value, errorMessageTarget); - } - return result; - } - - - }; - } + default: LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve argument value as integer."); break; + } + } + + + virtual uint_fast64_t getValueAsUnsignedInteger() const override { + switch (this->argumentType) { + case ArgumentType::UnsignedInteger: + return ArgumentTypeInferation::inferToUnsignedInteger(ArgumentType::UnsignedInteger, this->getArgumentValue()); + default: LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve argument value as unsigned integer."); break; + } + } + + + virtual double getValueAsDouble() const override { + switch (this->argumentType) { + case ArgumentType::Double: + return ArgumentTypeInferation::inferToDouble(ArgumentType::Double, this->getArgumentValue()); + default: LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve argument value as double."); break; + } + } + + + virtual bool getValueAsBoolean() const override { + switch (this->argumentType) { + case ArgumentType::Boolean: + return ArgumentTypeInferation::inferToBoolean(ArgumentType::Boolean, this->getArgumentValue()); + default: LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve argument value as boolean."); break; + } + } + + private: + // The value of the argument (in case it has been set). + T argumentValue; + + // The type of the argument. + ArgumentType argumentType; + + // The validation functions that were registered for this argument. + std::vector validationFunctions; + + // A flag indicating whether this argument is optional. + bool isOptional; + + // The default value for the argument (in case one has been provided). + T defaultValue; + + // A flag indicating whether a default value has been provided. + bool hasDefaultValue; + + void setDefaultValue(T const& newDefault) { + LOG_THROW(this->validate(newDefault), storm::exceptions::IllegalArgumentValueException, "The default value for the argument did not pass all validation functions."); + this->defaultValue = newDefault; + this->hasDefaultValue = true; + } + + /*! + * Applies all validation functions to the given value and therefore checks the validity of a value for this + * argument. + * + * @param value The value that is to be validated. + * @return True iff the value passed all validation functions successfully. + */ + bool validate(T const& value) const { + bool result = true; + for (auto const& lambda : validationFunctions) { + result = result && lambda(value); + } + return result; + } + + }; + } } #endif // STORM_SETTINGS_ARGUMENT_H_ \ No newline at end of file diff --git a/src/settings/ArgumentBase.h b/src/settings/ArgumentBase.h index 518910fb7..1f8f38c6f 100644 --- a/src/settings/ArgumentBase.h +++ b/src/settings/ArgumentBase.h @@ -3,111 +3,187 @@ #include #include +#include -#include "ArgumentType.h" -#include "src/utility/StringHelper.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; +#include "src/settings/ArgumentType.h" namespace storm { namespace settings { - typedef std::pair assignmentResult_t; - + /*! + * This class serves as the (untemplated) base class of argument classes. + */ class ArgumentBase { public: - ArgumentBase(std::string const& argumentName, std::string const& argumentDescription, bool isOptional) : isOptional(isOptional), hasBeenSet(false), argumentName(argumentName), argumentDescription(argumentDescription) {} - virtual ~ArgumentBase() { - //LOG4CPLUS_DEBUG(logger, "ArgumentBase::~ArgumentBase: Destructing ArgumentBase \"" << this->getArgumentName() << "\""); - } - virtual ArgumentType getArgumentType() const = 0; - - virtual bool getIsOptional() const { - return this->isOptional; - } - - std::string const& getArgumentName() const { - return this->argumentName; + /*! + * Constructs a new argument base with the given name, description and indication whether the argument is + * optional. + * + * @param name The name of the argument. + * @param description A description of the argument. + * @param isOptional A flag indicating whether the argument is optional. + */ + ArgumentBase(std::string const& name, std::string const& description) : hasBeenSet(false), name(name), description(description) { + // Intentionally left empty. + } + + /*! + * Retrieves the type of the argument. + * + * @return The type of the argument. + */ + virtual ArgumentType getType() const = 0; + + /*! + * Retrieves whether the argument is optional. + * + * @return True iff the argument is optional. + */ + virtual bool getIsOptional() const = 0; + + /*! + * Retrieves the name of the argument. + * + * @return The name of the argument. + */ + std::string const& getName() const { + return this->name; } - std::string const& getArgumentDescription() const { - return this->argumentDescription; + /*! + * Retrieves the description of the argument. + * + * @return The description of the argument. + */ + std::string const& getDescription() const { + return this->description; } + /*! + * Retrieves whether the argument has a default value. + * + * @return True iff the argument has a default value. + */ virtual bool getHasDefaultValue() const = 0; + + /*! + * Retrieves whether the argument has been set. + * + * @return True iff the argument has been set. + */ virtual bool getHasBeenSet() const { return this->hasBeenSet; } + /*! + * Sets the value of the argument from the default value. + */ virtual void setFromDefaultValue() = 0; - virtual assignmentResult_t fromStringValue(std::string const& fromStringValue) = 0; - virtual ArgumentBase* clone() const = 0; + /*! + * Tries to set the value of the argument from the given string. + * + * @param stringValue The new value of the argument given as a string. + * @return True iff the assignment was successful. + */ + virtual bool setFromStringValue(std::string const& stringValue) = 0; + + /*! + * Retrieves the value of this argument as a string. + * + * @return The value of this argument as a string. + */ virtual std::string getValueAsString() const = 0; + + /*! + * Retrieves the value of this argument as an integer. If the conversion cannot be performed, an exception + * is thrown. + * + * @return The value of this argument as an integer. + */ virtual int_fast64_t getValueAsInteger() const = 0; + + /*! + * Retrieves the value of this argument as an unsigned integer. If the conversion cannot be performed, an + * exception is thrown. + * + * @return The value of this argument as an unsigned integer. + */ virtual uint_fast64_t getValueAsUnsignedInteger() const = 0; + + /*! + * Retrieves the value of this argument as a double. If the conversion cannot be performed, an exception + * is thrown. + * + * @return The value of this argument as an double. + */ virtual double getValueAsDouble() const = 0; + + /*! + * Retrieves the value of this argument as a boolean. If the conversion cannot be performed, an exception + * is thrown. + * + * @return The value of this argument as an boolean. + */ virtual bool getValueAsBoolean() const = 0; - protected: - bool isOptional; + + protected: + // A flag indicating whether the argument has been set. bool hasBeenSet; - std::string argumentName; - std::string argumentDescription; - - class ArgumentHelper { - public: - template - static S convertFromString(std::string const& s, bool* ok = nullptr); - private: - ArgumentHelper() {} - ArgumentHelper(ArgumentHelper& other) {} - ~ArgumentHelper() {} - }; + // The name of the argument. + std::string name; + + // The description of the argument. + std::string description; + + /*! + * Converts the given value represented as a string to the type of the template parameter. The second + * is used to signal that the conversion was successful (or not). + * + * @param valueAsString The value to convert. + * @param conversionSuccessful After a call to this function returned, the supplied boolean indicates + * whether the conversion was successful. + */ + template + static TargetType convertFromString(std::string const& valueAsString, bool& conversionSuccessful) { + std::istringstream stream(valueAsString); + TargetType t; + conversionSuccessful = (stream >> t) && (stream >> std::ws).eof(); + return t; + } + + template <> + static bool convertFromString(std::string const& s, bool& ok) { + static const std::string lowerTrueString = "true"; + static const std::string lowerFalseString = "false"; + static const std::string lowerYesString = "yes"; + static const std::string lowerNoString = "no"; + + std::string lowerInput = boost::algorithm::to_lower_copy(s); + + if (s.compare(lowerTrueString) == 0 || s.compare(lowerYesString) == 0) { + ok = true; + return true; + } else if (s.compare(lowerFalseString) == 0 || s.compare(lowerNoString) == 0) { + ok = true; + return false; + } + + std::istringstream stream(s); + bool t; + ok = (stream >> t) && (stream >> std::ws).eof(); + return t; + } + + template + std::string convertToString(ValueType const& value) const { + std::ostringstream stream; + stream << value; + return stream.str(); + } }; - template S ArgumentBase::ArgumentHelper::convertFromString(std::string const& s, bool* ok) { - std::istringstream stream(s); - S t; - if (ok != nullptr) { - *ok = (stream >> t) && (stream >> std::ws).eof(); - } else { - stream >> t; - } - return t; - } - - template <> inline bool ArgumentBase::ArgumentHelper::convertFromString(std::string const& s, bool* ok) { - static const std::string lowerTrueString = "true"; - static const std::string lowerFalseString = "false"; - static const std::string lowerYesString = "yes"; - static const std::string lowerNoString = "no"; - - std::string lowerInput = storm::utility::StringHelper::stringToLower(s); - - if (s.compare(lowerTrueString) == 0 || s.compare(lowerYesString) == 0) { - if (ok != nullptr) { - *ok = true; - } - return true; - } else if (s.compare(lowerFalseString) == 0 || s.compare(lowerNoString) == 0) { - if (ok != nullptr) { - *ok = true; - } - return false; - } - - std::istringstream stream(s); - bool t; - if (ok != nullptr) { - *ok = (stream >> t) && (stream >> std::ws).eof(); - } else { - stream >> t; - } - return t; - } } } diff --git a/src/settings/ArgumentBuilder.h b/src/settings/ArgumentBuilder.h index 30350ca50..ba04142b8 100644 --- a/src/settings/ArgumentBuilder.h +++ b/src/settings/ArgumentBuilder.h @@ -11,247 +11,216 @@ #include #include -#include "ArgumentType.h" -#include "ArgumentTypeInferationHelper.h" -#include "ArgumentBase.h" -#include "Argument.h" -#include "ArgumentValidators.h" +#include "src/settings/ArgumentType.h" +#include "src/settings/ArgumentTypeInferationHelper.h" +#include "src/settings/ArgumentBase.h" +#include "src/settings/Argument.h" +#include "src/settings/ArgumentValidators.h" +#include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/IllegalFunctionCallException.h" #include "src/exceptions/IllegalArgumentTypeException.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - namespace storm { namespace settings { - + + /*! + * This class serves as an API for creating arguments. + */ class ArgumentBuilder { public: - ~ArgumentBuilder() {} - - /* - Preparation Functions for all ArgumentType's - - Resets all internal attributes - */ - static ArgumentBuilder createStringArgument(std::string const& argumentName, std::string const& argumentDescription) { - ArgumentBuilder ab(ArgumentType::String, argumentName, argumentDescription); + /*! + * Creates a string argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @return The builder of the argument. + */ + static ArgumentBuilder createStringArgument(std::string const& name, std::string const& description) { + ArgumentBuilder ab(ArgumentType::String, name, argumentDescription); return ab; } - - static ArgumentBuilder createIntegerArgument(std::string const& argumentName, std::string const& argumentDescription) { - ArgumentBuilder ab(ArgumentType::Integer, argumentName, argumentDescription); + + /*! + * Creates an integer argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @return The builder of the argument. + */ + static ArgumentBuilder createIntegerArgument(std::string const& name, std::string const& description) { + ArgumentBuilder ab(ArgumentType::Integer, name, description); return ab; } - - static ArgumentBuilder createUnsignedIntegerArgument(std::string const& argumentName, std::string const& argumentDescription) { - ArgumentBuilder ab(ArgumentType::UnsignedInteger, argumentName, argumentDescription); + + /*! + * Creates an unsigned integer argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @return The builder of the argument. + */ + static ArgumentBuilder createUnsignedIntegerArgument(std::string const& name, std::string const& description) { + ArgumentBuilder ab(ArgumentType::UnsignedInteger, name, description); return ab; } - - static ArgumentBuilder createDoubleArgument(std::string const& argumentName, std::string const& argumentDescription) { - ArgumentBuilder ab(ArgumentType::Double, argumentName, argumentDescription); + + /*! + * Creates a double argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @return The builder of the argument. + */ + static ArgumentBuilder createDoubleArgument(std::string const& name, std::string const& description) { + ArgumentBuilder ab(ArgumentType::Double, name, description); return ab; } - - static ArgumentBuilder createBooleanArgument(std::string const& argumentName, std::string const& argumentDescription) { - ArgumentBuilder ab(ArgumentType::Boolean, argumentName, argumentDescription); + + /*! + * Creates a boolean argument with the given parameters. + * + * @param name The name of the argument. + * @param description The description of the argument. + * @return The builder of the argument. + */ + static ArgumentBuilder createBooleanArgument(std::string const& name, std::string const& description) { + ArgumentBuilder ab(ArgumentType::Boolean, name, description); return ab; } - - ArgumentBuilder& setName(std::string const& newName) { - this->argumentName = newName; - return *this; - } - - ArgumentBuilder& setDescription(std::string const& newDescription) { - this->argumentDescription = newDescription; - return *this; - } - + + /*! + * Sets whether the argument is to be optional. + * + * @param isOptional A flag that indicates whether the argument is to be optional. + * @return A reference to the argument builder. + */ ArgumentBuilder& setIsOptional(bool isOptional) { this->isOptional = isOptional; return *this; } + #define PPCAT_NX(A, B) A ## B #define PPCAT(A, B) PPCAT_NX(A, B) #define MACROaddValidationFunction(funcName, funcType) ArgumentBuilder& PPCAT(addValidationFunction, funcName) (storm::settings::Argument< funcType >::userValidationFunction_t userValidationFunction) { \ - if (this->argumentType != ArgumentType::funcName) { \ - throw storm::exceptions::IllegalFunctionCallException() << "Unable to add a validation function for a \"" << ArgumentTypeHelper::toString(ArgumentType::funcName) << "\" argument, because this argument is of type \"" << ArgumentTypeHelper::toString(this->argumentType) << "\"."; \ - } \ - ( PPCAT(this->userValidationFunction_, funcName) ).push_back(userValidationFunction); \ - std::string errorMessageTarget = ""; \ - if (this->hasDefaultValue && !this->validateDefaultForEach(errorMessageTarget)) { \ - throw storm::exceptions::IllegalArgumentValueException() << "Unable to add a validation function for an argument that has a default value which is rejected by this validation function:\r\n" << errorMessageTarget << "."; \ - } \ - return *this; \ - } - +LOG_THROW(this->argumentType == ArgumentType::funcName, storm::exceptions::IllegalFunctionCallException, "Illegal validation function for argument, because it takes arguments of different type.") \ +( PPCAT(this->userValidationFunction_, funcName) ).push_back(userValidationFunction); \ +return *this; \ +} + + // Add the methods to add validation functions. MACROaddValidationFunction(String, std::string) MACROaddValidationFunction(Integer, int_fast64_t) MACROaddValidationFunction(UnsignedInteger, uint_fast64_t) MACROaddValidationFunction(Double, double) MACROaddValidationFunction(Boolean, bool) - - + + #define MACROsetDefaultValue(funcName, funcType) ArgumentBuilder& PPCAT(setDefaultValue, funcName) (funcType const& defaultValue) { \ - if (this->argumentType != ArgumentType::funcName) { \ - throw storm::exceptions::IllegalFunctionCallException() << "Unable to add a default value for a \"" << ArgumentTypeHelper::toString(ArgumentType::String) << "\" argument, because the argument \"" << this->argumentName << "\" is of type \"" << ArgumentTypeHelper::toString(this->argumentType) << "\"."; \ - } \ - PPCAT(this->defaultValue_, funcName) = defaultValue; \ - std::string errorMessageTarget = ""; \ - if (!this->validateDefaultForEach(errorMessageTarget)) { \ - throw storm::exceptions::IllegalArgumentValueException() << "Unable to add a default value for the argument \"" << this->argumentName << "\", but a validation function rejected it:\r\n" << errorMessageTarget << "."; \ - } \ - this->hasDefaultValue = true; \ - return *this; \ - } - +LOG_THROW(this->argumentType == ArgumentType::funcName, storm::exceptions::IllegalFunctionCallException, "Illegal default value for argument" << this->argumentName << ", because it is of different type.") \ +PPCAT(this->defaultValue_, funcName) = defaultValue; \ +this->hasDefaultValue = true; \ +return *this; \ +} + + // Add the methods to set a default value. MACROsetDefaultValue(String, std::string) MACROsetDefaultValue(Integer, int_fast64_t) MACROsetDefaultValue(UnsignedInteger, uint_fast64_t) MACROsetDefaultValue(Double, double) MACROsetDefaultValue(Boolean, bool) - - ArgumentBase* build() { - if (this->hasBeenBuild) { - throw storm::exceptions::IllegalFunctionCallException() << "Called build() on an instance of ArgumentBuilder that has already built an instance."; - } - this->hasBeenBuild = true; + + /*! + * Builds an argument based on the information that was added to the builder object. + * + * @return The resulting argument. + */ + std::shared_ptr build() { + LOG_THROW(!this->hasBeenBuilt, storm::exceptions::IllegalFunctionCallException, "Cannot rebuild argument with builder that was already used to build an argument."); + this->hasBeenBuilt = true; switch (this->argumentType) { case ArgumentType::String: { if (this->hasDefaultValue) { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_String, this->isOptional, this->defaultValue_String)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_String, this->isOptional, this->defaultValue_String)); } else { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_String, this->isOptional)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_String)); } break; - } + } case ArgumentType::Integer: if (this->hasDefaultValue) { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Integer, this->isOptional, this->defaultValue_Integer)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Integer, this->isOptional, this->defaultValue_Integer)); } else { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Integer, this->isOptional)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Integer)); } break; case ArgumentType::UnsignedInteger: if (this->hasDefaultValue) { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_UnsignedInteger, this->isOptional, this->defaultValue_UnsignedInteger)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_UnsignedInteger, this->isOptional, this->defaultValue_UnsignedInteger)); } else { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_UnsignedInteger, this->isOptional)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_UnsignedInteger)); } break; case ArgumentType::Double: if (this->hasDefaultValue) { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Double, this->isOptional, this->defaultValue_Double)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Double, this->isOptional, this->defaultValue_Double)); } else { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Double, this->isOptional)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Double)); } break; case ArgumentType::Boolean: if (this->hasDefaultValue) { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Boolean, this->isOptional, this->defaultValue_Boolean)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Boolean, this->isOptional, this->defaultValue_Boolean)); } else { - return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Boolean, this->isOptional)); + return std::shared_ptr(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_Boolean)); } break; - default: { - throw storm::exceptions::InternalTypeErrorException() << "Missing Case in ArgumentBuilder's switch/case Code."; - } } } + private: - ArgumentBuilder(ArgumentType argumentType, std::string const& argumentName, std::string const& argumentDescription) : hasBeenBuild(false), argumentType(argumentType), argumentName(argumentName), argumentDescription(argumentDescription), isOptional(false), hasDefaultValue(false) { - // - } - ArgumentBuilder(ArgumentBuilder& other) : hasBeenBuild(other.hasBeenBuild), argumentType(other.argumentType), argumentName(other.argumentName), argumentDescription(other.argumentDescription), isOptional(other.isOptional), - defaultValue_String(other.defaultValue_String), defaultValue_Integer(other.defaultValue_Integer), defaultValue_UnsignedInteger(other.defaultValue_UnsignedInteger), defaultValue_Double(other.defaultValue_Double), defaultValue_Boolean(other.defaultValue_Boolean), - hasDefaultValue(other.hasDefaultValue) { - // Copy all userFunctions - for (uint_fast64_t i = 0; i < userValidationFunction_String.size(); ++i) { - this->userValidationFunction_String.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_String.at(i))); - } - for (uint_fast64_t i = 0; i < userValidationFunction_Integer.size(); ++i) { - this->userValidationFunction_Integer.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Integer.at(i))); - } - for (uint_fast64_t i = 0; i < userValidationFunction_UnsignedInteger.size(); ++i) { - this->userValidationFunction_UnsignedInteger.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_UnsignedInteger.at(i))); - } - for (uint_fast64_t i = 0; i < userValidationFunction_Double.size(); ++i) { - this->userValidationFunction_Double.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Double.at(i))); - } - for (uint_fast64_t i = 0; i < userValidationFunction_Boolean.size(); ++i) { - this->userValidationFunction_Boolean.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Boolean.at(i))); - } + /*! + * Creates an argument builder for an argument with the given parameters. + * + * @param type The type of the argument. + * @param name The name of the argument. + * @param description The description of the argument. + */ + ArgumentBuilder(ArgumentType type, std::string const& name, std::string const& description) : hasBeenBuilt(false), type(type), name(name), description(description), isOptional(false), hasDefaultValue(false), defaultValue_String(), defaultValue_Integer(), defaultValue_UnsignedInteger(), defaultValue_Double(), defaultValue_Boolean(), userValidationFunctions_String(), userValidationFunctions_Integer(), userValidationFunctions_UnsignedInteger(), userValidationFunctions_Double(), userValidationFunctions_Boolean() { + // Intentionally left empty. } - - bool hasBeenBuild; - - ArgumentType argumentType; - - std::string argumentName; - std::string argumentDescription; - - /* - enum class ArgumentType { - Invalid, String, Integer, UnsignedInteger, Double, Boolean - }; - */ - - std::vector::userValidationFunction_t> userValidationFunction_String; - std::vector::userValidationFunction_t> userValidationFunction_Integer; - std::vector::userValidationFunction_t> userValidationFunction_UnsignedInteger; - std::vector::userValidationFunction_t> userValidationFunction_Double; - std::vector::userValidationFunction_t> userValidationFunction_Boolean; - + + // A flag that stores whether an argument has been built using this builder. + bool hasBeenBuilt; + + // The type of the argument. + ArgumentType type; + + // The name of te argument. + std::string name; + + // The description of the argument. + std::string description; + + // A flag indicating whether the argument is optional. bool isOptional; + // A flag that stores whether the argument has a default value. + bool hasDefaultValue; + + // The default value of the argument separated by its type. std::string defaultValue_String; int_fast64_t defaultValue_Integer; uint_fast64_t defaultValue_UnsignedInteger; double defaultValue_Double; bool defaultValue_Boolean; - - bool hasDefaultValue; - - bool validateDefaultForEach(std::string& errorMessageTarget) { - bool result = true; - - switch (this->argumentType) { - case ArgumentType::String: - for (auto lambda: this->userValidationFunction_String) { - result = result && lambda(this->defaultValue_String, errorMessageTarget); - } - break; - case ArgumentType::Integer: - for (auto lambda: this->userValidationFunction_Integer) { - result = result && lambda(this->defaultValue_Integer, errorMessageTarget); - } - break; - case ArgumentType::UnsignedInteger: - for (auto lambda: this->userValidationFunction_UnsignedInteger) { - result = result && lambda(this->defaultValue_UnsignedInteger, errorMessageTarget); - } - break; - case ArgumentType::Double: - for (auto lambda: this->userValidationFunction_Double) { - result = result && lambda(this->defaultValue_Double, errorMessageTarget); - } - break; - case ArgumentType::Boolean: - for (auto lambda: this->userValidationFunction_Boolean) { - result = result && lambda(this->defaultValue_Boolean, errorMessageTarget); - } - break; - default: { - throw storm::exceptions::InternalTypeErrorException() << "Missing case in ArgumentBuilder."; - } - } - - return result; - } + + // The validation functions separated by their type. + std::vector::userValidationFunction_t> userValidationFunctions_String; + std::vector::userValidationFunction_t> userValidationFunctions_Integer; + std::vector::userValidationFunction_t> userValidationFunctions_UnsignedInteger; + std::vector::userValidationFunction_t> userValidationFunctions_Double; + std::vector::userValidationFunction_t> userValidationFunctions_Boolean; }; } } diff --git a/src/settings/ArgumentType.h b/src/settings/ArgumentType.h index c652f8d2f..00fa3c362 100644 --- a/src/settings/ArgumentType.h +++ b/src/settings/ArgumentType.h @@ -1,58 +1,33 @@ #ifndef STORM_SETTINGS_ARGUMENTTYPE_H_ #define STORM_SETTINGS_ARGUMENTTYPE_H_ -#include "src/exceptions/InternalTypeErrorException.h" +#include -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; +#include "src/exceptions/ExceptionMacros.h" namespace storm { namespace settings { + + /*! + * This enum captures all possible types for arguments. + */ enum class ArgumentType { - Invalid, String, Integer, UnsignedInteger, Double, Boolean + String, Integer, UnsignedInteger, Double, Boolean }; - class ArgumentTypeHelper { - public: - static std::string const& toString(ArgumentType argumentType) { - static std::string argumentTypeInvalid = "Invalid"; - static std::string argumentTypeString = "String"; - static std::string argumentTypeInteger = "Integer"; - static std::string argumentTypeUnsignedInteger = "UnsignedInteger"; - static std::string argumentTypeDouble = "Double"; - static std::string argumentTypeBoolean = "Boolean"; - - switch (argumentType) { - case ArgumentType::Invalid: - return argumentTypeInvalid; - break; - case ArgumentType::String: - return argumentTypeString; - break; - case ArgumentType::Integer: - return argumentTypeInteger; - break; - case ArgumentType::UnsignedInteger: - return argumentTypeUnsignedInteger; - break; - case ArgumentType::Double: - return argumentTypeDouble; - break; - case ArgumentType::Boolean: - return argumentTypeBoolean; - break; - default: { - LOG4CPLUS_ERROR(logger, "ArgumentTypeHelper::toString: Missing case in ArgumentTypeHelper."); - throw storm::exceptions::InternalTypeErrorException() << "Missing case in ArgumentTypeHelper."; - } - } - } - private: - ArgumentTypeHelper() {} - ~ArgumentTypeHelper() {} - }; - } -} + std::ostream& operator<<(std::ostream& out, ArgumentType& argumentType) { + switch (argumentType) { + case ArgumentType::String: out << "string"; break; + case ArgumentType::Integer: out << "integer"; break; + case ArgumentType::UnsignedInteger: out << "unsigned integer"; break; + case ArgumentType::Double: out << "double"; break; + case ArgumentType::Boolean: out << "boolean"; break; + } + + return out; + } + + } // namespace settings +} // namespace storm #endif // STORM_SETTINGS_ARGUMENTTYPE_H_ \ No newline at end of file diff --git a/src/settings/ArgumentValidators.h b/src/settings/ArgumentValidators.h index d87d4a7d5..0758500cf 100644 --- a/src/settings/ArgumentValidators.h +++ b/src/settings/ArgumentValidators.h @@ -11,110 +11,143 @@ #include #include -#include "Argument.h" +#include "src/settings/Argument.h" namespace storm { namespace settings { class ArgumentValidators { public: - // Integer - int_fast64_t - static std::function integerRangeValidatorIncluding(int_fast64_t const lowerBound, int_fast64_t const upperBound) { + /*! + * Creates a validation function that checks whether an integer is in the given range (including the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function integerRangeValidatorIncluding(int_fast64_t const lowerBound, int_fast64_t const upperBound) { return rangeValidatorIncluding(lowerBound, upperBound); - } - static std::function integerRangeValidatorExcluding(int_fast64_t const lowerBound, int_fast64_t const upperBound) { + } + + /*! + * Creates a validation function that checks whether an integer is in the given range (excluding the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function integerRangeValidatorExcluding(int_fast64_t const lowerBound, int_fast64_t const upperBound) { return rangeValidatorExcluding(lowerBound, upperBound); } - // UnsignedInteger - uint_fast64_t - static std::function unsignedIntegerRangeValidatorIncluding(uint_fast64_t const lowerBound, uint_fast64_t const upperBound) { + /*! + * Creates a validation function that checks whether an unsigned integer is in the given range (including the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function unsignedIntegerRangeValidatorIncluding(uint_fast64_t const lowerBound, uint_fast64_t const upperBound) { return rangeValidatorIncluding(lowerBound, upperBound); - } - static std::function unsignedIntegerRangeValidatorExcluding(uint_fast64_t const lowerBound, uint_fast64_t const upperBound) { + } + + /*! + * Creates a validation function that checks whether an unsigned integer is in the given range (excluding the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function unsignedIntegerRangeValidatorExcluding(uint_fast64_t const lowerBound, uint_fast64_t const upperBound) { return rangeValidatorExcluding(lowerBound, upperBound); } - // Double - double - static std::function doubleRangeValidatorIncluding(double const lowerBound, double const upperBound) { + /*! + * Creates a validation function that checks whether a double is in the given range (including the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function doubleRangeValidatorIncluding(double const lowerBound, double const upperBound) { return rangeValidatorIncluding(lowerBound, upperBound); - } - static std::function doubleRangeValidatorExcluding(double const lowerBound, double const upperBound) { + } + + /*! + * Creates a validation function that checks whether a double is in the given range (excluding the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ + static std::function doubleRangeValidatorExcluding(double const lowerBound, double const upperBound) { return rangeValidatorExcluding(lowerBound, upperBound); } - static std::function existingReadableFileValidator() { - return [] (std::string const fileName, std::string& errorMessageTarget) -> bool { + /*! + * Creates a validation function that checks whether a given string corresponds to an existing and readable + * file. + * + * @return The resulting validation function. + */ + static std::function existingReadableFileValidator() { + return [] (std::string const fileName) -> bool { std::ifstream targetFile(fileName); bool isFileGood = targetFile.good(); - if (!isFileGood) { - std::ostringstream stream; - stream << "Given file does not exist or is not readable by this process: \"" << fileName << "\"" << std::endl; - errorMessageTarget.append(stream.str()); - } + LOG_THROW(isFileGood, storm::exceptions::IllegalArgumentValueException, "The file " << fileName << " does not exist or is not readable."); return isFileGood; }; } - static std::function stringInListValidator(std::vector list) { - return [list] (std::string const inputString, std::string& errorMessageTarget) -> bool { - std::string const lowerInputString = storm::utility::StringHelper::stringToLower(inputString); - for (auto it = list.cbegin(); it != list.cend(); ++it) { - if (storm::utility::StringHelper::stringToLower(*it).compare(lowerInputString) == 0) { + /*! + * Creates a validation function that checks whether a given string is in a provided list of strings. + * + * @param list The list of valid strings. + * @return The resulting validation function. + */ + static std::function stringInListValidator(std::vector const& list) { + return [list] (std::string const inputString) -> bool { + std::string lowerInputString = boost::algorithm::to_lower_copy(inputString); + for (auto const& validString : list) { + std::string lowerValidString = boost::algorithm::to_lower_copy(validString); + if (lowerInputString == lowerValidString) { return true; } } - std::ostringstream stream; - stream << "The given Input \"" << inputString << "\" is not in the list of valid Items ("; - bool first = true; - for (auto it = list.cbegin(); it != list.cend(); ++it) { - if (!first) { - stream << ", "; - } - stream << *it; - first = false; - } - stream << ")" << std::endl; - errorMessageTarget.append(stream.str()); - + LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Value " << inputString << " does not match any entry in the list of valid items."); return false; }; } + private: + /*! + * Creates a validation function that checks whether its argument is in a given range (including the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ template - static std::function rangeValidatorIncluding(T const lowerBound, T const upperBound) { - return std::bind([](T const lowerBound, T const upperBound, T const value, std::string& errorMessageTarget) -> bool { - bool lowerBoundCondition = (lowerBound <= value); - bool upperBoundCondition = (value <= upperBound); - if (!lowerBoundCondition) { - std::ostringstream stream; - stream << " lower bound condition not met: " << lowerBound << " is not smaller or equal than " << value << "."; - errorMessageTarget.append(stream.str()); - } - if (!upperBoundCondition) { - std::ostringstream stream; - stream << " upper bound condition not met: " << value << " is not smaller or equal than " << upperBound << "."; - errorMessageTarget.append(stream.str()); - } - return (lowerBoundCondition && upperBoundCondition); - }, lowerBound, upperBound, std::placeholders::_1, std::placeholders::_2); + static std::function rangeValidatorIncluding(T const lowerBound, T const upperBound) { + return std::bind([](T const& lowerBound, T const& upperBound, T const& value) -> bool { + LOG_THROW(lowerBound <= value && value <= upperBound, storm::exceptions::InvalidArgumentException, "Value " << value " is out range."); + return true; + }, lowerBound, upperBound, std::placeholders::_1, std::placeholders::_2); } + + /*! + * Creates a validation function that checks whether its argument is in a given range (excluding the bounds). + * + * @param lowerBound The lower bound of the valid range. + * @param upperBound The upper bound of the valid range. + * @return The resulting validation function. + */ template - static std::function rangeValidatorExcluding(T const lowerBound, T const upperBound) { - return std::bind([](T const lowerBound, T const upperBound, T const value, std::string& errorMessageTarget) -> bool { - bool lowerBoundCondition = (lowerBound < value); - bool upperBoundCondition = (value < upperBound); - if (!lowerBoundCondition) { - std::ostringstream stream; - stream << " lower bound condition not met: " << lowerBound << " is not smaller than " << value << "."; - errorMessageTarget.append(stream.str()); - } - if (!upperBoundCondition) { - std::ostringstream stream; - stream << " upper bound condition not met: " << value << " is not smaller than < " << upperBound << "."; - errorMessageTarget.append(stream.str()); - } - return (lowerBoundCondition && upperBoundCondition); + static std::function rangeValidatorExcluding(T const& lowerBound, T const& upperBound) { + return std::bind([](T const& lowerBound, T const& upperBound, T const& value) -> bool { + LOG_THROW(lowerBound < value && value < upperBound, storm::exceptions::InvalidArgumentException, "Value " << value " is out range."); + return true; }, lowerBound, upperBound, std::placeholders::_1, std::placeholders::_2); } }; diff --git a/src/settings/InternalOptionMemento.h b/src/settings/InternalOptionMemento.h deleted file mode 100644 index 88406894b..000000000 --- a/src/settings/InternalOptionMemento.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef STORM_SETTINGS_INTERNALOPTIONMEMENTO_H_ -#define STORM_SETTINGS_INTERNALOPTIONMEMENTO_H_ - -#include "src/settings/SettingsManager.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; - -namespace storm { -namespace settings { - - /*! - * @brief THIS CLASS IS FOR INTERNAL USE IN THE TESTS ONLY - * - * - * If an option is required to be set when executing a test - * the test may instantiate an Object of this class while the - * test itself is executed. - * - * This class ensures that the option has the requested value - * and resets it to its previous value as soon as its destructor - * is called. - */ - class InternalOptionMemento { - public: - /*! - * @brief The constructor. - * - * @param - */ - InternalOptionMemento(std::string const& longOptionName, bool requiredHasBeenSetState): instance(storm::settings::SettingsManager::getInstance()), optionName(longOptionName), stateRequired(requiredHasBeenSetState) { - this->stateBefore = instance->isSet(optionName); - if (stateRequired) { - instance->set(optionName); - } else { - instance->unset(optionName); - } - } - - /*! - * @brief The destructor. - * - * Resets the Options state to its previous state - */ - virtual ~InternalOptionMemento() { - // Reset the state of the Option - if (stateBefore) { - instance->set(optionName); - } else { - instance->unset(optionName); - } - this->instance = nullptr; - } - - - - private: - storm::settings::SettingsManager* instance; - std::string const optionName; - bool stateBefore; - bool stateRequired; - - InternalOptionMemento(InternalOptionMemento& other) {} - }; - -} // namespace settings -} // namespace storm - -#endif // STORM_SETTINGS_INTERNALOPTIONMEMENTO_H_ \ No newline at end of file diff --git a/src/settings/InternalSettingsMemento.h b/src/settings/InternalSettingsMemento.h new file mode 100644 index 000000000..48b3bce9f --- /dev/null +++ b/src/settings/InternalSettingsMemento.h @@ -0,0 +1,61 @@ +#ifndef STORM_SETTINGS_INTERNALSETTINGMEMENTO_H_ +#define STORM_SETTINGS_INTERNALSETTINGMEMENTO_H_ + +#include "src/settings/SettingsManager.h" +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" + +extern log4cplus::Logger logger; + +namespace storm { + namespace settings { + + /*! + * NOTE: THIS CLASS IS FOR INTERNAL USE IN THE TESTS ONLY. + * + * If an option is required to be set when executing a test the test may instantiate an object of this class + * while the test itself is executed. This object then ensures that the option has the requested value and + * resets it to its previous value as soon as its destructor is called. + */ + class InternalSettingMemento { + public: + /*! + * Constructs a new memento for the specified option. + * + * @param moduleName The name of the module that registered the option. + * @param longOptionName The long name of the option. + * @param requiredHasBeenSetState A flag that indicates whether the setting is to be temporarily set to + * true or false. + */ + InternalOptionMemento(std::string const& moduleName, std::string const& longOptionName, bool requiredHasBeenSetState): optionName(longOptionName), stateBefore() { + this->stateBefore = storm::settings::SettingsManager::manager().isSet(optionName); + if (requiredHasBeenSetState) { + storm::settings::SettingsManager::manager()..set(optionName); + } else { + storm::settings::SettingsManager::manager().unset(optionName); + } + } + + /*! + * Destructs the memento object and resets the value of the option to its original state. + */ + virtual ~InternalOptionMemento() { + if (stateBefore) { + storm::settings::SettingsManager::manager().set(optionName); + } else { + storm::settings::SettingsManager::manager().unset(optionName); + } + } + + private: + // The long name of the option that was temporarily set. + std::string const optionName; + + // The state of the option before it was set. + bool stateBefore; + }; + + } // namespace settings +} // namespace storm + +#endif // STORM_SETTINGS_INTERNALSETTINGMEMENTO_H_ \ No newline at end of file diff --git a/src/settings/Option.h b/src/settings/Option.h index 81ac9bfdf..ebec3acb8 100644 --- a/src/settings/Option.h +++ b/src/settings/Option.h @@ -1,10 +1,3 @@ -/* - * Option.h - * - * Created on: 11.08.2013 - * Author: Philipp Berger - */ - #ifndef STORM_SETTINGS_OPTION_H_ #define STORM_SETTINGS_OPTION_H_ @@ -22,234 +15,253 @@ #include "Argument.h" #include "src/utility/StringHelper.h" +#include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/IllegalArgumentException.h" #include "src/exceptions/OptionUnificationException.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - namespace storm { namespace settings { - class SettingsManager; - + /*! + * This class represents one command-line option. + */ class Option { public: - + // (Forward-)declare settings manager class as friend. friend class storm::settings::SettingsManager; - 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 (uint_fast64_t 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(); + /*! + * Creates an option with the given parameters. + * + * @param moduleName The module to which this option belongs. + * @param longOptionName The long option name. + * @param optionDescription The description of the option. + * @param isOptionRequired Sets whether the option is required to appear. + * @param requireModulePrefix A flag that indicates whether this option requires to be prefixed with the + * module name. + * @param optionArguments The arguments of the option. + */ + Option(std::string const& moduleName, std::string const& longOptionName, std::string const& optionDescription, bool isOptionRequired, bool requireModulePrefix, std::vector> const& optionArguments = std::vector>()) : Option(moduleName, longOptionName, "", false, optionDescription, isOptionRequired, requireModulePrefix, optionArguments) { + // Intentionally left empty. } + + /*! + * Creates an option with the given parameters. + * + * @param moduleName The module to which this option belongs. + * @param longOptionName The long option name. + * @param shortOptionName The short option name. + * @param optionDescription The description of the option. + * @param isOptionRequired Sets whether the option is required to appear. + * @param requireModulePrefix A flag that indicates whether this option requires to be prefixed with the + * module name. + * @param optionArguments The arguments of the option. + */ + Option(std::string const& moduleName, std::string const& longOptionName, std::string const& shortOptionName, std::string const& optionDescription, bool isOptionRequired, bool requireModulePrefix, std::vector> const& optionArguments = std::vector>()) : Option(moduleName, longOptionName, shortOptionName, true, optionDescription, isOptionRequired, requireModulePrefix, optionArguments) { + // Intentionally left empty. + } + + /*! + * Checks whether the given option is compatible with the current one. If not, an exception is thrown. + * + * @param other The other option with which to check compatibility. + * @return True iff the given argument is compatible with the current one. + */ + bool isCompatibleWith(Option const& other) { + LOG_THROW(this->getArgumentCount() == other.getArgumentCount(), storm::exceptions::OptionUnificationException, "Unable to unify two options, because their argument count differs."); - 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 (size_t 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) { - LOG4CPLUS_ERROR(logger, "Option::unify: Unable to unify option \"" << getLongName() << "\" because of mismatching names (\"" << getLongName() << "\" and \"" << other.getLongName() << "\")."); - throw storm::exceptions::OptionUnificationException() << "Unable to unify option \"" << getLongName() << "\" because of mismatching names (\"" << getLongName() << "\" and \"" << other.getLongName() << "\")."; - } - if (this->getShortName().compare(other.getShortName()) != 0) { - LOG4CPLUS_ERROR(logger, "Option::unify: Unable to unify option \"" << getLongName() << "\" because of mismatching names (\"" << getShortName() << "\" and \"" << other.getShortName() << "\")."); - throw storm::exceptions::OptionUnificationException() << "Unable to unify option \"" << getLongName() << "\" because of mismatching names (\"" << getShortName() << "\" and \"" << other.getShortName() << "\")."; - } - - if (this->getArgumentCount() != other.getArgumentCount()) { - LOG4CPLUS_ERROR(logger, "Option::unify: Unable to unify option \"" << getLongName() << "\" because of mismatching argument numbers."); - throw storm::exceptions::OptionUnificationException() << "Unable to unify option \"" << getLongName() << "\" because of mismatching argument numbers."; - } for(size_t i = 0; i != this->arguments.size(); i++) { - ArgumentBase* A = this->arguments.at(i).get(); - ArgumentBase* B = other.arguments.at(i).get(); + ArgumentBase const& firstArgument = this->getArgument(i); + ArgumentBase const& secondArgument = other.getArgument(i); - if (A->getArgumentType() != B->getArgumentType()) { - LOG4CPLUS_ERROR(logger, "Option::unify: Unable to unify option \"" << getLongName() << "\" because of mismatching argument types at index " << i << "."); - throw storm::exceptions::OptionUnificationException() << "Unable to unify option \"" << getLongName() << "\" because of mismatching argument types at index " << i << "."; - } + LOG_THROW(firstArgument.getArgumentType() == secondArgument.getArgumentType(), storm::exceptions::OptionUnificationException, "Unable to unify two options, because their arguments are incompatible."); - switch (A->getArgumentType()) { + switch (firstArgument->getArgumentType()) { case ArgumentType::String: - static_cast*>(A)->unify(*static_cast*>(B)); + static_cast&>(firstArgument).isCompatibleWith(static_cast&>(secondArgument)); break; case ArgumentType::Integer: - static_cast*>(A)->unify(*static_cast*>(B)); + static_cast&>(firstArgument).isCompatibleWith(static_cast&>(secondArgument)); break; case ArgumentType::UnsignedInteger: - static_cast*>(A)->unify(*static_cast*>(B)); + static_cast&>(firstArgument).isCompatibleWith(static_cast&>(secondArgument)); break; case ArgumentType::Double: - static_cast*>(A)->unify(*static_cast*>(B)); + static_cast&>(firstArgument).isCompatibleWith(static_cast&>(secondArgument)); break; case ArgumentType::Boolean: - static_cast*>(A)->unify(*static_cast*>(B)); + static_cast&>(firstArgument).isCompatibleWith(static_cast&>(secondArgument)); break; - default: - LOG4CPLUS_ERROR(logger, "Option::unify: Missing Case in ArgumentBuilder's switch/case Code."); - throw storm::exceptions::InternalTypeErrorException() << "Missing case in Option."; } } - - if (this->getModuleName().compare(other.getModuleName()) != 0) { - this->moduleName.append(", ").append(other.getModuleName()); - } + return true; } + /*! + * Retrieves the argument count this option expects. + * + * @return The argument count of this option. + */ uint_fast64_t getArgumentCount() const { return this->arguments.size(); } - ArgumentBase& getArgument(uint_fast64_t argumentIndex) const { - if (argumentIndex >= getArgumentCount()) { - LOG4CPLUS_ERROR(logger, "Option::getArgument: argumentIndex out of bounds!"); - throw storm::exceptions::IllegalArgumentException() << "Option::getArgument(): index of argument is out of bounds."; - } - return *this->arguments.at(argumentIndex).get(); + /*! + * Retrieves the i-th argument of this option. + * + * @param argumentIndex The index of the argument to retrieve. + * @return The i-th argument of this option. + */ + ArgumentBase const& getArgument(uint_fast64_t argumentIndex) const { + LOG_THROW(argumentIndex < this->getArgumentCount(), storm::exceptions::IllegalArgumentException, "Index of argument is out of bounds.") + return *this->arguments.at(argumentIndex); } /*! - * Returns a reference to the Argument with the specified longName. - * Throws an Exception of Type IllegalArgumentException if there is no such Option. - */ + * Returns a reference to the argument with the specified long name. + * + * @param argumentName The name of the argument to retrieve. + * @return The argument with the given name. + */ ArgumentBase const& getArgumentByName(std::string const& argumentName) const { auto argumentIterator = this->argumentNameMap.find(storm::utility::StringHelper::stringToLower(argumentName)); - - if (argumentIterator == this->argumentNameMap.end()) { - LOG4CPLUS_ERROR(logger, "Option::getArgumentByName: Unable to retrieve unknown argument \"" << argumentName << "\" of option \"" << this->getLongName() << "\"."); - throw storm::exceptions::IllegalArgumentException() << "Unable to retrieve unknown argument \"" << argumentName << "\" of option \"" << this->getLongName() << "\"."; - } - - return *argumentIterator->second.get(); + LOG_THROW(argumentIterator != this->argumentNameMap.end(), storm::exceptions::IllegalArgumentException, "Unable to retrieve argument with unknown name " << argumentName << "."); + return *argumentIterator->second; } + /*! + * Retrieves the long name of this option. + * + * @return The long name of this option. + */ std::string const& getLongName() const { return this->longName; } + /*! + * Retrieves whether this option has a short name. + * + * @return True iff the option has a short name. + */ + bool getHasShortName() const { + return this->hasShortName; + } + + /*! + * Retrieves the short name of this option. + * + * @return The short name of this option. + */ std::string const& getShortName() const { return this->shortName; } + /*! + * Retrieves the description of the option. + * + * @return The description of the option. + */ std::string const& getDescription() const { return this->description; } + /*! + * Retrieves the name of the module to which this option belongs. + * + * @return The name of the module to which this option belongs. + */ std::string const& getModuleName() const { return this->moduleName; } + /*! + * Retrieves whether the option is required. + * + * @return True iff the option is required. + */ bool getIsRequired() const { return this->isRequired; } + /*! + * Retrieves whether the option has been set. + * + * @return True iff the option has been set. + */ bool getHasOptionBeenSet() const { return this->hasBeenSet; } - void setHasOptionBeenSet(bool newValue = true) { - this->hasBeenSet = newValue; - } private: + // The long name of the option. std::string longName; + + // A flag that indicates whether the option has a short name. + bool hasShortName; + + // The short name of the option if any is set and an empty string otherwise. std::string shortName; + + // The description of the option. std::string description; + + // The name of the module to which this option belongs. std::string moduleName; + // A flag that indicates whether this option is required to appear. bool isRequired; + + // A flag that indicates whether this option is required to be prefixed with the module name. + bool requireModulePrefix; + + // A flag that indicates whether this option has been set. bool hasBeenSet; + // The arguments of this option (possibly empty). std::vector> arguments; + // A mapping from argument names of this option to the actual arguments. std::unordered_map> argumentNameMap; - void validateFields() const { - if (longName.empty()) { - LOG4CPLUS_ERROR(logger, "Option::validateFields: Unable to construct an option with empty name."); - throw storm::exceptions::IllegalArgumentException() << "Unable to construct an option with empty name."; - } - - if (moduleName.empty()) { - LOG4CPLUS_ERROR(logger, "Option::validateFields: Unable to construct an option with empty module."); - throw storm::exceptions::IllegalArgumentException() << "Unable to construct an option with empty module."; - } - - 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) { - LOG4CPLUS_ERROR(logger, "Option::validateFields: Unable to construct an option with a illegal name."); - throw storm::exceptions::IllegalArgumentException() << "Unable to construct an option with a illegal name."; - } - if (shortNameContainsNonAlpha) { - LOG4CPLUS_ERROR(logger, "Option::validateFields: Unable to construct an option with a illegal name."); - throw storm::exceptions::IllegalArgumentException() << "Unable to construct an option with a illegal name."; - } - } - - 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) { - LOG4CPLUS_ERROR(logger, "Option::isArgumentsVectorValid: The argument vector specified for option \"" << getLongName() << "\" is invalid, because it contains a non-optional argument after an optional one."); - throw storm::exceptions::IllegalArgumentException() << "The argument vector specified for option \"" << getLongName() << "\" is invalid, because it contains a non-optional argument after an optional one."; - } - std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(i->get()->getArgumentName()); - if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) { - LOG4CPLUS_ERROR(logger, "Option::isArgumentsVectorValid: The argument vector specified for option \"" << getLongName() << "\" is invalid, because it contains two arguments with the same name."); - throw storm::exceptions::IllegalArgumentException() << "The argument vector specified for option \"" << getLongName() << "\" is invalid, because it 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; - } + /*! + * Creates an option with the given parameters. + * + * @param moduleName The module to which this option belongs. + * @param longOptionName The long option name. + * @param shortOptionName The short option name. + * @param hasShortOptionName A flag that indicates whether this option has a short name. + * @param optionDescription The description of the option. + * @param isOptionRequired Sets whether the option is required to appear. + * @param requireModulePrefix A flag that indicates whether this option requires to be prefixed with the + * module name. + * @param optionArguments The arguments of the option. + */ + Option(std::string const& moduleName, std::string const& longOptionName, std::string const& shortOptionName, bool hasShortOptionName, std::string const& optionDescription, bool isOptionRequired, bool requireModulePrefix, std::vector> const& optionArguments = std::vector>()) : longName(longOptionName), hasShortName(hasShortOptionName), shortName(shortOptionName), description(optionDescription), moduleName(moduleName), isRequired(isOptionRequired), requireModulePrefix(requireModulePrefix), hasBeenSet(false), arguments(), argumentNameMap() { + + // First, do some sanity checks. + LOG_THROW(!longName.empty(), storm::exceptions::IllegalArgumentException, "Unable to construct option with empty name."); + LOG_THROW(!moduleName.empty(), storm::exceptions::IllegalArgumentException, "Unable to construct option with empty module name."); + + bool longNameContainsNonAlpha = std::find_if(longName.begin(), longName.end(), [](char c) { return !std::isalpha(c); }) != longName.end(); + LOG_THROW(!longNameContainsNonAlpha, storm::exceptions::IllegalArgumentException, "Unable to construct option with illegal name."); + + bool shortNameContainsNonAlpha = std::find_if(shortName.begin(), shortName.end(), [](char c) { return !std::isalpha(c); }) != shortName.end(); + LOG_THROW(!shortNameContainsNonAlpha, storm::exceptions::IllegalArgumentException, "Unable to construct option with illegal name."); + + // Then index all arguments. + for (auto const& argument : arguments) { + argumentNameMap.emplace(lowerArgumentName, std::shared_ptr(*i)); + } + } + + /*! + * Sets the flag that marks the option as being (un)set. + * + * @param newValue The new status of the flag. + */ + void setHasOptionBeenSet(bool newValue = true) { + this->hasBeenSet = newValue; + } }; } } diff --git a/src/settings/OptionBuilder.h b/src/settings/OptionBuilder.h index 0949deec9..a7b04f725 100644 --- a/src/settings/OptionBuilder.h +++ b/src/settings/OptionBuilder.h @@ -7,11 +7,13 @@ #include #include #include +#include #include "src/settings/ArgumentType.h" #include "src/settings/ArgumentBase.h" #include "src/settings/Option.h" +#include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/IllegalArgumentException.h" #include "src/exceptions/IllegalFunctionCallException.h" @@ -28,115 +30,96 @@ namespace storm { * * @param moduleName The name of the module to which this option belongs. * @param longName The long name of the option. - * @param shortName The short name of the option. If empty, the option does not have a short name. - * @param - * + * @param requireModulePrefix Sets whether this option can only be set by specifying the module name as its prefix. + * @param description A description that explains the purpose of this option. */ - OptionBuilder(std::string const& moduleName, std::string const& longName, std::string const& shortName, std::string const& description): longName(newOptionLongName), shortName(newOptionShortName), description(newOptionDescription), moduleName(newOptionModuleName), isRequired(false), isBuild(false) {} - - ~OptionBuilder() {} - - OptionBuilder& setLongName(std::string const& newLongName) { - this->longName = newLongName; - - return *this; - } + OptionBuilder(std::string const& moduleName, std::string const& longName, bool requireModulePrefix, std::string const& description) : longName(longName), shortName(""), hasShortName(false), description(description), moduleName(moduleName), requireModulePrefix(requireModulePrefix), isRequired(false), isBuild(false), arguments(), argumentNameSet() { + // Intentionally left empty. + } - std::string const& getLongName() const { - return this->longName; - } - - OptionBuilder& setShortName(std::string const& newShortName) { - this->shortName = newShortName; - - return *this; - } - - std::string const& getShortName() const { - return this->shortName; - } - - OptionBuilder& setDescription(std::string const& newDescription) { - this->description = newDescription; - + /*! + * Sets a short name for the option. + * + * @param shortName A short name for the option. + * @return A reference to the current builder. + */ + OptionBuilder& setShortName(std::string const& shortName) { + this->shortName = shortName; + this->hasShortName = true; return *this; } - std::string const& getDescription() const { - return this->description; - } - - OptionBuilder& setModuleName(std::string const& newModuleName) { - this->moduleName = newModuleName; - + /*! + * Sets whether the option is required. + * + * @param isRequired A flag indicating whether the option is required. + * @return A reference to the current builder. + */ + OptionBuilder& setIsRequired(bool isRequired) { + this->isRequired = isRequired; return *this; } - std::string const& getModuleName() const { - return this->moduleName; - } - - OptionBuilder& setIsRequired(bool newIsRequired) { - this->isRequired = newIsRequired; - - return *this; - } + /*! + * Adds the given argument to the arguments of this option. + * + * @param argument The argument to be added. + * @return A reference to the current builder. + */ + OptionBuilder& addArgument(std::shared_ptr argument) { + LOG_THROW(!this->isBuild, storm::exceptions::IllegalFunctionCallException, "Cannot add an argument to an option builder that was already used to build the option."); + LOG_THROW(this->arguments.empty() || !argument->getIsOptional() || this->arguments.back()->getIsOptional(), storm::exceptions::IllegalArgumentException, "Unable to add non-optional argument after an option that is optional."); - bool getIsRequired() const { - return this->isRequired; - } + std::string lowerArgumentName = boost::algorithm::to_lower_copy(argument->getName()); + LOG_THROW(argumentNamSet.find(lowerArgumentName) == argumentNameSet.end(), storm::exceptions::IllegalArgumentException, "Unable to add argument to option, because it already has an argument with the same name."); - OptionBuilder& addArgument(ArgumentBase* newArgument) { - // For automatic management of newArgument's lifetime - std::shared_ptr argumentPtr(newArgument); - if (this->isBuild) { - LOG4CPLUS_ERROR(logger, "OptionBuilder::addArgument: Illegal call to addArgument() on an instance of OptionBuilder that has already built an instance."); - throw storm::exceptions::IllegalFunctionCallException() << "Illegal call to addArgument() on an instance of OptionBuilder that has already built an instance."; - } - - if (newArgument->getArgumentType() == ArgumentType::Invalid) { - LOG4CPLUS_ERROR(logger, "OptionBuilder::addArgument: Unable to add argument to option \"" << getLongName() << "\" because its type is invalid."); - throw storm::exceptions::InternalTypeErrorException() << "Unable to add argument to option \"" << getLongName() << "\" because its type is invalid."; - } - - if (!newArgument->getIsOptional() && (this->arguments.size() > 0) && (this->arguments.at(this->arguments.size() - 1).get()->getIsOptional())) { - LOG4CPLUS_ERROR(logger, "OptionBuilder::addArgument: Unable to add a non-optional argument to option \"" << getLongName() << "\", because it already contains an optional argument."); - throw storm::exceptions::IllegalArgumentException() << "Unable to add non-optional argument to option \"" << getLongName() << "\", because it already contains an optional argument."; - } - - std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(newArgument->getArgumentName()); - if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) { - LOG4CPLUS_ERROR(logger, "OptionBuilder::addArgument: Unable to add argument with name \"" << newArgument->getArgumentName() << "\" to option \"" << getLongName() << "\", because it already contains an argument with the same name."); - throw storm::exceptions::IllegalArgumentException() << "Unable to add argument with name \"" << newArgument->getArgumentName() << "\" to option \"" << getLongName() << "\", because it already contains an argument with the same name."; - } argumentNameSet.insert(lowerArgumentName); - - this->arguments.push_back(std::shared_ptr(argumentPtr)); + this->arguments.push_back(argument); return *this; } - Option* build() { - if (this->isBuild) { - LOG4CPLUS_ERROR(logger, "OptionBuilder::addArgument: Illegal call to build() on an instance of OptionBuilder that has already built an instance."); - throw storm::exceptions::IllegalFunctionCallException() << "Illegal call to build() on an instance of OptionBuilder that has already built an instance."; - } - + /*! + * Builds an option from the data that was added to this builder. + * + * @return The resulting option. + */ + std::shared_ptr