/** * \copyright * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) * Distributed under a Modified BSD License. * See accompanying file LICENSE.txt or * http://www.opengeosys.org/project/license * */ #include "ConfigTreeNew.h" namespace BaseLib { const char ConfigTreeNew::pathseparator = '/'; const std::string ConfigTreeNew::key_chars_start = "abcdefghijklmnopqrstuvwxyz"; const std::string ConfigTreeNew::key_chars = key_chars_start + "_0123456789"; ConfigTreeNew:: ConfigTreeNew(PTree const& tree, Callback const& error_cb, Callback const& warning_cb) : _tree(&tree), _onerror(error_cb), _onwarning(warning_cb) { if (!_onerror) { ERR("ConfigTree: No valid error handler provided."); std::abort(); } if (!_onwarning) { ERR("ConfigTree: No valid warning handler provided."); std::abort(); } } ConfigTreeNew:: ConfigTreeNew(PTree const& tree, ConfigTreeNew const& parent, std::string const& root) : _tree(&tree), _path(joinPaths(parent._path, root)), _onerror(parent._onerror), _onwarning(parent._onwarning) { checkKeyname(root); } ConfigTreeNew:: ConfigTreeNew(ConfigTreeNew && other) : _tree(other._tree) , _path(other._path) , _visited_params(std::move(other._visited_params)) , _onerror(other._onerror) , _onwarning(other._onwarning) { other._tree = nullptr; } ConfigTreeNew::~ConfigTreeNew() { if (!_tree) return; for (auto const& p : *_tree) { markVisitedDecrement(p.first); } for (auto const& p : _visited_params) { if (p.second.count > 0) { warning("Key <" + p.first + "> has been read " + std::to_string(p.second.count) + " time(s) more than it was present in the configuration tree."); } else if (p.second.count < 0) { warning("Key <" + p.first + "> has been read " + std::to_string(-p.second.count) + " time(s) less than it was present in the configuration tree."); } } } ConfigTreeNew& ConfigTreeNew:: operator=(ConfigTreeNew&& other) { _tree = other._tree; other._tree = nullptr; _path = other._path; _visited_params = std::move(other._visited_params); // TODO Caution: That might be a very nontrivial operation (copying a std::function). _onerror = other._onerror; _onwarning = other._onwarning; return *this; } ConfigTreeNew ConfigTreeNew:: getConfSubtree(std::string const& root) const { if (auto t = getConfSubtreeOptional(root)) { return std::move(*t); } else { error("Key <" + root + "> has not been found."); return ConfigTreeNew(PTree(), *this, ""); // TODO that will crash } } boost::optional<ConfigTreeNew> ConfigTreeNew:: getConfSubtreeOptional(std::string const& root) const { checkUnique(root); auto subtree = _tree->get_child_optional(root); if (subtree) { markVisited(root); return boost::optional<ConfigTreeNew>(std::move( ConfigTreeNew(*subtree, *this, root))); } else { markVisited(root, true); return boost::optional<ConfigTreeNew>(); } } Range<ConfigTreeNew::SubtreeIterator> ConfigTreeNew:: getConfSubtreeList(std::string const& root) const { checkUnique(root); markVisited(root, true); auto p = _tree->equal_range(root); return Range<SubtreeIterator>( SubtreeIterator(p.first, root, *this), SubtreeIterator(p.second, root, *this)); } void ConfigTreeNew::ignoreConfParam(const std::string ¶m) const { checkUnique(param); // if not found, peek only bool peek_only = _tree->find(param) == _tree->not_found(); markVisited(param, peek_only); } void ConfigTreeNew::ignoreConfParamAll(const std::string ¶m) const { checkUnique(param); auto& ct = markVisited(param, true); auto p = _tree->equal_range(param); for (auto it = p.first; it != p.second; ++it) { ++ct.count; } } void ConfigTreeNew::error(const std::string& message) const { _onerror(_path, message); } void ConfigTreeNew::warning(const std::string& message) const { _onwarning(_path, message); } void ConfigTreeNew::onerror(const std::string& path, const std::string& message) { ERR("ConfigTree: At path <%s>: %s", path.c_str(), message.c_str()); std::abort(); } void ConfigTreeNew::onwarning(const std::string& path, const std::string& message) { WARN("ConfigTree: At path <%s>: %s", path.c_str(), message.c_str()); } std::string ConfigTreeNew::shortString(const std::string &s) { const std::size_t maxlen = 100; if (s.size() < maxlen) return s; return s.substr(0, maxlen-3) + "..."; } void ConfigTreeNew::checkKeyname(std::string const& key) const { if (key.empty()) { error("Search for empty key."); } else if (key_chars_start.find(key.front()) == std::string::npos) { error("Key <" + key + "> starts with an illegal character."); } else if (key.find_first_not_of(key_chars, 1) != std::string::npos) { error("Key <" + key + "> contains illegal characters."); } } std::string ConfigTreeNew:: joinPaths( const std::string &p1, const std::string &p2) const { if (p2.empty()) { error("Second path to be joined is empty."); } if (p1.empty()) return p2; return p1 + pathseparator + p2; } void ConfigTreeNew::checkUnique(const std::string &key) const { checkKeyname(key); if (_visited_params.find(key) != _visited_params.end()) { error("Key <" + key + "> has already been processed."); } } ConfigTreeNew::CountType& ConfigTreeNew:: markVisited(std::string const& key, bool peek_only) const { return markVisited<ConfigTreeNew>(key, peek_only); } void ConfigTreeNew:: markVisitedDecrement(std::string const& key) const { auto const type = std::type_index(typeid(nullptr)); auto p = _visited_params.emplace(key, CountType{-1, type}); if (!p.second) { // no insertion happened auto& v = p.first->second; --v.count; } } }