diff --git a/BaseLib/ConfigTreeNew-impl.h b/BaseLib/ConfigTreeNew-impl.h index fa006aa0db7a223ebefaa23d22d8e24246337985..6bce421e56448eb18f9e50b53a19d5132a3682d7 100644 --- a/BaseLib/ConfigTreeNew-impl.h +++ b/BaseLib/ConfigTreeNew-impl.h @@ -142,6 +142,17 @@ template<typename T> T ConfigTreeNew:: getConfAttribute(std::string const& attr) const +{ + if (auto a = getConfAttributeOptional<T>(attr)) + return *a; + + error("Did not find XML attribute with name \"" + attr + "\"."); +} + +template<typename T> +boost::optional<T> +ConfigTreeNew:: +getConfAttributeOptional(std::string const& attr) const { checkUniqueAttr(attr); auto& ct = markVisited<T>(attr, true, true); @@ -150,18 +161,16 @@ getConfAttribute(std::string const& attr) const if (auto a = attrs->get_child_optional(attr)) { ++ct.count; // count only if attribute has been found if (auto v = a->get_value_optional<T>()) { - return *v; + return v; } else { error("Value for XML attribute \"" + attr + "\" `" + shortString(a->data()) + "' not convertible to the desired type."); } - } else { - error("Did not find XML attribute with name \"" + attr + "\""); } - } else { - error("This parameter has no XML attributes"); } + + return boost::none; } template<typename T> diff --git a/BaseLib/ConfigTreeNew.cpp b/BaseLib/ConfigTreeNew.cpp index cf78d169720263ed2da2f1976dc3eafaf78a3c41..fffbdad9efb027875a1ab8e962983aea1df92205 100644 --- a/BaseLib/ConfigTreeNew.cpp +++ b/BaseLib/ConfigTreeNew.cpp @@ -84,7 +84,7 @@ ConfigTreeNew:: getConfParam(std::string const& root) const { auto ct = getConfSubtree(root); - if (hasChildren(ct)) + if (ct.hasChildren()) error("Requested parameter <" + root + "> actually is a subtree."); return ct; } @@ -94,11 +94,25 @@ ConfigTreeNew:: getConfParamOptional(std::string const& root) const { auto ct = getConfSubtreeOptional(root); - if (ct && hasChildren(*ct)) + if (ct && ct->hasChildren()) error("Requested parameter <" + root + "> actually is a subtree."); return ct; } +Range<ConfigTreeNew::ParameterIterator> +ConfigTreeNew:: +getConfParamList(const std::string ¶m) const +{ + checkUnique(param); + markVisited(param, false, true); + + auto p = _tree->equal_range(param); + + return Range<ParameterIterator>( + ParameterIterator(p.first, param, *this), + ParameterIterator(p.second, param, *this)); +} + ConfigTreeNew ConfigTreeNew:: getConfSubtree(std::string const& root) const @@ -117,10 +131,10 @@ getConfSubtreeOptional(std::string const& root) const checkUnique(root); if (auto subtree = _tree->get_child_optional(root)) { - markVisited(root, false); + markVisited(root, false, false); return ConfigTreeNew(*subtree, *this, root); } else { - markVisited(root, true); + markVisited(root, false, true); return boost::none; } } @@ -130,7 +144,7 @@ ConfigTreeNew:: getConfSubtreeList(std::string const& root) const { checkUnique(root); - markVisited(root, true); + markVisited(root, false, true); auto p = _tree->equal_range(root); @@ -144,13 +158,24 @@ 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); + markVisited(param, false, peek_only); +} + +void ConfigTreeNew::ignoreConfAttribute(const std::string &attr) const +{ + checkUniqueAttr(attr); + + // Exercise: Guess what not! (hint: if not found, peek only) + // Btw. (not a hint) _tree->find() does not seem to work here. + bool peek_only = !_tree->get_child_optional("<xmlattr>." + attr); + + markVisited(attr, true, peek_only); } void ConfigTreeNew::ignoreConfParamAll(const std::string ¶m) const { checkUnique(param); - auto& ct = markVisited(param, true); + auto& ct = markVisited(param, false, true); auto p = _tree->equal_range(param); for (auto it = p.first; it != p.second; ++it) { @@ -236,7 +261,23 @@ void ConfigTreeNew::checkUnique(const std::string &key) const void ConfigTreeNew::checkUniqueAttr(const std::string &attr) const { - checkKeyname(attr); + // Workaround for handling attributes with xml namespaces and uppercase letters. + if (attr.find(':') != attr.npos) { + auto pos = decltype(attr.npos){0}; + + // Replace colon and uppercase letters with an allowed character 'a'. + // That means, attributes containing a colon are also allowed to contain + // uppercase letters. + auto attr2 = attr; + do { + pos = attr2.find_first_of(":ABCDEFGHIJKLMNOPQRSTUVWXYZ", pos); + if (pos != attr.npos) attr2[pos] = 'a'; + } while (pos != attr.npos); + + checkKeyname(attr2); + } else { + checkKeyname(attr); + } if (_visited_params.find({true, attr}) != _visited_params.end()) { error("Attribute \"" + attr + "\" has already been processed."); @@ -245,9 +286,9 @@ void ConfigTreeNew::checkUniqueAttr(const std::string &attr) const ConfigTreeNew::CountType& ConfigTreeNew:: -markVisited(std::string const& key, bool const peek_only) const +markVisited(std::string const& key, bool const is_attr, bool const peek_only) const { - return markVisited<ConfigTreeNew>(key, false, peek_only); + return markVisited<ConfigTreeNew>(key, is_attr, peek_only); } void @@ -266,9 +307,9 @@ markVisitedDecrement(bool const is_attr, std::string const& key) const } bool -ConfigTreeNew::hasChildren(ConfigTreeNew const& ct) const +ConfigTreeNew::hasChildren() const { - auto const& tree = *ct._tree; + auto const& tree = *_tree; if (tree.begin() == tree.end()) return false; // no children if (tree.front().first == "<xmlattr>" diff --git a/BaseLib/ConfigTreeNew.h b/BaseLib/ConfigTreeNew.h index 98edbcba98e75e4cdfa61977a6da4f42786cd9b3..112700f3cb1e1b6aa65b52dfb40ab315c7486987 100644 --- a/BaseLib/ConfigTreeNew.h +++ b/BaseLib/ConfigTreeNew.h @@ -89,8 +89,7 @@ template<typename Iterator> class Range; class ConfigTreeNew final { public: - /*! - * A wrapper around a Boost Iterator for iterating over ranges of subtrees. + /*! A wrapper around a Boost Iterator for iterating over ranges of subtrees. * * The methods of this class tell the associated (parent) \c ConfigTree object when * a setting has been parsed. @@ -117,7 +116,7 @@ public: // tell the _parent instance that a subtree now has been parsed. if (_has_incremented) { _has_incremented = false; - _parent.markVisited(_tagname, false); + _parent.markVisited(_tagname, false, false); } return ConfigTreeNew(_it->second, _parent, _tagname); } @@ -133,10 +132,32 @@ public: private: bool _has_incremented = true; Iterator _it; + + protected: std::string const _tagname; ConfigTreeNew const& _parent; }; + /*! A wrapper around a Boost Iterator for iterating over ranges of parameters. + * + * The methods of this class tell the associated (parent) \c ConfigTree object when + * a setting has been parsed. + */ + class ParameterIterator : public SubtreeIterator + { + public: + // Inherit the constructor + using SubtreeIterator::SubtreeIterator; + + ConfigTreeNew operator*() { + auto st = SubtreeIterator::operator*(); + if (st.hasChildren()) + _parent.error("The requested parameter <" + _tagname + ">" + " has child elements."); + return st; + } + }; + /*! * A wrapper around a Boost Iterator for iterating over ranges of values. @@ -265,7 +286,7 @@ public: template<typename T> boost::optional<T> getConfParamOptional(std::string const& param) const; - /*! Returns all parameters with the name \c param from the current level of the tree. + /*! Fetches all parameters with name \c param from the current level of the tree. * * The return value is suitable to be used with range-base for-loops. * @@ -282,9 +303,8 @@ public: * parameters---check that the queried parameters do not have any children (apart from XML * attributes); if they do, error() is called. * - * The support for parameters with attributes is limited in the sense that there are no - * <tt>get...List()</tt> methods in this group and that it is not possible to explicitly - * ignore attibutes. However, such functionality can easily be added on demand. + * The support for parameters with attributes is limited in the sense that it is not + * possible to peek/check them. However, such functionality can easily be added on demand. */ //!\{ @@ -306,6 +326,17 @@ public: boost::optional<ConfigTreeNew> getConfParamOptional(std::string const& param) const; + /*! Fetches all parameters with name \c param from the current level of the tree. + * + * The return value is suitable to be used with range-base for-loops. + * + * \pre \c param must not have been read before from this ConfigTree. + * + * \todo write tests + */ + Range<ParameterIterator> + getConfParamList(std::string const& param) const; + /*! Get the plain data contained in the current level of the tree. * * \return the data converted to the type \c T @@ -324,6 +355,17 @@ public: template<typename T> T getConfAttribute(std::string const& attr) const; + /*! Get XML attribute \c attr of type \c T for the current parameter if present. + * + * \return the requested attribute + * + * \pre \c param must not have been read before from this ConfigTree. + * + * \todo write tests + */ + template<typename T> boost::optional<T> + getConfAttributeOptional(std::string const& attr) const; + //!\} /*! \name Methods for peeking and checking parameters @@ -401,7 +443,7 @@ public: * * This method is used to avoid warning messages. * - * \pre \c root must not have been read before from this ConfigTree. + * \pre \c param must not have been read before from this ConfigTree. */ void ignoreConfParam(std::string const& param) const; @@ -409,10 +451,18 @@ public: * * This method is used to avoid warning messages. * - * \pre \c root must not have been read before from this ConfigTree. + * \pre \c param must not have been read before from this ConfigTree. */ void ignoreConfParamAll(std::string const& param) const; + /*! Tell this instance to ignore the XML attribute \c attr. + * + * This method is used to avoid warning messages. + * + * \pre \c attr must not have been read before from this ConfigTree. + */ + void ignoreConfAttribute(std::string const& attr) const; + //!\} //! The destructor performs the check if all nodes at the current level of the tree @@ -481,14 +531,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 const peek_only) const; + CountType& markVisited(std::string const& key, bool const is_attr, + bool const peek_only) 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(bool const is_attr, std::string const& key) const; - //! Checks if the tree \c ct has any children. - bool hasChildren(ConfigTreeNew const& ct) const; + //! Checks if this tree has any children. + bool hasChildren() const; /*! Checks if the top level of this tree has been read entirely (and not too often). *