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 56 : static void readDoubleMandatory(double& storage, tinyxml2::XMLElement* node, std::string tag) {
80 56 : double value;
81 56 : 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 56 : storage = value;
86 : }
87 56 : }
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 12 : static void readDoubleOptional(double& storage, tinyxml2::XMLElement* node, std::string tag) {
97 12 : double value;
98 12 : int result = node->QueryDoubleAttribute(tag.c_str(), &value);
99 12 : 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 12 : }
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 76 : static void readIntMandatory(int& storage, tinyxml2::XMLElement* node, std::string tag) {
116 76 : int value;
117 76 : 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 76 : storage = value;
122 : }
123 76 : }
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 36 : static void readBoolOptional(bool& storage, tinyxml2::XMLElement* node, std::string tag) {
178 36 : const char* myTextChar = node->Attribute(tag.c_str());
179 36 : if (myTextChar == NULL) {
180 32 : return;
181 : }
182 4 : std::string myText(myTextChar);
183 4 : if (myText == "yes") {
184 0 : storage = true;
185 4 : } else if (myText == "no") {
186 4 : storage = false;
187 : } else {
188 0 : std::cout << "Error while reading bool optional argument: Argument can "
189 0 : "only be yes or no!"
190 0 : << std::endl;
191 0 : exit(EXIT_FAILURE);
192 : }
193 4 : }
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 160 : static void readStringMandatory(std::string& storage, tinyxml2::XMLElement* node, std::string tag) {
202 160 : const char* myText = node->Attribute(tag.c_str());
203 160 : 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 160 : storage = std::string(myText);
208 : }
209 160 : }
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 8 : static void readStringOptional(std::string& storage, tinyxml2::XMLElement* node, std::string tag) {
219 8 : const char* myText = node->Attribute(tag.c_str());
220 8 : if (myText != NULL) {
221 0 : storage = std::string(myText);
222 : }
223 8 : }
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 140 : 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 48 : template <unsigned int size, class T> static void readVectorMandatory(tarch::la::Vector<size, T>& result, tinyxml2::XMLElement* node, std::string tag) {
265 48 : const char* myText = node->Attribute(tag.c_str());
266 48 : 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 48 : readVector<size, T>(result, myText);
271 48 : }
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
|