Line data Source code
1 : // Copyright (C) 2015 Technische Universitaet Muenchen 2 : // This file is part of the Mamico project. For conditions of distribution 3 : // and use, please see the copyright notice in Mamico's main folder, or at 4 : // www5.in.tum.de/mamico 5 : #ifndef _TARCH_CONFIGURATION_PARSECONFIGURATION_H_ 6 : #define _TARCH_CONFIGURATION_PARSECONFIGURATION_H_ 7 : #include "tarch/la/Vector.h" 8 : #include "tarch/tinyxml2/tinyxml2.h" 9 : #include <cstdlib> 10 : #include <filesystem> 11 : #include <iostream> 12 : #include <sstream> 13 : 14 : namespace tarch { 15 : namespace configuration { 16 : 17 : /** interface for configuration using tinyxml2 18 : * @author Philipp Neumann 19 : */ 20 : class ParseConfiguration { 21 : public: 22 : /** 23 : * Container for the XML configuration for MaMiCo 24 : * (Manages the memory of the associated XML document and provides a pointer to the configuration root) 25 : */ 26 : class XMLConfiguration { 27 : private: 28 20 : XMLConfiguration(tinyxml2::XMLNode* _root, tinyxml2::XMLError _error) : root(_root), error(_error) {} 29 : 30 : public: 31 : /** 32 : * Root of the loaded MaMiCo configuration 33 : */ 34 : tinyxml2::XMLNode* const root; 35 : 36 : /** 37 : * Status of loading the XML document 38 : */ 39 : tinyxml2::XMLError const error; 40 : 41 : /** 42 : * Loads the MaMiCo configuration from a file (with or without XML root node) 43 : * @param filename 44 : */ 45 20 : static XMLConfiguration load(const std::string filename) { 46 20 : tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); 47 20 : tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); 48 20 : tinyxml2::XMLNode* root = doc->FirstChildElement("scenario-configuration"); 49 20 : if (root == NULL) { 50 16 : root = doc; 51 16 : std::cout << "No root node <scenario-configuration> found in configuration file. (Using legacy format without XML root node.)" << std::endl; 52 : } 53 20 : return XMLConfiguration(root, error); 54 : } 55 : 56 20 : ~XMLConfiguration() { delete root->GetDocument(); } 57 : }; 58 : 59 : /** parses an xml configuration file, and stores the information in the object 60 : *config 61 : * @tparam Configuration 62 : */ 63 8 : template <class Configuration> static void parseConfiguration(const std::string filename, const std::string topleveltag, Configuration& config) { 64 8 : XMLConfiguration xmlConfig = XMLConfiguration::load(filename); 65 8 : tinyxml2::XMLElement* node = xmlConfig.root->FirstChildElement(topleveltag.c_str()); 66 8 : if (node == NULL) { 67 0 : std::cout << "Could not read input file " << filename << " (missing or invalid)" << std::endl; 68 0 : exit(EXIT_FAILURE); 69 : } 70 8 : config.parseSubtag(node); 71 8 : } 72 : 73 : /** reads a double value at node "node" with tag "tag" and stores the result 74 : *in "storage". the program is exited if the value cannot be read. 75 : * @param storage 76 : * @param node 77 : * @param tag 78 : */ 79 28 : static void readDoubleMandatory(double& storage, tinyxml2::XMLElement* node, std::string tag) { 80 28 : double value; 81 28 : if (node->QueryDoubleAttribute(tag.c_str(), &value) != tinyxml2::XML_SUCCESS) { 82 0 : std::cout << "Error while reading mandatory argument " << tag << " of XML element " << node->Name() << std::endl; 83 0 : exit(EXIT_FAILURE); 84 : } else { 85 28 : storage = value; 86 : } 87 28 : } 88 : 89 : /** reads a double value at node "node" with tag "tag" and stores the result 90 : *in "storage". the program continues and only stores the result in "storage" 91 : *if "tag" is existent 92 : * @param storage 93 : * @param node 94 : * @param tag 95 : */ 96 8 : static void readDoubleOptional(double& storage, tinyxml2::XMLElement* node, std::string tag) { 97 8 : double value; 98 8 : int result = node->QueryDoubleAttribute(tag.c_str(), &value); 99 8 : if (result == tinyxml2::XML_NO_ATTRIBUTE) { 100 : // nop 101 8 : } else if (result == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { 102 0 : std::cout << "Error while reading optional argument " << tag << " of XML element " << node->Name() << std::endl; 103 0 : exit(EXIT_FAILURE); 104 : } else { 105 8 : storage = value; 106 : } 107 8 : } 108 : 109 : /** reads a int value at node "node" with tag "tag" and stores the result in 110 : *"storage". the program is exited if the value cannot be read. 111 : * @param storage 112 : * @param node 113 : * @param tag 114 : */ 115 52 : static void readIntMandatory(int& storage, tinyxml2::XMLElement* node, std::string tag) { 116 52 : int value; 117 52 : if (node->QueryIntAttribute(tag.c_str(), &value) != tinyxml2::XML_SUCCESS) { 118 0 : std::cout << "Error while reading mandatory argument " << tag << " of XML element " << node->Name() << std::endl; 119 0 : exit(EXIT_FAILURE); 120 : } else { 121 52 : storage = value; 122 : } 123 52 : } 124 : 125 : /** reads a int value at node "node" with tag "tag" and stores the result in 126 : *"storage". the program continues and only stores the result in "storage" if 127 : *"tag" is existent 128 : * @param storage 129 : * @param node 130 : * @param tag 131 : */ 132 4 : static void readIntOptional(int& storage, tinyxml2::XMLElement* node, std::string tag) { 133 4 : int value; 134 4 : int result = node->QueryIntAttribute(tag.c_str(), &value); 135 4 : if (result == tinyxml2::XML_NO_ATTRIBUTE) { 136 : // nop 137 4 : } else if (result == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { 138 0 : std::cout << "Error while reading optional argument " << tag << " of XML element " << node->Name() << std::endl; 139 0 : exit(EXIT_FAILURE); 140 : } else { 141 4 : storage = value; 142 : } 143 4 : } 144 : 145 : /** reads a bool value at node "node" with tag "tag" and stores the result in 146 : *"storage". the program is exited if the value cannot be read. 147 : * @param storage 148 : * @param node 149 : * @param tag 150 : */ 151 68 : static void readBoolMandatory(bool& storage, tinyxml2::XMLElement* node, std::string tag) { 152 68 : const char* myTextChar = node->Attribute(tag.c_str()); 153 68 : if (myTextChar == NULL) { 154 0 : std::cout << "Error: mandatory bool " << tag << " could not be found!" << std::endl; 155 0 : exit(EXIT_FAILURE); 156 : } 157 68 : std::string myText(myTextChar); 158 68 : if (myText == "yes") { 159 48 : storage = true; 160 20 : } else if (myText == "no") { 161 20 : storage = false; 162 : } else { 163 0 : std::cout << "Error while reading bool optional argument: Argument can " 164 0 : "only be yes or no!" 165 0 : << std::endl; 166 0 : exit(EXIT_FAILURE); 167 : } 168 68 : } 169 : 170 : /** reads a bool value at node "node" with tag "tag" and stores the result in 171 : *"storage". the program continues and only stores the result in "storage" if 172 : *"tag" is existent 173 : * @param storage 174 : * @param node 175 : * @param tag 176 : */ 177 : static void readBoolOptional(bool& storage, tinyxml2::XMLElement* node, std::string tag) { 178 : const char* myTextChar = node->Attribute(tag.c_str()); 179 : if (myTextChar == NULL) { 180 : return; 181 : } 182 : std::string myText(myTextChar); 183 : if (myText == "yes") { 184 : storage = true; 185 : } else if (myText == "no") { 186 : storage = false; 187 : } else { 188 : std::cout << "Error while reading bool optional argument: Argument can " 189 : "only be yes or no!" 190 : << std::endl; 191 : exit(EXIT_FAILURE); 192 : } 193 : } 194 : 195 : /** reads a string value at node "node" with tag "tag" and stores the result 196 : *in "storage". the program is exited if the value cannot be read. 197 : * @param storage 198 : * @param node 199 : * @param tag 200 : */ 201 44 : static void readStringMandatory(std::string& storage, tinyxml2::XMLElement* node, std::string tag) { 202 44 : const char* myText = node->Attribute(tag.c_str()); 203 44 : if (myText == NULL) { 204 0 : std::cout << "Error while reading mandatory argument " << tag << " of XML element " << node->Name() << std::endl; 205 0 : exit(EXIT_FAILURE); 206 : } else { 207 44 : storage = std::string(myText); 208 : } 209 44 : } 210 : 211 : /** reads a string value at node "node" with tag "tag" and stores the result 212 : *in "storage". the program continues and only stores the result in "storage" 213 : *if "tag" is existent 214 : * @param storage 215 : * @param node 216 : * @param tag 217 : */ 218 : static void readStringOptional(std::string& storage, tinyxml2::XMLElement* node, std::string tag) { 219 : const char* myText = node->Attribute(tag.c_str()); 220 : if (myText != NULL) { 221 : storage = std::string(myText); 222 : } 223 : } 224 : 225 : /** reads with ";"-separated entry "tag" with "size" entries and returns the 226 : *corresponding vector on success 227 : * @tparam T Data type 228 : * @tparam Size size of the entry 229 : * @param result 230 : * @param myText 231 : */ 232 48 : template <unsigned int size, class T> static void readVector(tarch::la::Vector<size, T>& result, const char* myText) { 233 48 : std::string input(myText); 234 188 : for (unsigned int i = 0; i < size; i++) { 235 : // search for first non-whitespace entry in this vector entry 236 140 : std::size_t first = input.find_first_not_of(" "); 237 : // search for end of this vector component, typically denoted by ";" 238 : // -> if this is the last entry, then npos is accepted as well 239 140 : std::size_t last = input.find_first_of(";"); 240 : // for debugging 241 : // std::cout << first << ", " << last << std::endl; 242 140 : if ((i == size - 1) && (last == std::string::npos)) { 243 48 : last = input.size(); 244 : } 245 : 246 140 : std::stringstream ss(input.substr(first, last - first)); 247 : // for debugging 248 : // std::cout << ss.str() << std::endl; 249 140 : ss >> result[i]; 250 140 : if (i < size - 1) { 251 92 : input = input.substr(last + 1, input.size() - last - 1); 252 : } 253 : } 254 48 : } 255 : 256 : /** reads with ";"-separated entry "tag" with "size" entries and returns the 257 : *corresponding vector on success 258 : * @tparam T Data type 259 : * @tparam Size size of the entry 260 : * @param result 261 : * @param node 262 : * @param tag 263 : */ 264 24 : template <unsigned int size, class T> static void readVectorMandatory(tarch::la::Vector<size, T>& result, tinyxml2::XMLElement* node, std::string tag) { 265 24 : const char* myText = node->Attribute(tag.c_str()); 266 24 : if (myText == NULL) { 267 0 : std::cout << "Error while reading mandatory argument " << tag << " of XML element " << node->Name() << std::endl; 268 0 : exit(EXIT_FAILURE); 269 : } 270 24 : readVector<size, T>(result, myText); 271 24 : } 272 : 273 : /** reads with ";"-separated entry "tag" with "size" entries and returns the 274 : *corresponding vector on success 275 : *if "tag" is existent 276 : * @tparam T Data type 277 : * @tparam Size size of the entry 278 : * @param result 279 : * @param node 280 : * @param tag 281 : */ 282 : template <unsigned int size, class T> static void readVectorOptional(tarch::la::Vector<size, T>& result, tinyxml2::XMLElement* node, std::string tag) { 283 : const char* myText = node->Attribute(tag.c_str()); 284 : if (myText != NULL) { 285 : readVector<size, T>(result, myText); 286 : } 287 : } 288 : }; 289 : } // namespace configuration 290 : } // namespace tarch 291 : #endif