summaryrefslogtreecommitdiff
path: root/zenXml/zenxml/xml.h
diff options
context:
space:
mode:
Diffstat (limited to 'zenXml/zenxml/xml.h')
-rw-r--r--zenXml/zenxml/xml.h181
1 files changed, 86 insertions, 95 deletions
diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h
index ffd00ea0..7e50221a 100644
--- a/zenXml/zenxml/xml.h
+++ b/zenXml/zenxml/xml.h
@@ -126,25 +126,28 @@ public:
</Root>
\endverbatim
*/
- XmlOut(XmlDoc& doc) : ref_(&doc.root()) {}
- ///Construct an output proxy for a single XML element
+ explicit XmlOut(XmlDoc& doc) : ref_(&doc.root()) {}
+
+ ///Retrieve a handle to an XML child element for writing
/**
- \sa XmlOut(XmlDoc& doc)
+ The child element will be created if it is not yet existing.
+ \param name The name of the child element
*/
- XmlOut(XmlElement& element) : ref_(&element) {}
+ XmlOut operator[](std::string name) const
+ {
+ XmlElement* child = ref_->getChild(name);
+ return XmlOut(child ? *child : ref_->addChild(std::move(name)));
+ }
///Retrieve a handle to an XML child element for writing
/**
- The child element will be created if it is not yet existing.
+ The child element will be added, allowing for multiple elements with the same name.
\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
\param name The name of the child element
*/
- template <class String>
- XmlOut operator[](const String& name) const
+ XmlOut addChild(std::string name) const
{
- const std::string utf8name = utfTo<std::string>(name);
- XmlElement* child = ref_->getChild(utf8name);
- return child ? *child : ref_->addChild(utf8name);
+ return XmlOut(ref_->addChild(std::move(name)));
}
///Write user data to the underlying XML element
@@ -176,17 +179,19 @@ public:
</Root>
\endverbatim
- \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
\tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers
\sa XmlElement::setAttribute()
*/
- template <class String, class T>
- void attribute(const String& name, const T& value) { ref_->setAttribute(name, value); }
-
- ///Return a reference to the underlying Xml element
- XmlElement& ref() { return *ref_; }
+ template <class T>
+ 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) {}
+
XmlElement* ref_; //always bound!
};
@@ -208,37 +213,28 @@ public:
in["elem3"](value3); //
\endcode
*/
- XmlIn(const XmlDoc& doc) { refList_.push_back(&doc.root()); }
- ///Construct an input proxy for a single XML element, may be nullptr
- /**
- \sa XmlIn(const XmlDoc& doc)
- */
- XmlIn(const XmlElement* element) { refList_.push_back(element); }
- ///Construct an input proxy for a single XML element
- /**
- \sa XmlIn(const XmlDoc& doc)
- */
- XmlIn(const XmlElement& element) { refList_.push_back(&element); }
+ explicit XmlIn(const XmlDoc& doc) : nodeNameFormatted_('<' + doc.root().getName() + '>')
+ {
+ refList_.push_back(&doc.root());
+ }
///Retrieve a handle to an XML child element for reading
/**
It is \b not an error if the child element does not exist, but only later if a conversion to user data is attempted.
- \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
\param name The name of the child element
*/
- template <class String>
- XmlIn operator[](const String& name) const
+ XmlIn operator[](const std::string& name) const
{
std::vector<const XmlElement*> childList;
- if (refIndex_ < refList_.size())
+ if (const XmlElement* elem = get())
{
- auto iterPair = refList_[refIndex_]->getChildren(name);
- std::for_each(iterPair.first, iterPair.second,
- [&](const XmlElement& child) { childList.push_back(&child); });
+ auto itPair = elem->getChildren(name);
+ std::for_each(itPair.first, itPair.second, [&](const XmlElement& child)
+ { childList.push_back(&child); });
}
- return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log_);
+ return XmlIn(childList, getChildNameFormatted(name), log_);
}
///Refer to next sibling element with the same name
@@ -264,6 +260,18 @@ public:
*/
void next() { ++refIndex_; }
+ ///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)
+ */
+ explicit operator bool() const { return get() != nullptr; }
+
///Read user data from the underlying XML element
/**
This conversion requires a specialization of zen::readText() or zen::readStruc() for type T.
@@ -273,18 +281,25 @@ public:
template <class T>
bool operator()(T& value) const
{
- if (refIndex_ < refList_.size())
+ if (const XmlElement* elem = get())
{
- const bool success = readStruc(*refList_[refIndex_], value);
- if (!success)
- log_.ref().notifyConversionError(getNameFormatted());
- return success;
+ if (readStruc(*elem, value))
+ return true;
+
+ log_.ref().notifyConversionError(getNameFormatted());
}
else
- {
log_.ref().notifyMissingElement(getNameFormatted());
- return false;
- }
+
+ return false;
+ }
+
+ bool hasAttribute(const std::string& name) const
+ {
+ if (const XmlElement* elem = get())
+ if (elem->hasAttribute(name))
+ return true;
+ return false;
}
///Read user data from an XML attribute
@@ -305,37 +320,21 @@ public:
\returns "true" if the attribute was found and the conversion to the output value was successful.
\sa XmlElement::getAttribute()
*/
- template <class String, class T>
- bool attribute(const String& name, T& value) const
+ template <class T>
+ bool attribute(const std::string& name, T& value) const
{
- if (refIndex_ < refList_.size())
+ if (const XmlElement* elem = get())
{
- const bool success = refList_[refIndex_]->getAttribute(name, value);
- if (!success)
- log_.ref().notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name));
- return success;
+ if (elem->getAttribute(name, value))
+ return true;
+
+ log_.ref().notifyMissingAttribute(getNameFormatted(), name);
}
else
- {
log_.ref().notifyMissingElement(getNameFormatted());
- return false;
- }
- }
- ///Return a pointer to the underlying Xml element, may be nullptr
- const XmlElement* get() const { return refIndex_ < refList_.size() ? refList_[refIndex_] : nullptr; }
-
- ///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)
- */
- explicit operator bool() const { return get() != nullptr; }
+ return false;
+ }
///Notifies errors while mapping the XML to user data
/**
@@ -357,56 +356,47 @@ public:
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.
*/
- bool haveErrors() const { return !log_.ref().elementList().empty(); }
///Get a list of XML element and attribute names which failed to convert to user data.
/**
- \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
\returns A list of XML element and attribute names, empty list if no errors occured.
*/
- template <class String>
- std::vector<String> getErrorsAs() const
+ std::vector<std::wstring> getErrors() const
{
- std::vector<String> output;
+ std::vector<std::wstring> output;
for (const std::string& str : log_.ref().elementList())
- output.push_back(utfTo<String>(str));
+ output.push_back(utfTo<std::wstring>(str));
return output;
}
private:
- XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const SharedRef<ErrorLog>& sharedlog) :
- refList_(siblingList), formattedName_(elementNameFmt), log_(sharedlog)
- { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); }
+ XmlIn(const std::vector<const XmlElement*>& siblingList,
+ const std::string& nodeNameFormatted,
+ const SharedRef<ErrorLog>& sharedlog) : refList_(siblingList), nodeNameFormatted_(nodeNameFormatted), log_(sharedlog) {}
- static std::string getNameFormatted(const XmlElement& elem) //"<Root> <Level1> <Level2>"
- {
- return (elem.parent() ? getNameFormatted(*elem.parent()) + ' ' : std::string()) + '<' + elem.getName() + '>';
- }
+ ///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
+ std::string getNameFormatted() const //"<Root> <Level1> <Level2>"
{
- if (refIndex_ < refList_.size())
- {
- assert(formattedName_.empty());
- return getNameFormatted(*refList_[refIndex_]);
- }
+ if (refIndex_ == 0 && refList_.size() <= 1)
+ return nodeNameFormatted_;
else
- return formattedName_;
+ return nodeNameFormatted_ + '[' + numberTo<std::string>(refIndex_ + 1) + ']';
}
std::string getChildNameFormatted(const std::string& childName) const
{
- std::string parentName = getNameFormatted();
- return (parentName.empty() ? std::string() : (parentName + ' ')) + '<' + childName + '>';
+ return getNameFormatted() + " <" + childName + '>';
}
class ErrorLog
{
public:
- void notifyConversionError (const std::string& displayName) { insert(displayName); }
- void notifyMissingElement (const std::string& displayName) { insert(displayName); }
+ 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; }
@@ -424,7 +414,7 @@ private:
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 formattedName_; //contains full and formatted element name if (and only if) refList_ is empty
+ std::string nodeNameFormatted_;
mutable SharedRef<ErrorLog> log_ = makeSharedRef<ErrorLog>();
};
@@ -438,10 +428,11 @@ private:
inline
void checkXmlMappingErrors(const XmlIn& xmlInput, const Zstring& filePath) //throw FileError
{
- if (xmlInput.haveErrors())
+ 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 : xmlInput.getErrorsAs<std::wstring>())
+ for (const std::wstring& elem : errors)
msg += L'\n' + elem;
throw FileError(replaceCpy(_("Configuration file %x is incomplete. The missing elements will be set to their default values."), L"%x", fmtPath(filePath)) + L"\n\n" + msg);
bgstack15