diff --git a/BaseLib/ConfigTreeNew-impl.h b/BaseLib/ConfigTreeNew-impl.h index 8fb48906aea467a66ff739e6d4f81f5be733bf3d..41ea3f9dc7c2509fa7fb2036f97a63aebe3b302d 100644 --- a/BaseLib/ConfigTreeNew-impl.h +++ b/BaseLib/ConfigTreeNew-impl.h @@ -35,11 +35,10 @@ T ConfigTreeNew:: getConfParam(std::string const& param) const { - auto p = getConfParamOptional<T>(param); - if (p) return *p; + if (auto p = getConfParamOptional<T>(param)) + return *p; error("Key <" + param + "> has not been found"); - return T(); } template<typename T> @@ -47,8 +46,9 @@ T ConfigTreeNew:: getConfParam(std::string const& param, T const& default_value) const { - auto p = getConfParamOptional<T>(param); - if (p) return *p; + if (auto p = getConfParamOptional<T>(param)) + return *p; + return default_value; } @@ -58,20 +58,9 @@ ConfigTreeNew:: getConfParamOptional(std::string const& param) const { checkUnique(param); - auto p = _tree->get_child_optional(param); - bool peek_only = p == boost::none; - markVisited<T>(param, peek_only); - - if (p) { - auto v = p->get_value_optional<T>(); - if (v) { - return v; - } else { - error("Value for key <" + param + "> `" + shortString(p->data()) - + "' not convertible to the desired type."); - } - } + if (auto p = getConfSubtreeOptional(param)) + return p->getValue<T>(); return boost::none; } @@ -97,20 +86,16 @@ peekConfParam(std::string const& param) const { checkKeyname(param); - auto p =_tree->get_child_optional(param); - - if (!p) { - error("Key <" + param + "> has not been found"); - } else { + if (auto p =_tree->get_child_optional(param)) { try { return p->get_value<T>(); } catch (boost::property_tree::ptree_bad_data) { error("Value for key <" + param + "> `" + shortString(p->data()) + "' not convertible to the desired type."); } + } else { + error("Key <" + param + "> has not been found"); } - - return T(); } template<typename T> @@ -133,11 +118,55 @@ checkConfParam(std::string const& param, Ch const* value) const } } +template<typename T> +T +ConfigTreeNew:: +getValue() const +{ + // TODO test this + if (_have_read_data) { + error("The data of this subtree has already been read."); + } + + _have_read_data = true; + + if (auto v = _tree->get_value_optional<T>()) { + return *v; + } else { + error("Value `" + shortString(_tree->data()) + + "' is not convertible to the desired type."); + } +} + +template<typename T> +T +ConfigTreeNew:: +getAttribute(std::string const& attr) const +{ + checkUniqueAttr(attr); + markVisited(attr, true); + + if (auto attrs = _tree->get_child_optional("<xmlattr>")) { + if (auto a = attrs->get_child_optional(attr)) { + if (auto v = a->get_value_optional<T>()) { + return *v; + } else { + error("Value for XML attribute \"" + attr + "\" `" + + shortString(a->data()) + + "' not convertible to the desired type."); + } + } else { + error("No XML attribute named \"" + attr + "\""); + } + } else { + error("This parameter has no XML attributes"); + } +} template<typename T> ConfigTreeNew::CountType& ConfigTreeNew:: -markVisited(std::string const& key, bool peek_only) const +markVisited(std::string const& key, bool const peek_only) const { auto const type = std::type_index(typeid(T)); diff --git a/BaseLib/ConfigTreeNew.cpp b/BaseLib/ConfigTreeNew.cpp index 57b16ed0a0090cec2ece05a1a31d7c73e10ce7cd..c5da6e1606677d6a8e7f97f661f97297de6facef 100644 --- a/BaseLib/ConfigTreeNew.cpp +++ b/BaseLib/ConfigTreeNew.cpp @@ -43,11 +43,12 @@ ConfigTreeNew(PTree const& tree, ConfigTreeNew const& parent, ConfigTreeNew:: ConfigTreeNew(ConfigTreeNew && other) - : _tree(other._tree) - , _path(other._path) + : _tree (other._tree) + , _path (std::move(other._path)) , _visited_params(std::move(other._visited_params)) - , _onerror(other._onerror) - , _onwarning(other._onwarning) + , _have_read_data (other._have_read_data) + , _onerror (std::move(other._onerror)) + , _onwarning (std::move(other._onwarning)) { other._tree = nullptr; } @@ -67,12 +68,23 @@ operator=(ConfigTreeNew&& other) other._tree = nullptr; _path = std::move(other._path); _visited_params = std::move(other._visited_params); + _have_read_data = other._have_read_data; _onerror = std::move(other._onerror); _onwarning = std::move(other._onwarning); return *this; } +ConfigTreeNew +ConfigTreeNew:: +getConfParam(std::string const& root) const +{ + auto ct = getConfSubtree(root); + if (hasChildren(ct)) + error("Requested parameter <" + root + "> actually is a subtree."); + return ct; +} + ConfigTreeNew ConfigTreeNew:: getConfSubtree(std::string const& root) const @@ -81,7 +93,6 @@ getConfSubtree(std::string const& root) const return std::move(*t); } else { error("Key <" + root + "> has not been found."); - return ConfigTreeNew(PTree(), *this, ""); // TODO that will crash } } @@ -90,14 +101,13 @@ ConfigTreeNew:: getConfSubtreeOptional(std::string const& root) const { checkUnique(root); - auto subtree = _tree->get_child_optional(root); - if (subtree) { - markVisited(root); + if (auto subtree = _tree->get_child_optional(root)) { + markVisited(root, false); return ConfigTreeNew(*subtree, *this, root); } else { markVisited(root, true); - return boost::optional<ConfigTreeNew>(); + return boost::none; } } @@ -138,6 +148,7 @@ void ConfigTreeNew::ignoreConfParamAll(const std::string ¶m) const void ConfigTreeNew::error(const std::string& message) const { _onerror(_path, message); + std::abort(); } void ConfigTreeNew::warning(const std::string& message) const @@ -175,6 +186,12 @@ void ConfigTreeNew::checkKeyname(std::string const& key) const 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."); + } else if (key.find("__") != std::string::npos) { + // This is illegal because we use parameter names to generate doxygen + // page names. Thereby "__" acts as a separator character. Choosing + // other separators is not possible because of observed limitations + // for valid doxygen page names. + error("Key <" + key + "> contains double underscore."); } } @@ -199,9 +216,18 @@ void ConfigTreeNew::checkUnique(const std::string &key) const } } +void ConfigTreeNew::checkUniqueAttr(const std::string &attr) const +{ + checkKeyname(attr); + + if (_visited_params.find("<xmlattr>." + attr) != _visited_params.end()) { + error("Attribute \"" + attr + "\" has already been processed."); + } +} + ConfigTreeNew::CountType& ConfigTreeNew:: -markVisited(std::string const& key, bool peek_only) const +markVisited(std::string const& key, bool const peek_only) const { return markVisited<ConfigTreeNew>(key, peek_only); } @@ -220,18 +246,48 @@ markVisitedDecrement(std::string const& key) const } } +bool +ConfigTreeNew::hasChildren(ConfigTreeNew const& ct) const +{ + auto const& tree = *ct._tree; + if (tree.begin() == tree.end()) + return false; // no children + if (tree.front().first == "<xmlattr>" + && (++tree.begin()) == tree.end()) + return false; // only attributes + + return true; +} + void ConfigTreeNew::checkAndInvalidate() { if (!_tree) return; - for (auto const& p : *_tree) - { - markVisitedDecrement(p.first); + // Note: due to a limitation in boost::property_tree it is not possible + // to discriminate between <tag></tag> and <tag/> in the input file. + // In both cases data() will be empty. + if ((!_have_read_data) && !_tree->data().empty()) { + warning("The immediate data `" + shortString(_tree->data()) + +"' of this tag has not been read."); + } + + // iterate over children + for (auto const& p : *_tree) { + DBUG("-- %s <%s> ", _path.c_str(), p.first.c_str()); + if (p.first != "<xmlattr>") // attributes are handled below + markVisitedDecrement(p.first); + } + + // iterate over attributes + if (auto attrs = _tree->get_child_optional("<xmlattr>")) { + for (auto const& p : *attrs) { + markVisitedDecrement("<xmlattr>." + p.first); + // markVisitedDecrement("<xmlattr>"); + } } - for (auto const& p : _visited_params) - { + 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."); diff --git a/BaseLib/ConfigTreeNew.h b/BaseLib/ConfigTreeNew.h index 66e553f4b564fe7b9e7a64a46bc4a6799cc51f65..1cc7a6ae835ebf8b0b87b12a33dcadfc12737f55 100644 --- a/BaseLib/ConfigTreeNew.h +++ b/BaseLib/ConfigTreeNew.h @@ -62,6 +62,9 @@ template<typename Iterator> class Range; * possible to read from this class, which configuration parameters are present in the tree. * This restriction, however, is intended, because it provides the possibility to get all * existing configuration parameters from the source code. + * + * \todo add usage description + * */ class ConfigTreeNew final { @@ -80,7 +83,7 @@ public: explicit SubtreeIterator(Iterator it, std::string const& root, ConfigTreeNew const& parent) - : _it(it), _root(root), _parent(parent) + : _it(it), _tagname(root), _parent(parent) {} SubtreeIterator& operator++() { @@ -94,9 +97,9 @@ public: // tell the _parent instance that a subtree now has been parsed. if (_has_incremented) { _has_incremented = false; - _parent.markVisited(_root); + _parent.markVisited(_tagname, false); } - return ConfigTreeNew(_it->second, _parent, _root); + return ConfigTreeNew(_it->second, _parent, _tagname); } bool operator==(SubtreeIterator const& other) const { @@ -110,7 +113,7 @@ public: private: bool _has_incremented = true; Iterator _it; - std::string const _root; + std::string const _tagname; ConfigTreeNew const& _parent; }; @@ -130,7 +133,7 @@ public: explicit ValueIterator(Iterator it, std::string const& root, ConfigTreeNew const& parent) - : _it(it), _root(root), _parent(parent) + : _it(it), _tagname(root), _parent(parent) {} ValueIterator<ValueType>& operator++() { @@ -144,21 +147,22 @@ public: // tell the _parent instance that a setting now has been parsed. if (_has_incremented) { _has_incremented = false; - _parent.markVisited<ValueType>(_root); + _parent.markVisited<ValueType>(_tagname, false); } if (_it->second.begin() != _it->second.end()) { - _parent.error("Configuration at key " + _root + " has subitems."); - return ValueType(); + // TODO test + _parent.error("Configuration at key " + _tagname + " has subitems."); + // TODO what about attributes? } - auto v = _it->second.get_value_optional<ValueType>(); - - if (v) return *v; + // TODO maybe better make complete ConfigTree + if (auto v = _it->second.get_value_optional<ValueType>()) + return *v; // TODO: change error method - _parent.error("Could not get value out of key " + _root + "."); - return ValueType(); + // TODO test + _parent.error("Could not get value out of key " + _tagname + "."); } bool operator==(ValueIterator<ValueType> const& other) const { @@ -172,7 +176,7 @@ public: private: bool _has_incremented = true; Iterator _it; - std::string const _root; + std::string const _tagname; ConfigTreeNew const& _parent; }; @@ -261,6 +265,26 @@ public: template<typename T> Range<ValueIterator<T> > getConfParamList(std::string const& param) const; + //! TODO doc + ConfigTreeNew + getConfParam(std::string const& param) const; + + //! TODO doc + boost::optional<ConfigTreeNew> + getConfParamOptional(std::string const& param) const; + + //! TODO doc + Range<SubtreeIterator> + getConfParamList(std::string const& param) const; + + //! TODO doc + template<typename T> T + getValue() const; + + //! TODO doc + template<typename T> T + getAttribute(std::string const& attr) const; + /*! Peek at a parameter \c param of type \c T from the configuration tree. * * This method is an exception to the single-read rule. It is meant to be used to @@ -347,7 +371,7 @@ private: //! Called if an error occurs. Will call the error callback. //! This method only acts as a helper method. - void error(std::string const& message) const; + [[noreturn]] void error(std::string const& message) const; //! Called for printing warning messages. Will call the warning callback. //! This method only acts as a helper method. @@ -362,6 +386,9 @@ private: //! Asserts that the \c key has not been read yet. void checkUnique(std::string const& key) const; + //! TODO doc + void checkUniqueAttr(std::string const& attr) const; + /*! Keeps track of the key \c key and its value type \c T. * * This method asserts that a key is read always with the same type. @@ -377,12 +404,15 @@ private: * * \c param peek_only if true, do not change the read-count of the given key. */ - CountType& markVisited(std::string const& key, bool peek_only = false) const; + CountType& markVisited(std::string const& key, bool const peek_only = false) const; //! Used in the destructor to compute the difference between number of reads of a parameter //! and the number of times it exists in the ConfigTree void markVisitedDecrement(std::string const& key) const; + //! TODO doc + bool hasChildren(ConfigTreeNew const& ct) const; + /*! Checks if the top level of this tree has been read entirely (and not too often). * * Caution: This method also invalidates the instance, i.e., afterwards it can not @@ -399,6 +429,8 @@ private: //! A path printed in error/warning messages. std::string _path; + //! \todo add file name + //! A map key -> (count, type) keeping track which parameters have been read how often //! and which datatype they have. //! @@ -408,6 +440,9 @@ private: //! temporaries. mutable std::map<std::string, CountType> _visited_params; + //! \todo doc + mutable bool _have_read_data = false; + Callback _onerror; Callback _onwarning;