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.

258 lines
9.8 KiB

  1. /*
  2. * Option.h
  3. *
  4. * Created on: 11.08.2013
  5. * Author: Philipp Berger
  6. */
  7. #ifndef STORM_SETTINGS_OPTION_H_
  8. #define STORM_SETTINGS_OPTION_H_
  9. #include <iostream>
  10. #include <string>
  11. #include <cstdint>
  12. #include <cctype>
  13. #include <vector>
  14. #include <memory>
  15. #include <algorithm>
  16. #include <unordered_set>
  17. #include "ArgumentType.h"
  18. #include "ArgumentBase.h"
  19. #include "Argument.h"
  20. #include "src/utility/StringHelper.h"
  21. #include "src/exceptions/IllegalArgumentException.h"
  22. #include "src/exceptions/OptionUnificationException.h"
  23. namespace storm {
  24. namespace settings {
  25. class Settings;
  26. class Option {
  27. public:
  28. friend class storm::settings::Settings;
  29. /*
  30. std::string longName;
  31. std::string shortName;
  32. std::string description;
  33. std::string moduleName;
  34. bool isRequired;
  35. bool hasBeenSet;
  36. std::vector<std::shared_ptr<ArgumentBase>> arguments;
  37. */
  38. Option(std::string const& moduleName, std::string const& longOptionName, std::string const& shortOptionName, std::string const& optionDescription, bool isOptionRequired)
  39. : longName(longOptionName), shortName(shortOptionName), description(optionDescription), moduleName(moduleName), isRequired(isOptionRequired), hasBeenSet(false) {
  40. validateFields();
  41. }
  42. 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)
  43. : longName(longOptionName), shortName(shortOptionName), description(optionDescription), moduleName(moduleName), isRequired(isOptionRequired), hasBeenSet(false) {
  44. // Copy all Arguments
  45. this->arguments.reserve(optionArguments.size());
  46. for (auto i = 0; i < optionArguments.size(); ++i) {
  47. // Clone gives a deep copy
  48. this->arguments.push_back(std::shared_ptr<ArgumentBase>(optionArguments.at(i).get()->clone()));
  49. }
  50. isArgumentsVectorValid(this->arguments);
  51. validateFields();
  52. }
  53. Option(Option const& other): longName(other.longName), shortName(other.shortName), description(other.description), moduleName(other.moduleName), isRequired(other.isRequired), hasBeenSet(other.hasBeenSet) {
  54. // Copy all Arguments
  55. this->arguments.reserve(other.arguments.size());
  56. for (auto i = 0; i < other.arguments.size(); ++i) {
  57. // Clone gives a deep copy
  58. this->arguments.push_back(std::shared_ptr<ArgumentBase>(other.arguments.at(i).get()->clone()));
  59. }
  60. isArgumentsVectorValid(this->arguments);
  61. validateFields();
  62. }
  63. ~Option() {
  64. std::cout << "Destructing an Option." << std::endl;
  65. this->arguments.clear();
  66. this->argumentNameMap.clear();
  67. }
  68. Option* clone() const {
  69. return new Option(*this);
  70. }
  71. void unify(Option& other) {
  72. if (this->getLongName().compare(other.getLongName()) != 0) {
  73. // LOG
  74. throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Names are different (\"" << getLongName() << "\" vs. \"" << other.getLongName() << "\")!";
  75. }
  76. if (this->getShortName().compare(other.getShortName()) != 0) {
  77. // LOG
  78. throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Shortnames are different (\"" << getShortName() << "\" vs. \"" << other.getShortName() << "\")!";
  79. }
  80. if (this->getArgumentCount() != other.getArgumentCount()) {
  81. // LOG
  82. throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Argument Counts are different!";
  83. }
  84. for(auto i = 0; i != this->arguments.size(); i++) {
  85. ArgumentBase* A = this->arguments.at(i).get();
  86. ArgumentBase* B = other.arguments.at(i).get();
  87. if (A->getArgumentType() != B->getArgumentType()) {
  88. // LOG
  89. throw storm::exceptions::OptionUnificationException() << "Error: Could not unify Option \"" << getLongName() << "\" because the Argument Types at Index " << i << " are different!";
  90. }
  91. switch (A->getArgumentType()) {
  92. case ArgumentType::String:
  93. static_cast<storm::settings::Argument<std::string>*>(A)->unify(*static_cast<storm::settings::Argument<std::string>*>(B));
  94. break;
  95. case ArgumentType::Integer:
  96. static_cast<storm::settings::Argument<int_fast64_t>*>(A)->unify(*static_cast<storm::settings::Argument<int_fast64_t>*>(B));
  97. break;
  98. case ArgumentType::UnsignedInteger:
  99. static_cast<storm::settings::Argument<uint_fast64_t>*>(A)->unify(*static_cast<storm::settings::Argument<uint_fast64_t>*>(B));
  100. break;
  101. case ArgumentType::Double:
  102. static_cast<storm::settings::Argument<double>*>(A)->unify(*static_cast<storm::settings::Argument<double>*>(B));
  103. break;
  104. case ArgumentType::Boolean:
  105. static_cast<storm::settings::Argument<bool>*>(A)->unify(*static_cast<storm::settings::Argument<bool>*>(B));
  106. break;
  107. default:
  108. // LOG
  109. throw storm::exceptions::InternalTypeErrorException() << "Error: Missing Case in ArgumentBuilder's switch/case Code.";
  110. }
  111. }
  112. if (this->getModuleName().compare(other.getModuleName()) != 0) {
  113. this->moduleName.append(", ").append(other.getModuleName());
  114. }
  115. }
  116. uint_fast64_t getArgumentCount() const {
  117. return this->arguments.size();
  118. }
  119. ArgumentBase& getArgument(uint_fast64_t argumentIndex) const {
  120. if (argumentIndex >= getArgumentCount()) {
  121. // LOG
  122. throw storm::exceptions::IllegalArgumentException() << "Error: Option::getArgument(): argumentIndex out of bounds!";
  123. }
  124. return *this->arguments.at(argumentIndex).get();
  125. }
  126. /*!
  127. * Returns a reference to the Argument with the specified longName.
  128. * Throws an Exception of Type IllegalArgumentException if there is no such Option.
  129. */
  130. ArgumentBase const& getArgumentByName(std::string const& argumentName) const {
  131. auto argumentIterator = this->argumentNameMap.find(storm::utility::StringHelper::stringToLower(argumentName));
  132. if (argumentIterator == this->argumentNameMap.end()) {
  133. // LOG
  134. throw storm::exceptions::IllegalArgumentException() << "The Option \"" << this->getLongName() << "\" does not contain an Argument with Name \"" << argumentName << "\"!";
  135. }
  136. return *argumentIterator->second.get();
  137. }
  138. std::string const& getLongName() const {
  139. return this->longName;
  140. }
  141. std::string const& getShortName() const {
  142. return this->shortName;
  143. }
  144. std::string const& getDescription() const {
  145. return this->description;
  146. }
  147. std::string const& getModuleName() const {
  148. return this->moduleName;
  149. }
  150. bool getIsRequired() const {
  151. return this->isRequired;
  152. }
  153. bool getHasOptionBeenSet() const {
  154. return this->hasBeenSet;
  155. }
  156. void setHasOptionBeenSet() {
  157. this->hasBeenSet = true;
  158. }
  159. private:
  160. std::string longName;
  161. std::string shortName;
  162. std::string description;
  163. std::string moduleName;
  164. bool isRequired;
  165. bool hasBeenSet;
  166. std::vector<std::shared_ptr<ArgumentBase>> arguments;
  167. std::unordered_map<std::string, std::shared_ptr<ArgumentBase>> argumentNameMap;
  168. void validateFields() const {
  169. if (longName.empty()) {
  170. throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with an empty longName field!";
  171. }
  172. if (moduleName.empty()) {
  173. throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with an empty moduleName field!";
  174. }
  175. bool longNameContainsNonAlpha = std::find_if(longName.begin(), longName.end(), [](char c) { return !std::isalpha(c); }) != longName.end();
  176. bool shortNameContainsNonAlpha = std::find_if(shortName.begin(), shortName.end(), [](char c) { return !std::isalpha(c); }) != shortName.end();
  177. if (longNameContainsNonAlpha) {
  178. throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with a longName that contains non-alpha characters!";
  179. }
  180. if (shortNameContainsNonAlpha) {
  181. throw storm::exceptions::IllegalArgumentException() << "Error: Tried constructing an Option with a shortName that contains non-alpha characters!";
  182. }
  183. }
  184. bool isArgumentsVectorValid(std::vector<std::shared_ptr<ArgumentBase>> const& arguments) {
  185. bool lastEntryWasOptional = false;
  186. std::unordered_set<std::string> argumentNameSet;
  187. for (auto i = arguments.begin(); i != arguments.end(); ++i) {
  188. bool isCurrentArgumentOptional = i->get()->getIsOptional();
  189. //if (!this->isRequired && !i->get()->getHasDefaultValue()) {
  190. // LOG
  191. // 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.";
  192. //}
  193. if (!isCurrentArgumentOptional && lastEntryWasOptional) {
  194. // LOG
  195. throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains a non-optional argument AFTER an optional argument.";
  196. }
  197. std::string lowerArgumentName = storm::utility::StringHelper::stringToLower(i->get()->getArgumentName());
  198. if (argumentNameSet.find(lowerArgumentName) != argumentNameSet.end()) {
  199. // LOG
  200. throw storm::exceptions::IllegalArgumentException() << "Error: The Argument Vector specified for Option \"" << getLongName() << "\" is invalid!\nIt contains two arguments with the same name.";
  201. }
  202. argumentNameSet.insert(lowerArgumentName);
  203. // This copies the Name to the Name Lookup Map
  204. argumentNameMap.insert(std::make_pair(lowerArgumentName, std::shared_ptr<ArgumentBase>(*i)));
  205. lastEntryWasOptional = isCurrentArgumentOptional;
  206. }
  207. return true;
  208. }
  209. };
  210. }
  211. }
  212. #endif // STORM_SETTINGS_OPTION_H_