Skip to content
Snippets Groups Projects
Commit 244fa211 authored by Christoph Lehmann's avatar Christoph Lehmann
Browse files

[BL] config tree: more complete support for XML attributes

parent de1d1463
No related branches found
No related tags found
No related merge requests found
...@@ -142,6 +142,17 @@ template<typename T> ...@@ -142,6 +142,17 @@ template<typename T>
T T
ConfigTreeNew:: ConfigTreeNew::
getConfAttribute(std::string const& attr) const 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); checkUniqueAttr(attr);
auto& ct = markVisited<T>(attr, true, true); auto& ct = markVisited<T>(attr, true, true);
...@@ -150,18 +161,16 @@ getConfAttribute(std::string const& attr) const ...@@ -150,18 +161,16 @@ getConfAttribute(std::string const& attr) const
if (auto a = attrs->get_child_optional(attr)) { if (auto a = attrs->get_child_optional(attr)) {
++ct.count; // count only if attribute has been found ++ct.count; // count only if attribute has been found
if (auto v = a->get_value_optional<T>()) { if (auto v = a->get_value_optional<T>()) {
return *v; return v;
} else { } else {
error("Value for XML attribute \"" + attr + "\" `" error("Value for XML attribute \"" + attr + "\" `"
+ shortString(a->data()) + shortString(a->data())
+ "' not convertible to the desired type."); + "' 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> template<typename T>
......
...@@ -84,7 +84,7 @@ ConfigTreeNew:: ...@@ -84,7 +84,7 @@ ConfigTreeNew::
getConfParam(std::string const& root) const getConfParam(std::string const& root) const
{ {
auto ct = getConfSubtree(root); auto ct = getConfSubtree(root);
if (hasChildren(ct)) if (ct.hasChildren())
error("Requested parameter <" + root + "> actually is a subtree."); error("Requested parameter <" + root + "> actually is a subtree.");
return ct; return ct;
} }
...@@ -94,11 +94,25 @@ ConfigTreeNew:: ...@@ -94,11 +94,25 @@ ConfigTreeNew::
getConfParamOptional(std::string const& root) const getConfParamOptional(std::string const& root) const
{ {
auto ct = getConfSubtreeOptional(root); auto ct = getConfSubtreeOptional(root);
if (ct && hasChildren(*ct)) if (ct && ct->hasChildren())
error("Requested parameter <" + root + "> actually is a subtree."); error("Requested parameter <" + root + "> actually is a subtree.");
return ct; return ct;
} }
Range<ConfigTreeNew::ParameterIterator>
ConfigTreeNew::
getConfParamList(const std::string &param) 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
ConfigTreeNew:: ConfigTreeNew::
getConfSubtree(std::string const& root) const getConfSubtree(std::string const& root) const
...@@ -117,10 +131,10 @@ getConfSubtreeOptional(std::string const& root) const ...@@ -117,10 +131,10 @@ getConfSubtreeOptional(std::string const& root) const
checkUnique(root); checkUnique(root);
if (auto subtree = _tree->get_child_optional(root)) { if (auto subtree = _tree->get_child_optional(root)) {
markVisited(root, false); markVisited(root, false, false);
return ConfigTreeNew(*subtree, *this, root); return ConfigTreeNew(*subtree, *this, root);
} else { } else {
markVisited(root, true); markVisited(root, false, true);
return boost::none; return boost::none;
} }
} }
...@@ -130,7 +144,7 @@ ConfigTreeNew:: ...@@ -130,7 +144,7 @@ ConfigTreeNew::
getConfSubtreeList(std::string const& root) const getConfSubtreeList(std::string const& root) const
{ {
checkUnique(root); checkUnique(root);
markVisited(root, true); markVisited(root, false, true);
auto p = _tree->equal_range(root); auto p = _tree->equal_range(root);
...@@ -144,13 +158,24 @@ void ConfigTreeNew::ignoreConfParam(const std::string &param) const ...@@ -144,13 +158,24 @@ void ConfigTreeNew::ignoreConfParam(const std::string &param) const
checkUnique(param); checkUnique(param);
// if not found, peek only // if not found, peek only
bool peek_only = _tree->find(param) == _tree->not_found(); 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 &param) const void ConfigTreeNew::ignoreConfParamAll(const std::string &param) const
{ {
checkUnique(param); checkUnique(param);
auto& ct = markVisited(param, true); auto& ct = markVisited(param, false, true);
auto p = _tree->equal_range(param); auto p = _tree->equal_range(param);
for (auto it = p.first; it != p.second; ++it) { for (auto it = p.first; it != p.second; ++it) {
...@@ -236,7 +261,23 @@ void ConfigTreeNew::checkUnique(const std::string &key) const ...@@ -236,7 +261,23 @@ void ConfigTreeNew::checkUnique(const std::string &key) const
void ConfigTreeNew::checkUniqueAttr(const std::string &attr) 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()) { if (_visited_params.find({true, attr}) != _visited_params.end()) {
error("Attribute \"" + attr + "\" has already been processed."); error("Attribute \"" + attr + "\" has already been processed.");
...@@ -245,9 +286,9 @@ void ConfigTreeNew::checkUniqueAttr(const std::string &attr) const ...@@ -245,9 +286,9 @@ void ConfigTreeNew::checkUniqueAttr(const std::string &attr) const
ConfigTreeNew::CountType& ConfigTreeNew::CountType&
ConfigTreeNew:: 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 void
...@@ -266,9 +307,9 @@ markVisitedDecrement(bool const is_attr, std::string const& key) const ...@@ -266,9 +307,9 @@ markVisitedDecrement(bool const is_attr, std::string const& key) const
} }
bool bool
ConfigTreeNew::hasChildren(ConfigTreeNew const& ct) const ConfigTreeNew::hasChildren() const
{ {
auto const& tree = *ct._tree; auto const& tree = *_tree;
if (tree.begin() == tree.end()) if (tree.begin() == tree.end())
return false; // no children return false; // no children
if (tree.front().first == "<xmlattr>" if (tree.front().first == "<xmlattr>"
......
...@@ -89,8 +89,7 @@ template<typename Iterator> class Range; ...@@ -89,8 +89,7 @@ template<typename Iterator> class Range;
class ConfigTreeNew final class ConfigTreeNew final
{ {
public: 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 * The methods of this class tell the associated (parent) \c ConfigTree object when
* a setting has been parsed. * a setting has been parsed.
...@@ -117,7 +116,7 @@ public: ...@@ -117,7 +116,7 @@ public:
// tell the _parent instance that a subtree now has been parsed. // tell the _parent instance that a subtree now has been parsed.
if (_has_incremented) { if (_has_incremented) {
_has_incremented = false; _has_incremented = false;
_parent.markVisited(_tagname, false); _parent.markVisited(_tagname, false, false);
} }
return ConfigTreeNew(_it->second, _parent, _tagname); return ConfigTreeNew(_it->second, _parent, _tagname);
} }
...@@ -133,10 +132,32 @@ public: ...@@ -133,10 +132,32 @@ public:
private: private:
bool _has_incremented = true; bool _has_incremented = true;
Iterator _it; Iterator _it;
protected:
std::string const _tagname; std::string const _tagname;
ConfigTreeNew const& _parent; 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. * A wrapper around a Boost Iterator for iterating over ranges of values.
...@@ -265,7 +286,7 @@ public: ...@@ -265,7 +286,7 @@ public:
template<typename T> boost::optional<T> template<typename T> boost::optional<T>
getConfParamOptional(std::string const& param) const; 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. * The return value is suitable to be used with range-base for-loops.
* *
...@@ -282,9 +303,8 @@ public: ...@@ -282,9 +303,8 @@ public:
* parameters---check that the queried parameters do not have any children (apart from XML * parameters---check that the queried parameters do not have any children (apart from XML
* attributes); if they do, error() is called. * attributes); if they do, error() is called.
* *
* The support for parameters with attributes is limited in the sense that there are no * The support for parameters with attributes is limited in the sense that it is not
* <tt>get...List()</tt> methods in this group and that it is not possible to explicitly * possible to peek/check them. However, such functionality can easily be added on demand.
* ignore attibutes. However, such functionality can easily be added on demand.
*/ */
//!\{ //!\{
...@@ -306,6 +326,17 @@ public: ...@@ -306,6 +326,17 @@ public:
boost::optional<ConfigTreeNew> boost::optional<ConfigTreeNew>
getConfParamOptional(std::string const& param) const; 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. /*! Get the plain data contained in the current level of the tree.
* *
* \return the data converted to the type \c T * \return the data converted to the type \c T
...@@ -324,6 +355,17 @@ public: ...@@ -324,6 +355,17 @@ public:
template<typename T> T template<typename T> T
getConfAttribute(std::string const& attr) const; 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 /*! \name Methods for peeking and checking parameters
...@@ -401,7 +443,7 @@ public: ...@@ -401,7 +443,7 @@ public:
* *
* This method is used to avoid warning messages. * 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; void ignoreConfParam(std::string const& param) const;
...@@ -409,10 +451,18 @@ public: ...@@ -409,10 +451,18 @@ public:
* *
* This method is used to avoid warning messages. * 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; 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 //! The destructor performs the check if all nodes at the current level of the tree
...@@ -481,14 +531,15 @@ private: ...@@ -481,14 +531,15 @@ private:
* *
* \c param peek_only if true, do not change the read-count of the given key. * \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 //! 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 //! and the number of times it exists in the ConfigTree
void markVisitedDecrement(bool const is_attr, std::string const& key) const; void markVisitedDecrement(bool const is_attr, std::string const& key) const;
//! Checks if the tree \c ct has any children. //! Checks if this tree has any children.
bool hasChildren(ConfigTreeNew const& ct) const; bool hasChildren() const;
/*! Checks if the top level of this tree has been read entirely (and not too often). /*! Checks if the top level of this tree has been read entirely (and not too often).
* *
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment