OGS 6.1.0-1721-g6382411ad
ConfigTree.cpp
Go to the documentation of this file.
1 
10 #include "ConfigTree.h"
11 
12 #include <forward_list>
13 #include <logog/include/logog.hpp>
14 #include <utility>
15 
16 #include "Error.h"
17 
18 // Explicitly instantiate the boost::property_tree::ptree which is a typedef to
19 // the following basic_ptree.
20 template class boost::property_tree::basic_ptree<std::string, std::string,
21  std::less<std::string>>;
22 
25 static std::forward_list<std::string> configtree_destructor_error_messages;
26 
27 namespace BaseLib
28 {
29 
30 const char ConfigTree::pathseparator = '/';
31 const std::string ConfigTree::key_chars_start = "abcdefghijklmnopqrstuvwxyz";
32 const std::string ConfigTree::key_chars = key_chars_start + "_0123456789";
33 
35  std::string filename,
36  Callback error_cb,
37  Callback warning_cb)
38  : _tree(&tree),
39  _filename(std::move(filename)),
40  _onerror(std::move(error_cb)),
41  _onwarning(std::move(warning_cb))
42 {
43  if (!_onerror) {
44  OGS_FATAL("ConfigTree: No valid error handler provided.");
45  }
46  if (!_onwarning) {
47  OGS_FATAL("ConfigTree: No valid warning handler provided.");
48  }
49 }
50 
52 ConfigTree(PTree const& tree, ConfigTree const& parent,
53  std::string const& root)
54  : _tree(&tree), _path(joinPaths(parent._path, root)),
55  _filename(parent._filename),
56  _onerror(parent._onerror), _onwarning(parent._onwarning)
57 {
58  checkKeyname(root);
59 }
60 
63  : _tree (other._tree)
64  , _path (std::move(other._path))
65  , _filename (std::move(other._filename))
66  , _visited_params(std::move(other._visited_params))
68  , _onerror (std::move(other._onerror))
69  , _onwarning (std::move(other._onwarning))
70 {
71  other._tree = nullptr;
72 }
73 
75 {
76  if (std::uncaught_exceptions() > 0)
77  {
78  /* If the stack unwinds the check below shall be suppressed in order to
79  * not accumulate false-positive configuration errors.
80  */
81  return;
82  }
83 
84  try {
86  } catch (std::exception& e) {
87  ERR("%s", e.what());
88  configtree_destructor_error_messages.push_front(e.what());
89  }
90 }
91 
95 {
97 
98  _tree = other._tree;
99  other._tree = nullptr;
100  _path = std::move(other._path);
101  _filename = std::move(other._filename);
102  _visited_params = std::move(other._visited_params);
103  _have_read_data = other._have_read_data;
104  _onerror = std::move(other._onerror);
105  _onwarning = std::move(other._onwarning);
106 
107  return *this;
108 }
109 
112 getConfigParameter(std::string const& root) const
113 {
114  auto ct = getConfigSubtree(root);
115  if (ct.hasChildren())
116  error("Requested parameter <" + root + "> actually is a subtree.");
117  return ct;
118 }
119 
120 boost::optional<ConfigTree>
122 getConfigParameterOptional(std::string const& root) const
123 {
124  auto ct = getConfigSubtreeOptional(root);
125  if (ct && ct->hasChildren())
126  error("Requested parameter <" + root + "> actually is a subtree.");
127  return ct;
128 }
129 
132 getConfigParameterList(const std::string &param) const
133 {
134  checkUnique(param);
135  markVisited(param, Attr::TAG, true);
136 
137  auto p = _tree->equal_range(param);
138 
140  ParameterIterator(p.first, param, *this),
141  ParameterIterator(p.second, param, *this));
142 }
143 
146 getConfigSubtree(std::string const& root) const
147 {
148  if (auto t = getConfigSubtreeOptional(root)) {
149  return std::move(*t);
150  }
151  error("Key <" + root + "> has not been found.");
152 }
153 
154 boost::optional<ConfigTree>
156 getConfigSubtreeOptional(std::string const& root) const
157 {
158  checkUnique(root);
159 
160  if (auto subtree = _tree->get_child_optional(root)) {
161  markVisited(root, Attr::TAG, false);
162  return ConfigTree(*subtree, *this, root);
163  }
164  markVisited(root, Attr::TAG, true);
165  return boost::none;
166 }
167 
170 getConfigSubtreeList(std::string const& root) const
171 {
172  checkUnique(root);
173  markVisited(root, Attr::TAG, true);
174 
175  auto p = _tree->equal_range(root);
176 
177  return Range<SubtreeIterator>(
178  SubtreeIterator(p.first, root, *this),
179  SubtreeIterator(p.second, root, *this));
180 }
181 
182 void ConfigTree::ignoreConfigParameter(const std::string &param) const
183 {
184  checkUnique(param);
185  // if not found, peek only
186  bool peek_only = _tree->find(param) == _tree->not_found();
187  markVisited(param, Attr::TAG, peek_only);
188 }
189 
190 void ConfigTree::ignoreConfigAttribute(const std::string &attr) const
191 {
192  checkUniqueAttr(attr);
193 
194  // Exercise: Guess what not! (hint: if not found, peek only)
195  // Btw. (not a hint) _tree->find() does not seem to work here.
196  bool peek_only = !_tree->get_child_optional("<xmlattr>." + attr);
197 
198  markVisited(attr, Attr::ATTR, peek_only);
199 }
200 
201 void ConfigTree::ignoreConfigParameterAll(const std::string &param) const
202 {
203  checkUnique(param);
204  auto& ct = markVisited(param, Attr::TAG, true);
205 
206  auto p = _tree->equal_range(param);
207  for (auto it = p.first; it != p.second; ++it) {
208  ++ct.count;
209  }
210 }
211 
212 
213 void ConfigTree::error(const std::string& message) const
214 {
215  _onerror(_filename, _path, message);
216  OGS_FATAL(
217  "ConfigTree: The error handler does not break out of the normal "
218  "control flow.");
219 }
220 
221 void ConfigTree::warning(const std::string& message) const
222 {
223  _onwarning(_filename, _path, message);
224 }
225 
226 
227 void ConfigTree::onerror(const std::string& filename, const std::string& path,
228  const std::string& message)
229 {
230  OGS_FATAL("ConfigTree: In file `%s' at path <%s>: %s",
231  filename.c_str(), path.c_str(), message.c_str());
232 }
233 
234 void ConfigTree::onwarning(const std::string& filename, const std::string& path,
235  const std::string& message)
236 {
237  WARN("ConfigTree: In file `%s' at path <%s>: %s",
238  filename.c_str(), path.c_str(), message.c_str());
239 }
240 
242 {
244  return;
245 
246  ERR("ConfigTree: There have been errors when parsing the configuration "
247  "file(s):");
248 
249  for (auto const& msg : configtree_destructor_error_messages) {
250  ERR("%s", msg.c_str());
251  }
252 
253  OGS_FATAL("There have been errors when parsing the configuration file(s).");
254 }
255 
256 std::string ConfigTree::shortString(const std::string &s)
257 {
258  const std::size_t maxlen = 100;
259 
260  if (s.size() < maxlen) return s;
261 
262  return s.substr(0, maxlen-3) + "...";
263 }
264 
265 
266 void ConfigTree::checkKeyname(std::string const& key) const
267 {
268  if (key.empty()) {
269  error("Search for empty key.");
270  } else if (key_chars_start.find(key.front()) == std::string::npos) {
271  error("Key <" + key + "> starts with an illegal character.");
272  } else if (key.find_first_not_of(key_chars, 1) != std::string::npos) {
273  error("Key <" + key + "> contains illegal characters.");
274  } else if (key.find("__") != std::string::npos) {
275  // This is illegal because we use parameter names to generate doxygen
276  // page names. Thereby "__" acts as a separator character. Choosing
277  // other separators is not possible because of observed limitations
278  // for valid doxygen page names.
279  error("Key <" + key + "> contains double underscore.");
280  }
281 }
282 
283 std::string ConfigTree::
284 joinPaths( const std::string &p1, const std::string &p2) const
285 {
286  if (p2.empty()) {
287  error("Second path to be joined is empty.");
288  }
289 
290  if (p1.empty()) return p2;
291 
292  return p1 + pathseparator + p2;
293 }
294 
295 void ConfigTree::checkUnique(const std::string &key) const
296 {
297  checkKeyname(key);
298 
299  if (_visited_params.find({Attr::TAG, key}) != _visited_params.end()) {
300  error("Key <" + key + "> has already been processed.");
301  }
302 }
303 
304 void ConfigTree::checkUniqueAttr(const std::string &attr) const
305 {
306  // Workaround for handling attributes with xml namespaces and uppercase letters.
307  if (attr.find(':') != attr.npos) {
308  auto pos = decltype(attr.npos){0};
309 
310  // Replace colon and uppercase letters with an allowed character 'a'.
311  // That means, attributes containing a colon are also allowed to contain
312  // uppercase letters.
313  auto attr2 = attr;
314  do {
315  pos = attr2.find_first_of(":ABCDEFGHIJKLMNOPQRSTUVWXYZ", pos);
316  if (pos != attr.npos) attr2[pos] = 'a';
317  } while (pos != attr.npos);
318 
319  checkKeyname(attr2);
320  } else {
321  checkKeyname(attr);
322  }
323 
324  if (_visited_params.find({Attr::ATTR, attr}) != _visited_params.end()) {
325  error("Attribute '" + attr + "' has already been processed.");
326  }
327 }
328 
331 markVisited(std::string const& key, Attr const is_attr, bool const peek_only) const
332 {
333  return markVisited<ConfigTree>(key, is_attr, peek_only);
334 }
335 
336 void
338 markVisitedDecrement(Attr const is_attr, std::string const& key) const
339 {
340  auto const type = std::type_index(typeid(nullptr));
341 
342  auto p = _visited_params.emplace(std::make_pair(is_attr, key),
343  CountType{-1, type});
344 
345  if (!p.second) { // no insertion happened
346  auto& v = p.first->second;
347  --v.count;
348  }
349 }
350 
351 bool
353 {
354  auto const& tree = *_tree;
355  if (tree.begin() == tree.end())
356  return false; // no children
357  if (tree.front().first == "<xmlattr>"
358  && (++tree.begin()) == tree.end())
359  return false; // only attributes
360 
361  return true;
362 }
363 
364 void
366 {
367  if (!_tree) return;
368 
369  // Note: due to a limitation in boost::property_tree it is not possible
370  // to discriminate between <tag></tag> and <tag/> in the input file.
371  // In both cases data() will be empty.
372  if ((!_have_read_data) && !_tree->data().empty()) {
373  warning("The immediate data `" + shortString(_tree->data())
374  +"' of this tag has not been read.");
375  }
376 
377  // iterate over children
378  for (auto const& p : *_tree) {
379  if (p.first != "<xmlattr>") // attributes are handled below
381  }
382 
383  // iterate over attributes
384  if (auto attrs = _tree->get_child_optional("<xmlattr>")) {
385  for (auto const& p : *attrs) {
387  }
388  }
389 
390  for (auto const& p : _visited_params)
391  {
392  auto const& tag = p.first.second;
393  auto const& count = p.second.count;
394 
395  switch (p.first.first) {
396  case Attr::ATTR:
397  if (count > 0) {
398  warning("XML attribute '" + tag + "' has been read " +
399  std::to_string(count) +
400  " time(s) more than it was present in the "
401  "configuration tree.");
402  } else if (count < 0) {
403  warning("XML attribute '" + tag + "' has been read " +
404  std::to_string(-count) +
405  " time(s) less than it was present in the "
406  "configuration tree.");
407  }
408  break;
409  case Attr::TAG:
410  if (count > 0) {
411  warning("Key <" + tag + "> has been read " + std::to_string(count)
412  + " time(s) more than it was present in the configuration tree.");
413  } else if (count < 0) {
414  warning("Key <" + tag + "> has been read " + std::to_string(-count)
415  + " time(s) less than it was present in the configuration tree.");
416  }
417  }
418  }
419 
420  // The following invalidates this instance, s.t. it can not be read from it anymore,
421  // but it also prevents double-checking.
422  _tree = nullptr;
423 }
424 
425 
427 {
428  conf.checkAndInvalidate();
429 }
430 
432 {
433  if (conf) conf->checkAndInvalidate();
434 }
435 
436 void checkAndInvalidate(std::unique_ptr<ConfigTree> const& conf)
437 {
438  if (conf) conf->checkAndInvalidate();
439 }
440 
441 }
static void onwarning(std::string const &filename, std::string const &path, std::string const &message)
Definition: ConfigTree.cpp:234
CountType & markVisited(std::string const &key, Attr const is_attr, bool peek_only) const
Attr
Used to indicate if dealing with XML tags or XML attributes.
Definition: ConfigTree.h:534
std::string _path
A path printed in error/warning messages.
Definition: ConfigTree.h:609
void error(std::string const &message) const
Definition: ConfigTree.cpp:213
bool _have_read_data
Indicates if the plain data contained in this tree has already been read.
Definition: ConfigTree.h:627
void ignoreConfigParameterAll(std::string const &param) const
Definition: ConfigTree.cpp:201
boost::property_tree::ptree const * _tree
The wrapped tree.
Definition: ConfigTree.h:606
void checkUnique(std::string const &key) const
Asserts that the key has not been read yet.
Definition: ConfigTree.cpp:295
void warning(std::string const &message) const
Definition: ConfigTree.cpp:221
T getConfigParameter(std::string const &param) const
ConfigTree & operator=(ConfigTree const &)=delete
copying is not compatible with the semantics of this class
static const char pathseparator
Character separating two path components.
Definition: ConfigTree.h:633
std::map< KeyType, CountType > _visited_params
Definition: ConfigTree.h:624
std::string _filename
The path of the file from which this tree has been read.
Definition: ConfigTree.h:612
void checkKeyname(std::string const &key) const
Checks if key complies with the rules [a-z0-9_].
Definition: ConfigTree.cpp:266
Callback _onerror
Custom error callback.
Definition: ConfigTree.h:629
boost::property_tree::ptree PTree
The tree being wrapped by this class.
Definition: ConfigTree.h:218
static std::forward_list< std::string > configtree_destructor_error_messages
Definition: ConfigTree.cpp:25
void checkUniqueAttr(std::string const &attr) const
Asserts that the attribute attr has not been read yet.
Definition: ConfigTree.cpp:304
static std::string shortString(std::string const &s)
returns a short string at suitable for error/warning messages
Definition: ConfigTree.cpp:256
Wraps a pair of iterators for use as a range in range-based for-loops.
void markVisitedDecrement(Attr const is_attr, std::string const &key) const
Definition: ConfigTree.cpp:338
std::function< void(const std::string &filename, const std::string &path, const std::string &message)> Callback
Definition: ConfigTree.h:229
ConfigTree(PTree const &tree, std::string filename, Callback error_cb, Callback warning_cb)
Definition: ConfigTree.cpp:34
Build information.
static void assertNoSwallowedErrors()
Asserts that there have not been any errors reported in the destructor.
Definition: ConfigTree.cpp:241
ConfigTree getConfigSubtree(std::string const &root) const
Definition: ConfigTree.cpp:146
static void onerror(std::string const &filename, std::string const &path, std::string const &message)
Definition: ConfigTree.cpp:227
Range< SubtreeIterator > getConfigSubtreeList(std::string const &root) const
Definition: ConfigTree.cpp:170
Callback _onwarning
Custom warning callback.
Definition: ConfigTree.h:630
boost::optional< T > getConfigParameterOptional(std::string const &param) const
static const std::string key_chars_start
Set of allowed characters as the first letter of a key name.
Definition: ConfigTree.h:636
#define OGS_FATAL(fmt,...)
Definition: Error.h:71
Range< ValueIterator< T > > getConfigParameterList(std::string const &param) const
boost::optional< ConfigTree > getConfigSubtreeOptional(std::string const &root) const
Definition: ConfigTree.cpp:156
void ignoreConfigAttribute(std::string const &attr) const
Definition: ConfigTree.cpp:190
void ignoreConfigParameter(std::string const &param) const
Definition: ConfigTree.cpp:182
std::string joinPaths(std::string const &p1, std::string const &p2) const
Used to generate the path of a subtree.
Definition: ConfigTree.cpp:284
static const std::string key_chars
Set of allowed characters in a key name.
Definition: ConfigTree.h:639
bool hasChildren() const
Checks if this tree has any children.
Definition: ConfigTree.cpp:352