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

259 lines
9.8 KiB

/*
* Option.h
*
* Created on: 11.08.2013
* Author: Philipp Berger
*/
#ifndef STORM_SETTINGS_OPTION_H_
#define STORM_SETTINGS_OPTION_H_
#include <iostream>
#include <string>
#include <cstdint>
#include <cctype>
#include <vector>
#include <memory>
#include <algorithm>
#include <unordered_set>
#include "ArgumentType.h"
#include "ArgumentBase.h"
#include "Argument.h"
#include "src/utility/StringHelper.h"
#include "src/exceptions/IllegalArgumentException.h"
#include "src/exceptions/OptionUnificationException.h"
namespace storm {
namespace settings {
class Settings;
class Option {
public:
friend class storm::settings::Settings;
/*
std::string longName;
std::string shortName;
std::string description;
std::string moduleName;
bool isRequired;
bool hasBeenSet;
std::vector<std::shared_ptr<ArgumentBase>> 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<std::shared_ptr<ArgumentBase>> 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<ArgumentBase>(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<ArgumentBase>(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<storm::settings::Argument<std::string>*>(A)->unify(*static_cast<storm::settings::Argument<std::string>*>(B));
break;
case ArgumentType::Integer:
static_cast<storm::settings::Argument<int_fast64_t>*>(A)->unify(*static_cast<storm::settings::Argument<int_fast64_t>*>(B));
break;
case ArgumentType::UnsignedInteger:
static_cast<storm::settings::Argument<uint_fast64_t>*>(A)->unify(*static_cast<storm::settings::Argument<uint_fast64_t>*>(B));
break;
case ArgumentType::Double:
static_cast<storm::settings::Argument<double>*>(A)->unify(*static_cast<storm::settings::Argument<double>*>(B));
break;
case ArgumentType::Boolean:
static_cast<storm::settings::Argument<bool>*>(A)->unify(*static_cast<storm::settings::Argument<bool>*>(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<std::shared_ptr<ArgumentBase>> arguments;
std::unordered_map<std::string, std::shared_ptr<ArgumentBase>> 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<std::shared_ptr<ArgumentBase>> const& arguments) {
bool lastEntryWasOptional = false;
std::unordered_set<std::string> argumentNameSet;
for (auto i = arguments.begin(); i != arguments.end(); ++i) {
bool isCurrentArgumentOptional = i->get()->getIsOptional();
//if (!this->isRequired && !i->get()->getHasDefaultValue()) {
// LOG
// throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains an argument without a default value, but the containing option is optional and therefor requires all arguments to provide default values.";
//}
if (!isCurrentArgumentOptional && lastEntryWasOptional) {
// LOG
throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains a non-optional argument AFTER an optional argument.";
}
std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(i->get()->getArgumentName());
if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) {
// LOG
throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains two arguments with the same name.";
}
argumentNameSet.insert(lowerArgumentName);
// This copies the Name to the Name Lookup Map
argumentNameMap.insert(std::make_pair(lowerArgumentName, std::shared_ptr<ArgumentBase>(*i)));
lastEntryWasOptional = isCurrentArgumentOptional;
}
return true;
}
};
}
}
#endif // STORM_SETTINGS_OPTION_H_