summaryrefslogtreecommitdiff
path: root/zenXml/zenxml
diff options
context:
space:
mode:
Diffstat (limited to 'zenXml/zenxml')
-rw-r--r--zenXml/zenxml/cvrt_struc.h4
-rw-r--r--zenXml/zenxml/dom.h110
-rw-r--r--zenXml/zenxml/parser.h14
-rw-r--r--zenXml/zenxml/xml.h246
4 files changed, 141 insertions, 233 deletions
diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h
index 0bfe5996..40277556 100644
--- a/zenXml/zenxml/cvrt_struc.h
+++ b/zenXml/zenxml/cvrt_struc.h
@@ -124,9 +124,9 @@ struct ConvertElement<T, ValueType::stlContainer>
value.clear();
bool success = true;
- const auto itPair = input.getChildren("Item");
+ auto [it, itEnd] = input.getChildren();
- std::for_each(itPair.first, itPair.second, [&](const XmlElement& xmlChild)
+ std::for_each(it, itEnd, [&](const XmlElement& xmlChild)
{
typename T::value_type childVal;
if (zen::readStruc(xmlChild, childVal))
diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h
index cba12cfb..89320db1 100644
--- a/zenXml/zenxml/dom.h
+++ b/zenXml/zenxml/dom.h
@@ -82,8 +82,8 @@ public:
it->second->value = std::move(attrValue);
else
{
- auto itBack = attributes_.insert(attributes_.end(), {name, std::move(attrValue)});
- attributesByName.emplace(std::move(name), itBack);
+ attributes_.push_back({name, std::move(attrValue)});
+ attributesByName.emplace(std::move(name), --attributes_.end());
}
static_assert(std::is_same_v<decltype(attributes_), std::list<Attribute>>); //must NOT invalidate references used in "attributesByName"!
}
@@ -97,6 +97,7 @@ public:
attributes_.erase(it->second);
attributesByName.erase(it);
}
+ else assert(false);
}
///Create a new child element and return a reference to it.
@@ -107,9 +108,9 @@ public:
{
childElements_.emplace_back(name, this);
XmlElement& newElement = childElements_.back();
- childElementsByName_.emplace(std::move(name), &newElement);
+ childElementByName_.emplace(std::move(name), --childElements_.end());
- static_assert(std::is_same_v<decltype(childElements_), std::list<XmlElement>>); //must NOT invalidate references used in "childElementsByName_"!
+ static_assert(std::is_same_v<decltype(childElements_), std::list<XmlElement>>); //must NOT invalidate references used in "childElementByName_"!
return newElement;
}
@@ -120,8 +121,8 @@ public:
*/
const XmlElement* getChild(const std::string& name) const
{
- auto it = childElementsByName_.find(name);
- return it == childElementsByName_.end() ? nullptr : it->second;
+ auto it = childElementByName_.find(name);
+ return it == childElementByName_.end() ? nullptr : &*(it->second);
}
///\sa getChild
@@ -130,71 +131,16 @@ public:
return const_cast<XmlElement*>(static_cast<const XmlElement*>(this)->getChild(name));
}
- template <class IterTy, //underlying iterator type
- class T, //target object type
- class AccessPolicy> //access policy: see AccessPtrMap
- class PtrIter : private AccessPolicy //get rid of shared_ptr indirection
- {
- public:
- using iterator_category = std::input_iterator_tag;
- using value_type = T;
- using difference_type = ptrdiff_t;
- using pointer = T*;
- using reference = T&;
-
- PtrIter(IterTy it) : it_(it) {}
- //PtrIter(const PtrIter& other) : it_(other.it_) {}
- PtrIter& operator++() { ++it_; return *this; }
- PtrIter operator++(int) { PtrIter tmp(*this); operator++(); return tmp; }
- inline friend bool operator==(const PtrIter& lhs, const PtrIter& rhs) { return lhs.it_ == rhs.it_; }
- T& operator* () const { return AccessPolicy::template objectRef<T>(it_); }
- T* operator->() const { return &AccessPolicy::template objectRef<T>(it_); }
- private:
- IterTy it_;
- };
-
- struct AccessMapElement
- {
- template <class T, class IterTy>
- T& objectRef(const IterTy& it) const { return *(it->second); }
- };
-
- using ChildIter2 = PtrIter<std::multimap<std::string, XmlElement*>::iterator, XmlElement, AccessMapElement>;
- using ChildIterConst2 = PtrIter<std::multimap<std::string, XmlElement*>::const_iterator, const XmlElement, AccessMapElement>;
-
- ///Access all child elements with the given name via STL iterators.
- /**
- \code
- auto itPair = elem.getChildren("Item");
- std::for_each(iterPair.first, iterPair.second,
- [](const XmlElement& child) { ... });
- \endcode
- \param name The name of the child elements to be retrieved.
- \return A pair of STL begin/end iterators to access the child elements sequentially.
- */
- std::pair<ChildIterConst2, ChildIterConst2> getChildren(const std::string& name) const { return childElementsByName_.equal_range(name); }
-
- ///\sa getChildren
- std::pair<ChildIter2, ChildIter2> getChildren(const std::string& name) { return childElementsByName_.equal_range(name); }
-
- struct AccessListElement
- {
- template <class T, class IterTy>
- T& objectRef(const IterTy& it) const { return *it; }
- };
-
- using ChildIter = PtrIter<std::list<XmlElement>::iterator, XmlElement, AccessListElement>;
- using ChildIterConst = PtrIter<std::list<XmlElement>::const_iterator, const XmlElement, AccessListElement>;
+ using ChildIter = std::list<XmlElement>::iterator;
+ using ChildIterConst = std::list<XmlElement>::const_iterator;
///Access all child elements sequentially via STL iterators.
/**
- \code
- auto itPair = elem.getChildren();
- std::for_each(itPair.first, itPair.second,
- [](const XmlElement& child) { ... });
- \endcode
- \return A pair of STL begin/end iterators to access all child elements sequentially.
- */
+ \code
+ auto [it, itEnd] = elem.getChildren();
+ std::for_each(it, itEnd, [](const XmlElement& child) { ... });
+ \endcode
+ \return A pair of STL begin/end iterators to access all child elements sequentially. */
std::pair<ChildIterConst, ChildIterConst> getChildren() const { return {childElements_.begin(), childElements_.end()}; }
///\sa getChildren
@@ -213,24 +159,24 @@ public:
using AttrIter = std::list<Attribute>::const_iterator;
/* -> disabled documentation extraction
- \brief Get all attributes associated with the element.
- \code
+ \brief Get all attributes associated with the element.
+ \code
auto itPair = elem.getAttributes();
for (auto it = itPair.first; it != itPair.second; ++it)
std::cout << std::string("name: ") + it->name + " value: " + it->value + '\n';
- \endcode
- \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. */
+ \endcode
+ \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. */
std::pair<AttrIter, AttrIter> getAttributes() const { return {attributes_.begin(), attributes_.end()}; }
//swap two elements while keeping references to parent. -> disabled documentation extraction
void swapSubtree(XmlElement& other) noexcept
{
- name_ .swap(other.name_);
- value_ .swap(other.value_);
- attributes_ .swap(other.attributes_);
- attributesByName .swap(other.attributesByName);
- childElements_ .swap(other.childElements_);
- childElementsByName_.swap(other.childElementsByName_);
+ name_ .swap(other.name_);
+ value_ .swap(other.value_);
+ attributes_ .swap(other.attributes_);
+ attributesByName .swap(other.attributesByName);
+ childElements_ .swap(other.childElements_);
+ childElementByName_.swap(other.childElementByName_);
for (XmlElement& child : childElements_)
child.parent_ = this;
@@ -248,12 +194,10 @@ private:
std::list<Attribute> attributes_; //attributes in order of creation
std::unordered_map<std::string, std::list<Attribute>::iterator> attributesByName; //alternate view for lookup
- std::list<XmlElement> childElements_; //child elements in order of creation
- std::multimap<std::string, XmlElement*> childElementsByName_; //alternate view for lookup
- //alternative: std::unordered_map => but let's keep std::map, so which guarantees consistent order of duplicate items!
- //e.g. std::unordered_map on Linux inserts duplicates in reverse!
+ std::list<XmlElement> childElements_; //child elements in order of creation
+ std::unordered_map<std::string, std::list<XmlElement>::iterator> childElementByName_; //alternate view for lookup of (*first*) child by name
- XmlElement* parent_ = nullptr;
+ XmlElement* parent_ = nullptr; //currently unused: YAGNI?
};
diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h
index 8416c211..e089a86f 100644
--- a/zenXml/zenxml/parser.h
+++ b/zenXml/zenxml/parser.h
@@ -206,14 +206,14 @@ void serialize(const XmlElement& element, std::string& stream,
for (auto it = attr.first; it != attr.second; ++it)
stream += ' ' + normalizeName(it->name) + "=\"" + normalizeAttribValue(it->value) + '"';
- auto itPair = element.getChildren();
- if (itPair.first != itPair.second) //structured element
+ auto [it, itEnd] = element.getChildren();
+ if (it != itEnd) //structured element
{
//no support for mixed-mode content
stream += '>' + lineBreak;
- std::for_each(itPair.first, itPair.second,
- [&](const XmlElement& el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); });
+ std::for_each(it, itEnd, [&](const XmlElement& el)
+ { serialize(el, stream, lineBreak, indent, indentLevel + 1); });
for (size_t i = 0; i < indentLevel; ++i)
stream += indent;
@@ -483,9 +483,9 @@ public:
XmlElement dummy;
parseChildElements(dummy);
- auto itPair = dummy.getChildren();
- if (itPair.first != itPair.second)
- doc.root().swapSubtree(*itPair.first);
+ auto [it, itEnd] = dummy.getChildren();
+ if (it != itEnd)
+ doc.root().swapSubtree(*it);
expectToken(Token::TK_END); //throw XmlParsingError
return doc;
diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h
index 8b86a49f..d4748bca 100644
--- a/zenXml/zenxml/xml.h
+++ b/zenXml/zenxml/xml.h
@@ -109,7 +109,7 @@ class XmlOut
public:
///Construct an output proxy for an XML document
/**
- \code
+ \code
zen::XmlDoc doc;
zen::XmlOut out(doc);
@@ -118,18 +118,18 @@ public:
out["elem3"](-3); //
saveXml(doc, "out.xml"); //throw FileError
- \endcode
- Output:
- \verbatim
+ \endcode
+ Output:
+ \verbatim
<?xml version="1.0" encoding="utf-8"?>
<Root>
<elem1>1</elem1>
<elem2>2</elem2>
<elem3>-3</elem3>
</Root>
- \endverbatim
+ \endverbatim
*/
- explicit XmlOut(XmlDoc& doc) : ref_(&doc.root()) {}
+ explicit XmlOut(XmlDoc& doc) : ref_(doc.root()) {}
///Retrieve a handle to an XML child element for writing
/**
@@ -138,8 +138,8 @@ public:
*/
XmlOut operator[](std::string name) const
{
- XmlElement* child = ref_->getChild(name);
- return XmlOut(child ? *child : ref_->addChild(std::move(name)));
+ XmlElement* child = ref_.getChild(name);
+ return XmlOut(child ? *child : ref_.addChild(std::move(name)));
}
///Retrieve a handle to an XML child element for writing
@@ -150,7 +150,7 @@ public:
*/
XmlOut addChild(std::string name) const
{
- return XmlOut(ref_->addChild(std::move(name)));
+ return XmlOut(ref_.addChild(std::move(name)));
}
///Write user data to the underlying XML element
@@ -159,12 +159,12 @@ public:
\tparam T User type that is converted into an XML element value.
*/
template <class T>
- void operator()(const T& value) { writeStruc(value, *ref_); }
+ void operator()(const T& value) { writeStruc(value, ref_); }
///Write user data to an XML attribute
/**
This conversion requires a specialization of zen::writeText() for type T.
- \code
+ \code
zen::XmlDoc doc;
zen::XmlOut out(doc);
@@ -173,53 +173,50 @@ public:
out["elem"].attribute("attr3", -3); //
saveXml(doc, "out.xml"); //throw FileError
- \endcode
+ \endcode
Output:
- \verbatim
+ \verbatim
<?xml version="1.0" encoding="utf-8"?>
<Root>
<elem attr1="1" attr2="2" attr3="-3"/>
</Root>
- \endverbatim
+ \endverbatim
\tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers
\sa XmlElement::setAttribute()
*/
template <class T>
- void attribute(std::string name, const T& value) { ref_->setAttribute(std::move(name), value); }
+ void attribute(std::string name, const T& value) { ref_.setAttribute(std::move(name), value); }
private:
///Construct an output proxy for a single XML element
/**
\sa XmlOut(XmlDoc& doc)
*/
- explicit XmlOut(XmlElement& element) : ref_(&element) {}
+ explicit XmlOut(XmlElement& element) : ref_(element) {}
- XmlElement* ref_; //always bound!
+ XmlElement& ref_;
};
///Proxy class to conveniently convert XML structure to user data
class XmlIn
{
- class ErrorLog;
+ struct ErrorLog;
public:
///Construct an input proxy for an XML document
/**
- \code
+ \code
zen::XmlDoc doc;
... //load document
zen::XmlIn in(doc);
in["elem1"](value1); //
in["elem2"](value2); //read data from XML elements into variables "value1", "value2", "value3"
in["elem3"](value3); //
- \endcode
+ \endcode
*/
- explicit XmlIn(const XmlDoc& doc) : nodeNameFormatted_('<' + doc.root().getName() + '>')
- {
- refList_.push_back(&doc.root());
- }
+ explicit XmlIn(const XmlDoc& doc) : XmlIn(&doc.root(), '<' + doc.root().getName() + '>', makeSharedRef<ErrorLog>()) {}
///Retrieve a handle to an XML child element for reading
/**
@@ -228,52 +225,59 @@ public:
*/
XmlIn operator[](const std::string& name) const
{
- std::vector<const XmlElement*> childList;
-
- if (const XmlElement* elem = get())
- {
- auto itPair = elem->getChildren(name);
- std::for_each(itPair.first, itPair.second, [&](const XmlElement& child)
- { childList.push_back(&child); });
- }
-
- return XmlIn(childList, getChildNameFormatted(name), log_);
+ return XmlIn(elem_ ? elem_->getChild(name) : nullptr, elementNameFmt_ + " <" + name + '>', log_);
}
- ///Refer to next sibling element with the same name
+ ///Iterate over XML child elements
/**
- <b>Example:</b> Loop over all XML child elements named "Item"
+ <b>Example:</b> Loop over all XML child elements
\verbatim
- <?xml version="1.0" encoding="utf-8"?>
- <Root>
- <Item>1</Item>
- <Item>3</Item>
- <Item>5</Item>
- </Root>
+ <?xml version="1.0" encoding="utf-8"?>
+ <Root>
+ <Item>1</Item>
+ <Item>3</Item>
+ <Item>5</Item>
+ </Root>
\endverbatim
\code
- zen::XmlIn in(doc);
- ...
- for (zen::XmlIn child = in["Item"]; child; child.next())
- {
+ zen::XmlIn in(doc);
...
- }
+ in.visitChildren([&](const XmlIn& inChild)
+ {
+ ...
+ });
\endcode
*/
- void next() { ++refIndex_; }
+ template <class Function>
+ void visitChildren(Function fun)
+ {
+ if (!elem_)
+ logMissingElement();
+ else if (std::string value; elem_->getValue(value) && !value.empty())
+ logConversionError(); //have XML value element, not container!
+ else
+ {
+ auto [it, itEnd] = elem_->getChildren();
+ size_t childIdx = 0;
+ std::for_each(it, itEnd, [&](const XmlElement& child)
+ {
+ fun(XmlIn(&child, elementNameFmt_ + " <" + child.getName() + ">[" + numberTo<std::string>(++childIdx) + ']', log_));
+ });
+ }
+ }
///Test whether the underlying XML element exists
/**
- \code
- XmlIn in(doc);
- XmlIn child = in["elem1"];
- if (child)
- ...
- \endcode
- Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20)
+ \code
+ XmlIn in(doc);
+ XmlIn child = in["elem1"];
+ if (child)
+ ...
+ \endcode
+ Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20)
*/
- explicit operator bool() const { return get() != nullptr; }
+ explicit operator bool() const { return elem_; }
///Read user data from the underlying XML element
/**
@@ -284,25 +288,22 @@ public:
template <class T>
bool operator()(T& value) const
{
- if (const XmlElement* elem = get())
+ if (elem_)
{
- if (readStruc(*elem, value))
+ if (readStruc(*elem_, value))
return true;
- log_.ref().notifyConversionError(getNameFormatted());
+ logConversionError();
}
else
- log_.ref().notifyMissingElement(getNameFormatted());
+ logMissingElement();
return false;
}
bool hasAttribute(const std::string& name) const
{
- if (const XmlElement* elem = get())
- if (elem->hasAttribute(name))
- return true;
- return false;
+ return elem_ && elem_->hasAttribute(name);
}
///Read user data from an XML attribute
@@ -326,15 +327,15 @@ public:
template <class T>
bool attribute(const std::string& name, T& value) const
{
- if (const XmlElement* elem = get())
+ if (elem_)
{
- if (elem->getAttribute(name, value))
+ if (elem_->getAttribute(name, value))
return true;
- log_.ref().notifyMissingAttribute(getNameFormatted(), name);
+ logMissingAttribute(name);
}
else
- log_.ref().notifyMissingElement(getNameFormatted());
+ logMissingElement();
return false;
}
@@ -342,104 +343,67 @@ public:
///Notifies errors while mapping the XML to user data
/**
Error logging is shared by each hiearchy of XmlIn proxy instances that are created from each other. Consequently it doesn't matter which instance you query for errors:
- \code
+ \code
XmlIn in(doc);
XmlIn inItem = in["item1"];
int value = 0;
inItem(value); //let's assume this conversion failed
- assert(in.haveErrors() == inItem.haveErrors());
- assert(in.getErrorsAs<std::string>() == inItem.getErrorsAs<std::string>());
- \endcode
+ assert(in.getErrors() == inItem.getErrors());
+ \endcode
- Note that error logging is \b NOT global, but owned by all instances of a hierarchy of XmlIn proxies.
- Therefore it's safe to use unrelated XmlIn proxies in multiple threads.
- \n\n
- However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer.
- Errors that occur when working with this pointer are not logged by the original set of related instances.
+ Note that error logging is \b NOT global, but owned by all instances of a hierarchy of XmlIn proxies.
+ Therefore it's safe to use unrelated XmlIn proxies in different threads.
*/
///Get a list of XML element and attribute names which failed to convert to user data.
/**
- \returns A list of XML element and attribute names, empty list if no errors occured.
+ \returns A list of XML element and attribute names, empty if no errors occured.
*/
- std::vector<std::wstring> getErrors() const
- {
- std::vector<std::wstring> output;
+ const std::wstring& getErrors() const { return log_.ref().failedElements; }
- for (const std::string& str : log_.ref().elementList())
- output.push_back(utfTo<std::wstring>(str));
-
- return output;
+ ///Retrieve the name of this XML element.
+ /**
+ \returns Name of the XML element.
+ */
+ const std::string* getName() const
+ {
+ if (elem_)
+ return &elem_->getName();
+ return nullptr;
}
private:
- XmlIn(const std::vector<const XmlElement*>& siblingList,
- const std::string& nodeNameFormatted,
- const SharedRef<ErrorLog>& sharedlog) : refList_(siblingList), nodeNameFormatted_(nodeNameFormatted), log_(sharedlog) {}
-
- ///Return a pointer to the underlying Xml element, may be nullptr
- const XmlElement* get() const { return refIndex_ < refList_.size() ? refList_[refIndex_] : nullptr; }
-
- std::string getNameFormatted() const //"<Root> <Level1> <Level2>"
- {
- if (refIndex_ == 0 && refList_.size() <= 1)
- return nodeNameFormatted_;
- else
- return nodeNameFormatted_ + '[' + numberTo<std::string>(refIndex_ + 1) + ']';
- }
+ XmlIn(const XmlElement* elem,
+ const std::string& elementNameFmt,
+ const SharedRef<ErrorLog>& sharedlog) : log_(sharedlog), elem_(elem), elementNameFmt_(elementNameFmt) {}
- std::string getChildNameFormatted(const std::string& childName) const
+ struct ErrorLog
{
- return getNameFormatted() + " <" + childName + '>';
- }
+ std::wstring failedElements; //unique list of failed elements
+ std::unordered_set<std::string> usedElements;
+ };
- class ErrorLog
+ void logElementError(const std::string& elementName) const
{
- public:
- void notifyConversionError (const std::string& displayName) { insert(displayName); }
- void notifyMissingElement (const std::string& displayName) { insert(displayName); }
- void notifyMissingAttribute(const std::string& displayName, const std::string& attribName) { insert(displayName + " @" + attribName); }
-
- const std::vector<std::string>& elementList() const { return failedElements; }
-
- private:
- void insert(const std::string& newVal)
+ if (const auto [it, inserted] = log_.ref().usedElements.insert(elementName);
+ inserted)
{
- if (usedElements.insert(newVal).second)
- failedElements.push_back(newVal);
+ if (!log_.ref().failedElements.empty())
+ log_.ref().failedElements += L'\n';
+ log_.ref().failedElements += utfTo<std::wstring>(elementName);
}
+ }
- std::vector<std::string> failedElements; //unique list of failed elements
- std::set<std::string> usedElements;
- };
+ void logConversionError() const { logElementError(elementNameFmt_); }
+ void logMissingElement() const { logElementError(elementNameFmt_); }
+ void logMissingAttribute(const std::string& attribName) const { logElementError(elementNameFmt_ + " @" + attribName); }
- std::vector<const XmlElement*> refList_; //all sibling elements with same name (all pointers bound!)
- size_t refIndex_ = 0; //this sibling's index in refList_
- std::string nodeNameFormatted_;
- mutable SharedRef<ErrorLog> log_ = makeSharedRef<ErrorLog>();
+ mutable SharedRef<ErrorLog> log_;
+ const XmlElement* elem_;
+ std::string elementNameFmt_; //e.g. "<Root> <Child> <List>[1]"
};
-
-
-///Check XML input proxy for errors and map to FileError exception
-/**
-\param xmlInput XML input proxy
-\throw FileError
-*/
-inline
-void checkXmlMappingErrors(const XmlIn& xmlInput) //throw FileError
-{
- if (const std::vector<std::wstring>& errors = xmlInput.getErrors();
- !errors.empty())
- {
- std::wstring msg = _("The following XML elements could not be read:") + L'\n';
- for (const std::wstring& elem : errors)
- msg += L'\n' + elem;
-
- throw FileError(msg);
- }
-}
}
#endif //XML_H_349578228034572457454554
bgstack15