From bde10b750ed35fbf4f61262b71ddf015d7d48c8a Mon Sep 17 00:00:00 2001 From: PBerger Date: Thu, 5 Sep 2013 23:32:25 +0200 Subject: [PATCH] Added my initial implementation of Settings Former-commit-id: 5da4b7604f8a458f6473680141e60f0f3e51d1b9 --- CMakeLists.txt | 2 + src/exceptions/ArgumentUnificationException.h | 23 ++ src/exceptions/IllegalArgumentException.h | 18 ++ src/exceptions/IllegalArgumentTypeException.h | 23 ++ .../IllegalArgumentValueException.h | 23 ++ src/exceptions/IllegalFunctionCallException.h | 25 ++ src/exceptions/InternalTypeErrorException.h | 26 ++ src/exceptions/OptionParserException.h | 23 ++ src/exceptions/OptionUnificationException.h | 23 ++ src/settings/Argument.h | 259 ++++++++++++++++ src/settings/ArgumentBase.h | 110 +++++++ src/settings/ArgumentBuilder.h | 292 ++++++++++++++++++ src/settings/ArgumentType.h | 52 ++++ src/settings/ArgumentTypeInferationHelper.h | 152 +++++++++ src/settings/Option.h | 256 +++++++++++++++ src/settings/OptionBuilder.h | 142 +++++++++ src/settings/OptionsAccumulator.cpp | 48 +++ src/settings/OptionsAccumulator.h | 120 +++++++ src/settings/Settings.cpp | 195 ++++++++++++ src/settings/Settings.h | 210 +++++++++++++ src/storm.cpp | 4 +- src/utility/Settings.cpp | 234 -------------- src/utility/Settings.h | 266 ---------------- src/utility/StringHelper.h | 63 ++++ 24 files changed, 2087 insertions(+), 502 deletions(-) create mode 100644 src/exceptions/ArgumentUnificationException.h create mode 100644 src/exceptions/IllegalArgumentException.h create mode 100644 src/exceptions/IllegalArgumentTypeException.h create mode 100644 src/exceptions/IllegalArgumentValueException.h create mode 100644 src/exceptions/IllegalFunctionCallException.h create mode 100644 src/exceptions/InternalTypeErrorException.h create mode 100644 src/exceptions/OptionParserException.h create mode 100644 src/exceptions/OptionUnificationException.h create mode 100644 src/settings/Argument.h create mode 100644 src/settings/ArgumentBase.h create mode 100644 src/settings/ArgumentBuilder.h create mode 100644 src/settings/ArgumentType.h create mode 100644 src/settings/ArgumentTypeInferationHelper.h create mode 100644 src/settings/Option.h create mode 100644 src/settings/OptionBuilder.h create mode 100644 src/settings/OptionsAccumulator.cpp create mode 100644 src/settings/OptionsAccumulator.h create mode 100644 src/settings/Settings.cpp create mode 100644 src/settings/Settings.h delete mode 100644 src/utility/Settings.cpp delete mode 100644 src/utility/Settings.h create mode 100644 src/utility/StringHelper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 61968242b..3f4158c90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ file(GLOB_RECURSE STORM_MODELCHECKER_FILES ${PROJECT_SOURCE_DIR}/src/modelchecke file(GLOB_RECURSE STORM_MODELS_FILES ${PROJECT_SOURCE_DIR}/src/models/*.h ${PROJECT_SOURCE_DIR}/src/models/*.cpp) file(GLOB STORM_PARSER_FILES ${PROJECT_SOURCE_DIR}/src/parser/*.h ${PROJECT_SOURCE_DIR}/src/parser/*.cpp) file(GLOB_RECURSE STORM_PARSER_PRISMPARSER_FILES ${PROJECT_SOURCE_DIR}/src/parser/prismparser/*.h ${PROJECT_SOURCE_DIR}/src/parser/prismparser/*.cpp) +file(GLOB_RECURSE STORM_SETTINGS_FILES ${PROJECT_SOURCE_DIR}/src/settings/*.h ${PROJECT_SOURCE_DIR}/src/settings/*.cpp) file(GLOB_RECURSE STORM_STORAGE_FILES ${PROJECT_SOURCE_DIR}/src/storage/*.h ${PROJECT_SOURCE_DIR}/src/storage/*.cpp) file(GLOB_RECURSE STORM_UTILITY_FILES ${PROJECT_SOURCE_DIR}/src/utility/*.h ${PROJECT_SOURCE_DIR}/src/utility/*.cpp) file(GLOB STORM_IR_FILES ${PROJECT_SOURCE_DIR}/src/ir/*.h ${PROJECT_SOURCE_DIR}/src/ir/*.cpp) @@ -171,6 +172,7 @@ source_group(modelchecker FILES ${STORM_MODELCHECKER_FILES}) source_group(models FILES ${STORM_MODELS_FILES}) source_group(parser FILES ${STORM_PARSER_FILES}) source_group(parser\\prismparser FILES ${STORM_PARSER_PRISMPARSER_FILES}) +source_group(settings FILES ${STORM_SETTINGS_FILES}) source_group(storage FILES ${STORM_STORAGE_FILES}) source_group(utility FILES ${STORM_UTILITY_FILES}) source_group(ir FILES ${STORM_IR_FILES}) diff --git a/src/exceptions/ArgumentUnificationException.h b/src/exceptions/ArgumentUnificationException.h new file mode 100644 index 000000000..7f772819c --- /dev/null +++ b/src/exceptions/ArgumentUnificationException.h @@ -0,0 +1,23 @@ +/* + * ArgumentUnificationException.h + * + * Created on: 19.07.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_ARGUMENTUNIFICATIONEXCEPTION_H_ +#define STORM_EXCEPTIONS_ARGUMENTUNIFICATIONEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +STORM_EXCEPTION_DEFINE_NEW(ArgumentUnificationException) + +} + +} + +#endif /* STORM_EXCEPTIONS_ARGUMENTUNIFICATIONEXCEPTION_H_ */ diff --git a/src/exceptions/IllegalArgumentException.h b/src/exceptions/IllegalArgumentException.h new file mode 100644 index 000000000..baa1581de --- /dev/null +++ b/src/exceptions/IllegalArgumentException.h @@ -0,0 +1,18 @@ +#ifndef STORM_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_ +#define STORM_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +/*! + * @brief This exception is thrown when a parameter is invalid or illegal in this context + */ +STORM_EXCEPTION_DEFINE_NEW(IllegalArgumentException) + +} // namespace exceptions + +} // namespace storm +#endif // STORM_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_ diff --git a/src/exceptions/IllegalArgumentTypeException.h b/src/exceptions/IllegalArgumentTypeException.h new file mode 100644 index 000000000..8a5f242da --- /dev/null +++ b/src/exceptions/IllegalArgumentTypeException.h @@ -0,0 +1,23 @@ +/* + * IllegalArgumentTypeException.h + * + * Created on: 10.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_ILLEGALARGUMENTTYPEEXCEPTION_H_ +#define STORM_EXCEPTIONS_ILLEGALARGUMENTTYPEEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +STORM_EXCEPTION_DEFINE_NEW(IllegalArgumentTypeException) + +} + +} + +#endif /* STORM_EXCEPTIONS_ILLEGALARGUMENTTYPEEXCEPTION_H_ */ diff --git a/src/exceptions/IllegalArgumentValueException.h b/src/exceptions/IllegalArgumentValueException.h new file mode 100644 index 000000000..28b14af79 --- /dev/null +++ b/src/exceptions/IllegalArgumentValueException.h @@ -0,0 +1,23 @@ +/* + * IllegalArgumentValueException.h + * + * Created on: 10.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_ILLEGALARGUMENTVALUEEXCEPTION_H_ +#define STORM_EXCEPTIONS_ILLEGALARGUMENTVALUEEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +STORM_EXCEPTION_DEFINE_NEW(IllegalArgumentValueException) + +} + +} + +#endif /* STORM_EXCEPTIONS_ILLEGALARGUMENTVALUEEXCEPTION_H_ */ diff --git a/src/exceptions/IllegalFunctionCallException.h b/src/exceptions/IllegalFunctionCallException.h new file mode 100644 index 000000000..0515e1c00 --- /dev/null +++ b/src/exceptions/IllegalFunctionCallException.h @@ -0,0 +1,25 @@ +/* + * IllegalFunctionCallException.h + * + * Created on: 09.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_ILLEGALFUNCTIONCALLEXCEPTION_H_ +#define STORM_EXCEPTIONS_ILLEGALFUNCTIONCALLEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +/*! + * @brief This exception is thrown when a function call is not allowed in this context + */ +STORM_EXCEPTION_DEFINE_NEW(IllegalFunctionCallException) + +} // namespace exceptions + +} // namespace storm +#endif // STORM_EXCEPTIONS_ILLEGALFUNCTIONCALLEXCEPTION_H_ diff --git a/src/exceptions/InternalTypeErrorException.h b/src/exceptions/InternalTypeErrorException.h new file mode 100644 index 000000000..ed1f609ba --- /dev/null +++ b/src/exceptions/InternalTypeErrorException.h @@ -0,0 +1,26 @@ +/* + * InternalTypeErrorException.h + * + * Created on: 09.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_INTERNALTYPEERROREXCEPTION_H_ +#define STORM_EXCEPTIONS_INTERNALTYPEERROREXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +/*! + * @brief This exception is thrown when an internal function observes an unknown state or type, e.g. a missing case statement. + */ +STORM_EXCEPTION_DEFINE_NEW(InternalTypeErrorException) + +} + +} + +#endif /* STORM_EXCEPTIONS_INTERNALTYPEERROREXCEPTION_H_ */ diff --git a/src/exceptions/OptionParserException.h b/src/exceptions/OptionParserException.h new file mode 100644 index 000000000..d0befae51 --- /dev/null +++ b/src/exceptions/OptionParserException.h @@ -0,0 +1,23 @@ +/* + * OptionParserException.h + * + * Created on: 23.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_OPTIONPARSEREXCEPTION_H_ +#define STORM_EXCEPTIONS_OPTIONPARSEREXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +STORM_EXCEPTION_DEFINE_NEW(OptionParserException) + +} + +} + +#endif /* STORM_EXCEPTIONS_OPTIONPARSEREXCEPTION_H_ */ diff --git a/src/exceptions/OptionUnificationException.h b/src/exceptions/OptionUnificationException.h new file mode 100644 index 000000000..6eac306e7 --- /dev/null +++ b/src/exceptions/OptionUnificationException.h @@ -0,0 +1,23 @@ +/* + * OptionUnificationException.h + * + * Created on: 21.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_EXCEPTIONS_OPTIONUNIFICATIONEXCEPTION_H_ +#define STORM_EXCEPTIONS_OPTIONUNIFICATIONEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + +namespace exceptions { + +STORM_EXCEPTION_DEFINE_NEW(OptionUnificationException) + +} + +} + +#endif /* STORM_EXCEPTIONS_OPTIONUNIFICATIONEXCEPTION_H_ */ diff --git a/src/settings/Argument.h b/src/settings/Argument.h new file mode 100644 index 000000000..cef2209fc --- /dev/null +++ b/src/settings/Argument.h @@ -0,0 +1,259 @@ +#ifndef STORM_SETTINGS_ARGUMENT_H_ +#define STORM_SETTINGS_ARGUMENT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/settings/ArgumentBase.h" +#include "src/settings/ArgumentType.h" +#include "src/settings/ArgumentTypeInferationHelper.h" +#include "src/utility/StringHelper.h" +#include "src/exceptions/ArgumentUnificationException.h" +#include "src/exceptions/IllegalArgumentValueException.h" +#include "src/exceptions/IllegalFunctionCallException.h" + +namespace storm { + namespace settings { + template + class Argument : public ArgumentBase { + public: + typedef std::function userValidationFunction_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) { + throw storm::exceptions::IllegalArgumentException() << "Error: 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), hasDefaultValue(true), defaultValue(defaultValue) { + + } + + Argument(Argument const& other) : ArgumentBase(other.argumentName, other.argumentDescription, other.isOptional), argumentType(other.argumentType), hasDefaultValue(other.hasDefaultValue), defaultValue(other.defaultValue) { + this->userValidationFunction.reserve(other.userValidationFunction.size()); + for (auto i = 0; i < other.userValidationFunction.size(); ++i) { + this->userValidationFunction.push_back(userValidationFunction_t(other.userValidationFunction.at(i))); + } + } + + virtual ~Argument() { + std::cout << "Destructing an Argument: " << this->getArgumentName() << " of Type " << ArgumentTypeHelper::toString(this->getArgumentType()) << std::endl; + + this->userValidationFunction.clear(); + this->argumentType = ArgumentType::Invalid; + } + + virtual ArgumentBase* clone() const override { + return new Argument(*this); + } + + assignmentResult_t fromStringValue(std::string const& fromStringValue) override { + bool conversionOk = false; + T newValue = this->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 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); + } + this->argumentValue = newValue; + this->hasBeenSet = true; + return std::make_pair(true, "Success"); + } + + virtual ArgumentType getArgumentType() const override { + return this->argumentType; + } + + template + void unify(Argument &rhs) { + if (this->getArgumentType() != rhs.getArgumentType()) { + // LOG + throw storm::exceptions::ArgumentUnificationException() << "Error while unifying Argument \"" << this->getArgumentName() << "\" and Argument \"" << rhs.getArgumentName() << "\": Type Missmatch: \"" << ArgumentTypeHelper::toString(this->getArgumentType()) << "\" against \"" << ArgumentTypeHelper::toString(rhs.getArgumentType()) << "\""; + } + + if (this->getIsOptional() != rhs.getIsOptional()) { + // LOG + throw storm::exceptions::ArgumentUnificationException() << "Error while unifying Argument \"" << this->getArgumentName() << "\" and Argument \"" << rhs.getArgumentName() << "\": IsOptional Missmatch!"; + } + + if (this->getHasDefaultValue() != rhs.getHasDefaultValue()) { + // LOG + throw storm::exceptions::ArgumentUnificationException() << "Error while unifying Argument \"" << this->getArgumentName() << "\" and Argument \"" << rhs.getArgumentName() << "\": defaultValue Missmatch!"; + } + + if (this->getArgumentDescription().compare(rhs.getArgumentDescription()) != 0) { + // LOG Warning: Descriptions of unified arguments do not match + } + + if (this->getArgumentName().compare(rhs.getArgumentName()) != 0) { + // LOG Warning: Names of unified arguments do not match + } + + // 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()) { + // LOG + throw storm::exceptions::IllegalFunctionCallException() << "Error: Called getArgumentValue() on Argument \"" << this->getArgumentName() << "\", but it was never set and does not contain a default value."; + } + return this->argumentValue; + } + + virtual bool getHasDefaultValue() const override { + return this->hasDefaultValue; + } + + std::string const& getDefaultValue() { + return this->defaultValue; + } + + void setFromDefaultValue() override { + if (!this->hasDefaultValue) { + throw storm::exceptions::IllegalFunctionCallException() << "Error: The Argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << ") was asked to set its default value but none was set!"; + } + // this call also sets the hasBeenSet flag + assignmentResult_t result = this->fromTypeValue(this->defaultValue); + if (!result.first) { + throw storm::exceptions::IllegalArgumentValueException() << "Error: While parsing a given configuration the Argument \"" << this->getArgumentName() << "\" (" << this->getArgumentDescription() << ") could not receive its Default Value as it was rejected by its Validation Functions with message: " << result.second; + } + } + + virtual std::string getValueAsString() const override { + switch (this->argumentType) { + case ArgumentType::String: + return ArgumentTypeInferation::inferToString(ArgumentType::String, this->argumentValue); + case ArgumentType::Boolean: { + bool iValue = ArgumentTypeInferation::inferToBoolean(ArgumentType::Boolean, this->argumentValue); + if (iValue) { + return "true"; + } else { + return "false"; + } + + } + default: + return this->convertToString(this->argumentValue); + + } + } + + virtual int_fast64_t getValueAsInteger() const override { + switch (this->argumentType) { + case ArgumentType::Integer: + return ArgumentTypeInferation::inferToInteger(ArgumentType::Integer, this->argumentValue); + default: + throw storm::exceptions::IllegalFunctionCallException() << "Error: getValueAsInteger() was called on Argument \"" << getArgumentName() << "\" of Type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\"!"; + + } + } + + virtual uint_fast64_t getValueAsUnsignedInteger() const override { + switch (this->argumentType) { + case ArgumentType::UnsignedInteger: + return ArgumentTypeInferation::inferToUnsignedInteger(ArgumentType::UnsignedInteger, this->argumentValue); + default: + throw storm::exceptions::IllegalFunctionCallException() << "Error: getValueAsUnsignedInteger() was called on Argument \"" << getArgumentName() << "\" of Type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\"!"; + + } + } + + virtual double getValueAsDouble() const override { + switch (this->argumentType) { + case ArgumentType::Double: + return ArgumentTypeInferation::inferToDouble(ArgumentType::Double, this->argumentValue); + default: + throw storm::exceptions::IllegalFunctionCallException() << "Error: getValueAsDouble() was called on Argument \"" << getArgumentName() << "\" of Type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\"!"; + + } + } + + virtual bool getValueAsBoolean() const override { + switch (this->argumentType) { + case ArgumentType::Boolean: + return ArgumentTypeInferation::inferToBoolean(ArgumentType::Boolean, this->argumentValue); + default: + throw storm::exceptions::IllegalFunctionCallException() << "Error: getValueAsBoolean() was called on Argument \"" << getArgumentName() << "\" of Type \"" << ArgumentTypeHelper::toString(getArgumentType()) << "\"!"; + + } + } + 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. + // LOG + throw storm::exceptions::IllegalArgumentValueException() << "Illegal Default Value for Argument \"" << this->getArgumentName() << "\".\nThe 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; + } + + + }; + } +} + +#endif // STORM_SETTINGS_ARGUMENT_H_ \ No newline at end of file diff --git a/src/settings/ArgumentBase.h b/src/settings/ArgumentBase.h new file mode 100644 index 000000000..a7b3b888a --- /dev/null +++ b/src/settings/ArgumentBase.h @@ -0,0 +1,110 @@ +#ifndef STORM_SETTINGS_ARGUMENTBASE_H_ +#define STORM_SETTINGS_ARGUMENTBASE_H_ + +#include +#include + +#include "ArgumentType.h" +#include "src/utility/StringHelper.h" + +namespace storm { + namespace settings { + + typedef std::pair assignmentResult_t; + + class ArgumentBase { + public: + ArgumentBase(std::string const& argumentName, std::string const& argumentDescription, bool isOptional) : isOptional(isOptional), hasBeenSet(false), argumentName(argumentName), argumentDescription(argumentDescription) {} + virtual ~ArgumentBase() { + std::cout << "Destructing an ArgumentBase." << std::endl; + } + virtual ArgumentType getArgumentType() const = 0; + + virtual bool getIsOptional() const { + return this->isOptional; + } + + std::string const& getArgumentName() const { + return this->argumentName; + } + + std::string const& getArgumentDescription() const { + return this->argumentDescription; + } + + virtual bool getHasDefaultValue() const = 0; + virtual bool getHasBeenSet() const { + return this->hasBeenSet; + } + + virtual void setFromDefaultValue() = 0; + virtual assignmentResult_t fromStringValue(std::string const& fromStringValue) = 0; + virtual ArgumentBase* clone() const = 0; + + virtual std::string getValueAsString() const = 0; + virtual int_fast64_t getValueAsInteger() const = 0; + virtual uint_fast64_t getValueAsUnsignedInteger() const = 0; + virtual double getValueAsDouble() const = 0; + virtual bool getValueAsBoolean() const = 0; + protected: + bool isOptional; + 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() {} + }; + }; + + 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; + } + } +} + +#endif // STORM_SETTINGS_ARGUMENTBASE_H_ \ No newline at end of file diff --git a/src/settings/ArgumentBuilder.h b/src/settings/ArgumentBuilder.h new file mode 100644 index 000000000..c134e4418 --- /dev/null +++ b/src/settings/ArgumentBuilder.h @@ -0,0 +1,292 @@ +#ifndef STORM_SETTINGS_ARGUMENTBUILDER_H_ +#define STORM_SETTINGS_ARGUMENTBUILDER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ArgumentType.h" +#include "ArgumentTypeInferationHelper.h" +#include "ArgumentBase.h" +#include "Argument.h" + +#include "src/exceptions/IllegalFunctionCallException.h" +#include "src/exceptions/IllegalArgumentTypeException.h" + +namespace storm { + namespace settings { + + class ArgumentBuilder { + public: + ~ArgumentBuilder() {} + + 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 <= " << value; + errorMessageTarget.append(stream.str()); + } + if (!upperBoundCondition) { + std::ostringstream stream; + stream << " Upper Bound Condition not met: " << value << " is not <= " << upperBound; + errorMessageTarget.append(stream.str()); + } + return (lowerBoundCondition && upperBoundCondition); + }, lowerBound, upperBound, std::placeholders::_1, std::placeholders::_2); + } + 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 < " << value; + errorMessageTarget.append(stream.str()); + } + if (!upperBoundCondition) { + std::ostringstream stream; + stream << " Upper Bound Condition not met: " << value << " is not < " << upperBound; + errorMessageTarget.append(stream.str()); + } + return (lowerBoundCondition && upperBoundCondition); + }, lowerBound, upperBound, std::placeholders::_1, std::placeholders::_2); + } + + /* + 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); + return ab; + } + + static ArgumentBuilder createIntegerArgument(std::string const& argumentName, std::string const& argumentDescription) { + ArgumentBuilder ab(ArgumentType::Integer, argumentName, argumentDescription); + return ab; + } + + static ArgumentBuilder createUnsignedIntegerArgument(std::string const& argumentName, std::string const& argumentDescription) { + ArgumentBuilder ab(ArgumentType::UnsignedInteger, argumentName, argumentDescription); + return ab; + } + + static ArgumentBuilder createDoubleArgument(std::string const& argumentName, std::string const& argumentDescription) { + ArgumentBuilder ab(ArgumentType::Double, argumentName, argumentDescription); + return ab; + } + + static ArgumentBuilder createBooleanArgument(std::string const& argumentName, std::string const& argumentDescription) { + ArgumentBuilder ab(ArgumentType::Boolean, argumentName, argumentDescription); + return ab; + } + + ArgumentBuilder& setName(std::string const& newName) { + this->argumentName = newName; + return *this; + } + + ArgumentBuilder& setDescription(std::string const& newDescription) { + this->argumentDescription = newDescription; + return *this; + } + + 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() << "Error: You tried adding a Validation-Function for a \"" << ArgumentTypeHelper::toString(ArgumentType::funcName) << "\" Argument, but this Argument is configured to be 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() << "Error: You tried adding a Validation-Function for an Argument which has a Default Value set which is rejected by this Validation-Function:\r\n" << errorMessageTarget; \ + } \ + return *this; \ + } + + 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() << "Error: You tried adding a default Value for a \"" << ArgumentTypeHelper::toString(ArgumentType::String) << "\" Argument, but this Argument is configured to be of Type \"" << ArgumentTypeHelper::toString(this->argumentType) << "\"."; \ + } \ + PPCAT(this->defaultValue_, funcName) = defaultValue; \ + std::string errorMessageTarget = ""; \ + if (!this->validateDefaultForEach(errorMessageTarget)) { \ + throw storm::exceptions::IllegalArgumentValueException() << "Error: You tried adding a default Value for an Argument, but a Validation Function rejected it:\r\n" << errorMessageTarget; \ + } \ + this->hasDefaultValue = true; \ + return *this; \ + } + + 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) { + // LOG + throw storm::exceptions::IllegalFunctionCallException() << "Error: Called build() on an instance of ArgumentBuilder which has already build an Instance."; + } + this->hasBeenBuild = 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)); + } else { + return dynamic_cast(new Argument(this->argumentName, this->argumentDescription, userValidationFunction_String, this->isOptional)); + } + break; + } + case ArgumentType::Integer: + if (this->hasDefaultValue) { + return dynamic_cast(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)); + } + break; + case ArgumentType::UnsignedInteger: + if (this->hasDefaultValue) { + return dynamic_cast(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)); + } + break; + case ArgumentType::Double: + if (this->hasDefaultValue) { + return dynamic_cast(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)); + } + break; + case ArgumentType::Boolean: + if (this->hasDefaultValue) { + return dynamic_cast(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)); + } + break; + default: + // LOG + throw storm::exceptions::InternalTypeErrorException() << "Error: 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 (auto i = 0; i < userValidationFunction_String.size(); ++i) { + this->userValidationFunction_String.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_String.at(i))); + } + for (auto i = 0; i < userValidationFunction_Integer.size(); ++i) { + this->userValidationFunction_Integer.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Integer.at(i))); + } + for (auto i = 0; i < userValidationFunction_UnsignedInteger.size(); ++i) { + this->userValidationFunction_UnsignedInteger.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_UnsignedInteger.at(i))); + } + for (auto i = 0; i < userValidationFunction_Double.size(); ++i) { + this->userValidationFunction_Double.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Double.at(i))); + } + for (auto i = 0; i < userValidationFunction_Boolean.size(); ++i) { + this->userValidationFunction_Boolean.push_back(storm::settings::Argument::userValidationFunction_t(other.userValidationFunction_Boolean.at(i))); + } + } + + 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; + + bool isOptional; + + 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: + // LOG + throw storm::exceptions::InternalTypeErrorException() << "Error: Missing Case in ArgumentBuilder's switch/case Code."; + } + + return result; + } + }; + } +} + +#endif // STORM_SETTINGS_ARGUMENTBUILDER_H_ \ No newline at end of file diff --git a/src/settings/ArgumentType.h b/src/settings/ArgumentType.h new file mode 100644 index 000000000..c93f4d293 --- /dev/null +++ b/src/settings/ArgumentType.h @@ -0,0 +1,52 @@ +#ifndef STORM_SETTINGS_ARGUMENTTYPE_H_ +#define STORM_SETTINGS_ARGUMENTTYPE_H_ + +#include "src/exceptions/InternalTypeErrorException.h" + +namespace storm { + namespace settings { + enum class ArgumentType { + Invalid, 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: + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n" << "Missing a Switch Case in the ArgumentTypeHelper!\n" << "It seems there is a new ArgumentType, but it was not added to the Helper Class!"; + } + } + private: + ArgumentTypeHelper() {} + ~ArgumentTypeHelper() {} + }; + } +} + +#endif // STORM_SETTINGS_ARGUMENTTYPE_H_ \ No newline at end of file diff --git a/src/settings/ArgumentTypeInferationHelper.h b/src/settings/ArgumentTypeInferationHelper.h new file mode 100644 index 000000000..2f452143e --- /dev/null +++ b/src/settings/ArgumentTypeInferationHelper.h @@ -0,0 +1,152 @@ +/* + * ArgumentTypeInferationHelper.h + * + * Created on: 19.07.2013 + * Author: Philipp Berger + * Static Lookup Helper that detects whether the given Template Type is valid. + */ + +#ifndef STORM_SETTINGS_ARGUMENTTYPEINFERATIONHELPER_H_ +#define STORM_SETTINGS_ARGUMENTTYPEINFERATIONHELPER_H_ + +#include +#include + +#include "ArgumentType.h" +#include "src/exceptions/InternalTypeErrorException.h" + +namespace storm { + namespace settings { + class ArgumentTypeInferation { + public: + // Specialized function template that infers the Type of T to our local enum + template + static ArgumentType inferToEnumType(); + + // Specialized function templates that allow casting using the Enum Class as Target + template static std::string inferToString(ArgumentType argumentType, T value); + template static int_fast64_t inferToInteger(ArgumentType argumentType, T value); + template static uint_fast64_t inferToUnsignedInteger(ArgumentType argumentType, T value); + template static double inferToDouble(ArgumentType argumentType, T value); + template static bool inferToBoolean(ArgumentType argumentType, T value); + + private: + ArgumentTypeInferation(); + ~ArgumentTypeInferation(); + }; + + /* + * All functions related to the EnumType Inferation from the Template Parameter + */ + template + ArgumentType ArgumentTypeInferation::inferToEnumType() { + // "Missing Template Specialization Case in ArgumentTypeInferation" + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n" << "Missing a Template Specialization Case in the ArgumentTypeInferationHelper!\n" << "It seems you tried to use a new, non-standard Type as a Settings Parameter-Type!"; + + return ArgumentType::Invalid; + } + + template <> inline ArgumentType ArgumentTypeInferation::inferToEnumType() { + return ArgumentType::String; + } + template <> inline ArgumentType ArgumentTypeInferation::inferToEnumType() { + return ArgumentType::Integer; + } + template <> inline ArgumentType ArgumentTypeInferation::inferToEnumType() { + return ArgumentType::UnsignedInteger; + } + template <> inline ArgumentType ArgumentTypeInferation::inferToEnumType() { + return ArgumentType::Double; + } + template <> inline ArgumentType ArgumentTypeInferation::inferToEnumType() { + return ArgumentType::Boolean; + } + + /* + * All functions related to the conversion to std::string based on the Template and Enum Type + */ + template + std::string ArgumentTypeInferation::inferToString(ArgumentType argumentType, T value) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToString was called on a non-string Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + + return std::string(); + } + + template <> inline std::string ArgumentTypeInferation::inferToString(ArgumentType argumentType, std::string value) { + if (argumentType != ArgumentType::String) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToString was called on a string Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + } + return value; + } + + /* + * All functions related to the conversion to int_fast64_t based on the Template and Enum Type + */ + template + int_fast64_t ArgumentTypeInferation::inferToInteger(ArgumentType argumentType, T value) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToInteger was called on a non-int_fast64_t Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + + return 0; + } + + template <> inline int_fast64_t ArgumentTypeInferation::inferToInteger(ArgumentType argumentType, int_fast64_t value) { + if (argumentType != ArgumentType::Integer) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToInteger was called on an int_fast64_t Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + } + return value; + } + + /* + * All functions related to the conversion to uint_fast64_t based on the Template and Enum Type + */ + template + uint_fast64_t ArgumentTypeInferation::inferToUnsignedInteger(ArgumentType argumentType, T value) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToUnsignedInteger was called on a non-uint_fast64_t Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + + return 0; + } + + template <> inline uint_fast64_t ArgumentTypeInferation::inferToUnsignedInteger(ArgumentType argumentType, uint_fast64_t value) { + if (argumentType != ArgumentType::UnsignedInteger) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToUnsignedInteger was called on an uint_fast64_t Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + } + return value; + } + + /* + * All functions related to the conversion to double based on the Template and Enum Type + */ + template + double ArgumentTypeInferation::inferToDouble(ArgumentType argumentType, T value) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToDouble was called on a non-double Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + + return 0.0; + } + + template <> inline double ArgumentTypeInferation::inferToDouble(ArgumentType argumentType, double value) { + if (argumentType != ArgumentType::Double) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToDouble was called on a double Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + } + return value; + } + + /* + * All functions related to the conversion to bool based on the Template and Enum Type + */ + template + bool ArgumentTypeInferation::inferToBoolean(ArgumentType argumentType, T value) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToBoolean was called on a non-bool Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + + return false; + } + + template <> inline bool ArgumentTypeInferation::inferToBoolean(ArgumentType argumentType, bool value) { + if (argumentType != ArgumentType::Boolean) { + throw storm::exceptions::InternalTypeErrorException() << "ERROR:\n " << "inferToBoolean was called on a bool Template Object to cast to " << ArgumentTypeHelper::toString(argumentType) << "!"; + } + return value; + } + } +} + +#endif // STORM_SETTINGS_ARGUMENTTYPEINFERATIONHELPER_H_ \ No newline at end of file diff --git a/src/settings/Option.h b/src/settings/Option.h new file mode 100644 index 000000000..265724f3f --- /dev/null +++ b/src/settings/Option.h @@ -0,0 +1,256 @@ +/* + * 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 Option { + public: + /* + 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 Option is optional, all arguments must have default values + 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_ \ No newline at end of file diff --git a/src/settings/OptionBuilder.h b/src/settings/OptionBuilder.h new file mode 100644 index 000000000..16df661bd --- /dev/null +++ b/src/settings/OptionBuilder.h @@ -0,0 +1,142 @@ +/* + * OptionBuilder.h + * + * Created on: 11.08.2013 + * Author: Philipp Berger + */ + +#ifndef STORM_SETTINGS_OPTIONBUILDER_H_ +#define STORM_SETTINGS_OPTIONBUILDER_H_ + +#include +#include +#include +#include +#include +#include + +#include "ArgumentType.h" +#include "ArgumentBase.h" +#include "Option.h" + +#include "src/exceptions/IllegalArgumentException.h" +#include "src/exceptions/IllegalFunctionCallException.h" + +namespace storm { + namespace settings { + + class OptionBuilder { + public: + OptionBuilder(std::string const& newOptionModuleName, std::string const& newOptionLongName, std::string const& newOptionShortName, std::string const& newOptionDescription): longName(newOptionLongName), shortName(newOptionShortName), description(newOptionDescription), moduleName(newOptionModuleName), isRequired(false), isBuild(false) {} + + ~OptionBuilder() { + std::cout << "Destructing an OptionBuilder." << std::endl; + } + + OptionBuilder& setLongName(std::string const& newLongName) { + this->longName = newLongName; + + return *this; + } + + 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; + + return *this; + } + + std::string const& getDescription() const { + return this->description; + } + + OptionBuilder& setModuleName(std::string const& newModuleName) { + this->moduleName = newModuleName; + + return *this; + } + + std::string const& getModuleName() const { + return this->moduleName; + } + + OptionBuilder& setIsRequired(bool newIsRequired) { + this->isRequired = newIsRequired; + + return *this; + } + + bool getIsRequired() const { + return this->isRequired; + } + + OptionBuilder& addArgument(ArgumentBase* newArgument) { + // For automatic management of newArgument's lifetime + std::shared_ptr argumentPtr(newArgument); + if (this->isBuild) { + // LOG + throw storm::exceptions::IllegalFunctionCallException() << "Error: Called addArgument() on an instance of OptionBuilder which has already build an Instance."; + } + + if (newArgument->getArgumentType() == ArgumentType::Invalid) { + // LOG + throw storm::exceptions::InternalTypeErrorException() << "Error: Could not 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())) { + // LOG + throw storm::exceptions::IllegalArgumentException() << "Error: Could not add Non-Optional Argument to Option \"" << getLongName() << "\" because it already contains an optional argument! Please note that after an optional argument has been added only arguments which are also optional can be appended."; + } + + std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(newArgument->getArgumentName()); + if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) { + // LOG + throw storm::exceptions::IllegalArgumentException() << "Error: Could not add Argument with Name \"" << newArgument->getArgumentName() << "\" to Option \"" << getLongName() << "\" because it already contains an argument with the same name! Please note that all argument names must be unique in its respective option."; + } + argumentNameSet.insert(lowerArgumentName); + + this->arguments.push_back(std::shared_ptr(argumentPtr)); + + return *this; + } + + Option* build() { + if (this->isBuild) { + // LOG + throw storm::exceptions::IllegalFunctionCallException() << "Error: Called build() on an instance of OptionBuilder which has already build an Instance."; + } + + this->isBuild = true; + + return new storm::settings::Option(this->moduleName, this->longName, this->shortName, this->description, this->isRequired, this->arguments); + } + private: + std::string longName; + std::string shortName; + std::string description; + std::string moduleName; + + bool isRequired; + bool isBuild; + + std::vector> arguments; + + std::unordered_set argumentNameSet; + }; + } +} + +#endif // STORM_SETTINGS_OPTIONBUILDER_H_ \ No newline at end of file diff --git a/src/settings/OptionsAccumulator.cpp b/src/settings/OptionsAccumulator.cpp new file mode 100644 index 000000000..f89aed47b --- /dev/null +++ b/src/settings/OptionsAccumulator.cpp @@ -0,0 +1,48 @@ +#include "src/settings/OptionsAccumulator.h" + +/*! +* The map holding the information regarding registered options and their types +*/ +//std::unordered_map> options; + +/*! +* The map holding the information regarding registered options and their short names +*/ +//std::unordered_map shortNames; + +storm::settings::OptionsAccumulator& storm::settings::OptionsAccumulator::addOption(Option* option) { + // For automatic management of option's lifetime + std::shared_ptr