summaryrefslogtreecommitdiff
path: root/zenxml
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:23:19 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:23:19 +0200
commit0887aee8c54d0ed51bb2031431e2bcdafebb4c6e (patch)
tree69537ceb9787bb25ac363cc4e6cdaf0804d78363 /zenxml
parent5.12 (diff)
downloadFreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.tar.gz
FreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.tar.bz2
FreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.zip
5.13
Diffstat (limited to 'zenxml')
-rw-r--r--zenxml/bind.h389
-rw-r--r--zenxml/cvrt_struc.h218
-rw-r--r--zenxml/cvrt_text.h229
-rw-r--r--zenxml/dom.h324
-rw-r--r--zenxml/error.h19
-rw-r--r--zenxml/io.h124
-rw-r--r--zenxml/parser.h609
-rw-r--r--zenxml/summary.dox680
-rw-r--r--zenxml/unit_test.cpp95
-rw-r--r--zenxml/xml.h15
10 files changed, 2702 insertions, 0 deletions
diff --git a/zenxml/bind.h b/zenxml/bind.h
new file mode 100644
index 00000000..b297b607
--- /dev/null
+++ b/zenxml/bind.h
@@ -0,0 +1,389 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_BIND_HEADER_9081740816593478258435
+#define ZEN_XML_BIND_HEADER_9081740816593478258435
+
+#include <set>
+#include "cvrt_struc.h"
+#include "parser.h"
+#include "io.h"
+
+namespace zen
+{
+/**
+\file
+\brief Map user data types to XML
+*/
+
+///Load XML document from a file
+/**
+Convenience function that does nothing more than calling loadStream() and parse().
+
+\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+\param filename Input file name
+\param doc The XML document to load
+\throw XmlFileError
+\throw XmlParsingError
+*/
+template <class String> inline
+void load(const String& filename, XmlDoc& doc) //throw XmlFileError, XmlParsingError
+{
+ std::string stream = loadStream(filename); //throw XmlFileError
+ parse(stream, doc); //throw XmlParsingError
+}
+
+
+///Save XML document to a file
+/**
+Convenience function that does nothing more than calling serialize() and saveStream().
+
+\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+\param doc The XML document to save
+\param filename Output file name
+\param lineBreak Line break, default: carriage return + new line
+\param indent Indentation, default: four space characters
+\throw XmlFileError
+*/
+template <class String> inline
+void save(const XmlDoc& doc,
+ const String& filename,
+ const std::string& lineBreak = "\r\n",
+ const std::string& indent = " ") //throw XmlFileError
+{
+ std::string stream = serialize(doc, lineBreak, indent); //throw ()
+ saveStream(stream, filename); //throw XmlFileError
+}
+
+
+///Proxy class to conveniently convert user data into XML structure
+class XmlOut
+{
+public:
+ ///Construct an output proxy for an XML document
+ /**
+ \code
+ zen::XmlDoc doc;
+
+ zen::XmlOut out(doc);
+ out["elem1"](value1); //
+ out["elem2"](value2); //write data of variables "value1", "value2", "value3" into XML elements
+ out["elem3"](value3); //
+
+ save(doc, "out.xml"); //throw XmlFileError
+ \endcode
+ Output:
+ \verbatim
+ <?xml version="1.0" encoding="UTF-8"?>
+ <Root>
+ <elem1>1</elem1>
+ <elem2>2.000000</elem2>
+ <elem3>-3</elem3>
+ </Root>
+ \endverbatim
+ */
+ XmlOut(XmlDoc& doc) : ref_(&doc.root()) {}
+ ///Construct an output proxy for a single XML element
+ /**
+ \sa XmlOut(XmlDoc& doc)
+ */
+ XmlOut(XmlElement& element) : ref_(&element) {}
+
+ ///Retrieve a handle to an XML child element for writing
+ /**
+ The child element will be created if it is not yet existing.
+ \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
+ {
+ const std::string utf8name = utfCvrtTo<std::string>(name);
+ XmlElement* child = ref_->getChild(utf8name);
+ return child ? *child : ref_->addChild(utf8name);
+ }
+
+ ///Write user data to the underlying XML element
+ /**
+ This conversion requires a specialization of zen::writeText() or zen::writeStruc() for type T.
+ \tparam T User type that is converted into an XML element value.
+ */
+ template <class T>
+ 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
+ zen::XmlDoc doc;
+
+ zen::XmlOut out(doc);
+ out["elem"].attribute("attr1", value1); //
+ out["elem"].attribute("attr2", value2); //write data of variables "value1", "value2", "value3" into XML attributes
+ out["elem"].attribute("attr3", value3); //
+
+ save(doc, "out.xml"); //throw XmlFileError
+ \endcode
+ Output:
+ \verbatim
+ <?xml version="1.0" encoding="UTF-8"?>
+ <Root>
+ <elem attr1="1" attr2="2.000000" attr3="-3"/>
+ </Root>
+ \endverbatim
+
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ \tparam T User type that is converted into an XML attribute value.
+ \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_; }
+
+private:
+ XmlElement* ref_; //always bound!
+};
+
+
+///Proxy class to conveniently convert XML structure to user data
+class XmlIn
+{
+ class ErrorLog;
+ struct ConversionToBool { int dummy; };
+
+public:
+ ///Construct an input proxy for an XML document
+ /**
+ \code
+ zen::XmlDoc doc;
+ ... //load document
+ zen::XmlIn in(doc);
+ in["elem1"](value1); //
+ in["elem2"](value2); //write data of XML elements into variables "value1", "value2", "value3"
+ in["elem3"](value3); //
+ \endcode
+ */
+ XmlIn(const XmlDoc& doc) : refIndex(0), log(std::make_shared<ErrorLog>()) { 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) : refIndex(0), log(std::make_shared<ErrorLog>()) { refList.push_back(element); }
+ ///Construct an input proxy for a single XML element
+ /**
+ \sa XmlIn(const XmlDoc& doc)
+ */
+ XmlIn(const XmlElement& element) : refIndex(0), log(std::make_shared<ErrorLog>()) { refList.push_back(&element); }
+
+ ///Retrieve a handle to an XML child element for reading
+ /**
+ It is \b not an error if the child element does not exist, but a subsequent conversion to user data will fail.
+ \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
+ {
+ std::vector<const XmlElement*> childList;
+
+ if (refIndex < refList.size())
+ {
+ auto iterPair = refList[refIndex]->getChildren(name);
+ std::for_each(iterPair.first, iterPair.second,
+ [&](const XmlElement & child) { childList.push_back(&child); });
+ }
+
+ return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log);
+ }
+
+ ///Refer to next sibling element with the same name
+ /**
+ <b>Example:</b> Loop over all XML child elements named "Item"
+ \verbatim
+ <?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())
+ {
+ ...
+ }
+ \endcode
+ */
+ void next() { ++refIndex; }
+
+ ///Read user data from the underlying XML element
+ /**
+ This conversion requires a specialization of zen::readText() or zen::readStruc() for type T.
+ \tparam T User type that receives the data
+ \return "true" if data was read successfully
+ */
+ template <class T>
+ bool operator()(T& value) const
+ {
+ if (refIndex < refList.size())
+ {
+ bool success = readStruc(*refList[refIndex], value);
+ if (!success)
+ log->notifyConversionError(getNameFormatted());
+ return success;
+ }
+ else
+ {
+ log->notifyMissingElement(getNameFormatted());
+ return false;
+ }
+ }
+
+ ///Read user data from an XML attribute
+ /**
+ This conversion requires a specialization of zen::readText() for type T.
+
+ \code
+ zen::XmlDoc doc;
+ ... //load document
+ zen::XmlIn in(doc);
+ in["elem"].attribute("attr1", value1); //
+ in["elem"].attribute("attr2", value2); //write data of XML attributes into variables "value1", "value2", "value3"
+ in["elem"].attribute("attr3", value3); //
+ \endcode
+
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ \tparam T User type that is converted into an XML attribute value.
+ \sa XmlElement::getAttribute()
+ */
+ template <class String, class T>
+ bool attribute(const String& name, T& value) const
+ {
+ if (refIndex < refList.size())
+ {
+ bool success = refList[refIndex]->getAttribute(name, value);
+ if (!success)
+ log->notifyMissingAttribute(getNameFormatted(), utfCvrtTo<std::string>(name));
+ return success;
+ }
+ else
+ {
+ log->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 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)
+ */
+ operator int ConversionToBool::* () const { return get() ? &ConversionToBool::dummy : nullptr; }
+
+ ///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
+ XmlIn in(doc);
+ XmlIn inItem = in["item1"];
+
+ int value = 0;
+ inItem(value); //assume this conversion failed
+
+ assert(in.errorsOccured() == inItem.errorsOccured());
+ assert(in.getErrorsAs<std::string>() == inItem.getErrorsAs<std::string>());
+ \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.
+ */
+ bool errorsOccured() const { return !log->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<String> output;
+ const auto& elements = log->elementList();
+ std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfCvrtTo<String>(str); });
+ return output;
+ }
+
+private:
+ XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) :
+ refList(siblingList), refIndex(0), formattedName(elementNameFmt), log(sharedlog)
+ { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); }
+
+ static std::string getNameFormatted(const XmlElement& elem) //"<Root> <Level1> <Level2>"
+ {
+ return (elem.parent() ? getNameFormatted(*elem.parent()) + " " : std::string()) + "<" + elem.getNameAs<std::string>() + ">";
+ }
+
+ std::string getNameFormatted() const
+ {
+ if (refIndex < refList.size())
+ {
+ assert(formattedName.empty());
+ return getNameFormatted(*refList[refIndex]);
+ }
+ else
+ return formattedName;
+ }
+
+ std::string getChildNameFormatted(const std::string& childName) const
+ {
+ std::string parentName = getNameFormatted();
+ return (parentName.empty() ? std::string() : (parentName + " ")) + "<" + childName + ">";
+ }
+
+ class ErrorLog
+ {
+ public:
+ void notifyConversionError (const std::string& formattedName) { insert(formattedName); }
+ void notifyMissingElement (const std::string& formattedName) { insert(formattedName); }
+ void notifyMissingAttribute(const std::string& formattedName, const std::string& attribName) { insert(formattedName + " @" + attribName); }
+
+ const std::vector<std::string>& elementList() const { return failedElements; }
+
+ private:
+ void insert(const std::string& newVal)
+ {
+ if (usedElements.insert(newVal).second)
+ failedElements.push_back(newVal);
+ }
+
+ std::vector<std::string> failedElements; //unique list of failed elements
+ std::set<std::string> usedElements;
+ };
+
+ std::vector<const XmlElement*> refList; //all sibling elements with same name (all pointers bound!)
+ size_t refIndex; //this sibling's index in refList
+ std::string formattedName; //contains full and formatted element name if (and only if) refList is empty
+ std::shared_ptr<ErrorLog> log; //always bound
+};
+}
+
+#endif //ZEN_XML_BIND_HEADER_9081740816593478258435
diff --git a/zenxml/cvrt_struc.h b/zenxml/cvrt_struc.h
new file mode 100644
index 00000000..5f7f4ad1
--- /dev/null
+++ b/zenxml/cvrt_struc.h
@@ -0,0 +1,218 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
+#define ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
+
+#include "dom.h"
+
+namespace zen
+{
+/**
+\file
+\brief Handle conversion of arbitrary types to and from XML elements.
+See comments in cvrt_text.h
+*/
+
+///Convert XML element to structured user data
+/**
+ \param input The input XML element.
+ \param value Conversion target value.
+ \return "true" if value was read successfully.
+*/
+template <class T> bool readStruc(const XmlElement& input, T& value);
+///Convert structured user data into an XML element
+/**
+ \param value The value to be converted.
+ \param output The output XML element.
+*/
+template <class T> void writeStruc(const T& value, XmlElement& output);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//------------------------------ implementation -------------------------------------
+namespace impl_2384343
+{
+ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
+ZEN_INIT_DETECT_MEMBER_TYPE(iterator);
+ZEN_INIT_DETECT_MEMBER_TYPE(const_iterator);
+
+ZEN_INIT_DETECT_MEMBER(begin) //
+ZEN_INIT_DETECT_MEMBER(end) //we don't know the exact declaration of the member attribute: may be in a base class!
+ZEN_INIT_DETECT_MEMBER(insert) //
+}
+
+template <typename T>
+struct IsStlContainer :
+ StaticBool<
+ impl_2384343::HasMemberType_value_type <T>::value &&
+ impl_2384343::HasMemberType_iterator <T>::value &&
+ impl_2384343::HasMemberType_const_iterator<T>::value &&
+ impl_2384343::HasMember_begin <T>::value &&
+ impl_2384343::HasMember_end <T>::value &&
+ impl_2384343::HasMember_insert <T>::value> {};
+
+
+namespace impl_2384343
+{
+ZEN_INIT_DETECT_MEMBER_TYPE(first_type);
+ZEN_INIT_DETECT_MEMBER_TYPE(second_type);
+
+ZEN_INIT_DETECT_MEMBER(first) //we don't know the exact declaration of the member attribute: may be in a base class!
+ZEN_INIT_DETECT_MEMBER(second) //
+}
+
+template <typename T>
+struct IsStlPair :
+ StaticBool<
+ impl_2384343::HasMemberType_first_type <T>::value &&
+ impl_2384343::HasMemberType_second_type<T>::value &&
+ impl_2384343::HasMember_first <T>::value &&
+ impl_2384343::HasMember_second <T>::value> {};
+
+//######################################################################################
+
+//Conversion from arbitrary types to an XML element
+enum ValueType
+{
+ VALUE_TYPE_STL_CONTAINER,
+ VALUE_TYPE_STL_PAIR,
+ VALUE_TYPE_OTHER,
+};
+
+template <class T>
+struct GetValueType : StaticEnum<ValueType,
+ GetTextType<T>::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first
+ IsStlContainer<T>::value ? VALUE_TYPE_STL_CONTAINER :
+ IsStlPair<T>::value ? VALUE_TYPE_STL_PAIR :
+ VALUE_TYPE_OTHER> {};
+
+
+template <class T, ValueType type>
+struct ConvertElement;
+/* -> expected interface
+{
+ void writeStruc(const T& value, XmlElement& output) const;
+ bool readStruc(const XmlElement& input, T& value) const;
+};
+*/
+
+
+//partial specialization: handle conversion for all STL-container types!
+template <class T>
+struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER>
+{
+ void writeStruc(const T& value, XmlElement& output) const
+ {
+ std::for_each(value.begin(), value.end(),
+ [&](const typename T::value_type & childVal)
+ {
+ XmlElement& newChild = output.addChild("Item");
+ zen::writeStruc(childVal, newChild);
+ });
+ }
+ bool readStruc(const XmlElement& input, T& value) const
+ {
+ bool success = true;
+ value.clear();
+
+ auto iterPair = input.getChildren("Item");
+ for (auto iter = iterPair.first; iter != iterPair.second; ++iter)
+ {
+ typename T::value_type childVal; //MSVC 2010 bug: cannot put this into a lambda body
+ if (zen::readStruc(*iter, childVal))
+ value.insert(value.end(), childVal);
+ else
+ success = false;
+ }
+ return success;
+ }
+};
+
+
+//partial specialization: handle conversion for std::pair
+template <class T>
+struct ConvertElement<T, VALUE_TYPE_STL_PAIR>
+{
+ void writeStruc(const T& value, XmlElement& output) const
+ {
+ XmlElement& child1 = output.addChild("one"); //don't use "1st/2nd", this will confuse a few pedantic XML parsers
+ zen::writeStruc(value.first, child1);
+
+ XmlElement& child2 = output.addChild("two");
+ zen::writeStruc(value.second, child2);
+ }
+ bool readStruc(const XmlElement& input, T& value) const
+ {
+ bool success = true;
+ const XmlElement* child1 = input.getChild("one");
+ if (!child1 || !zen::readStruc(*child1, value.first))
+ success = false;
+
+ const XmlElement* child2 = input.getChild("two");
+ if (!child2 || !zen::readStruc(*child2, value.second))
+ success = false;
+
+ return success;
+ }
+};
+
+
+//partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText())
+template <class T>
+struct ConvertElement<T, VALUE_TYPE_OTHER>
+{
+ void writeStruc(const T& value, XmlElement& output) const
+ {
+ std::string tmp;
+ writeText(value, tmp);
+ output.setValue(tmp);
+ }
+ bool readStruc(const XmlElement& input, T& value) const
+ {
+ std::string rawStr;
+ input.getValue(rawStr);
+ return readText(rawStr, value);
+ }
+};
+
+
+template <class T> inline
+void writeStruc(const T& value, XmlElement& output)
+{
+ ConvertElement<T, GetValueType<T>::value>().writeStruc(value, output);
+}
+
+
+template <class T> inline
+bool readStruc(const XmlElement& input, T& value)
+{
+ return ConvertElement<T, GetValueType<T>::value>().readStruc(input, value);
+}
+}
+
+#endif //ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
diff --git a/zenxml/cvrt_text.h b/zenxml/cvrt_text.h
new file mode 100644
index 00000000..a70c0813
--- /dev/null
+++ b/zenxml/cvrt_text.h
@@ -0,0 +1,229 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
+#define ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
+
+#include <zen/utf.h>
+#include <zen/string_tools.h>
+
+namespace zen
+{
+/**
+\file
+\brief Handle conversion of string-convertible types to and from std::string.
+
+It is \b not required to call these functions directly. They are implicitly used by zen::XmlElement::getValue(),
+zen::XmlElement::setValue(), zen::XmlElement::getAttribute() and zen::XmlElement::setAttribute().
+\n\n
+Conversions for the following user types are supported by default:
+ - strings - std::string, std::wstring, char*, wchar_t*, char, wchar_t, ect..., all STL-compatible-string-classes
+ - numbers - int, double, float, bool, long, ect..., all built-in numbers
+ - STL containers - std::map, std::set, std::vector, std::list, ect..., all STL-compatible-containers
+ - std::pair
+
+You can add support for additional types via template specialization. \n\n
+Specialize zen::readStruc() and zen::writeStruc() to enable conversion from structured user types to XML elements.
+Specialize zen::readText() and zen::writeText() to enable conversion from string-convertible user types to std::string.
+Prefer latter if possible since it does not only enable conversions from XML elements to user data, but also from and to XML attributes.
+\n\n
+<b> Example: </b> type "bool"
+\code
+namespace zen
+{
+template <> inline
+void writeText(const bool& value, std::string& output)
+{
+ output = value ? "true" : "false";
+}
+
+template <> inline
+bool readText(const std::string& input, bool& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "true")
+ value = true;
+ else if (tmp == "false")
+ value = false;
+ else
+ return false;
+ return true;
+}
+}
+\endcode
+*/
+
+
+///Convert text to user data - used by XML elements and attributes
+/**
+ \param input Input text.
+ \param value Conversion target value.
+ \return "true" if value was read successfully.
+*/
+template <class T> bool readText(const std::string& input, T& value);
+///Convert user data into text - used by XML elements and attributes
+/**
+ \param value The value to be converted.
+ \param output Output text.
+*/
+template <class T> void writeText(const T& value, std::string& output);
+
+
+/* Different classes of data types:
+
+---------------------------
+| structured | readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types
+| ---------------------- |
+| | string-convertible | | readText/writeText - e.g. string-like types, all built-in arithmetic numbers, bool
+| | --------------- | |
+| | | string-like | | | utfCvrtTo - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+| | --------------- | |
+| ---------------------- |
+---------------------------
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//------------------------------ implementation -------------------------------------
+
+//Conversion from arbitrary types to text (for use with XML elements and attributes)
+enum TextType
+{
+ TEXT_TYPE_BOOL,
+ TEXT_TYPE_NUMBER,
+ TEXT_TYPE_STRING,
+ TEXT_TYPE_OTHER,
+};
+
+template <class T>
+struct GetTextType : StaticEnum<TextType,
+ IsSameType<T, bool>::value ? TEXT_TYPE_BOOL :
+ IsStringLike<T>::value ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
+ IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : //
+ TEXT_TYPE_OTHER> {};
+
+//######################################################################################
+
+template <class T, TextType type>
+struct ConvertText;
+/* -> expected interface
+{
+ void writeText(const T& value, std::string& output) const;
+ bool readText(const std::string& input, T& value) const;
+};
+*/
+
+//partial specialization: type bool
+template <class T>
+struct ConvertText<T, TEXT_TYPE_BOOL>
+{
+ void writeText(bool value, std::string& output) const
+ {
+ output = value ? "true" : "false";
+ }
+ bool readText(const std::string& input, bool& value) const
+ {
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "true")
+ value = true;
+ else if (tmp == "false")
+ value = false;
+ else
+ return false;
+ return true;
+ }
+};
+
+//partial specialization: handle conversion for all built-in arithmetic types!
+template <class T>
+struct ConvertText<T, TEXT_TYPE_NUMBER>
+{
+ void writeText(const T& value, std::string& output) const
+ {
+ output = numberTo<std::string>(value);
+ }
+ bool readText(const std::string& input, T& value) const
+ {
+ value = stringTo<T>(input);
+ return true;
+ }
+};
+
+//partial specialization: handle conversion for all string-like types!
+template <class T>
+struct ConvertText<T, TEXT_TYPE_STRING>
+{
+ void writeText(const T& value, std::string& output) const
+ {
+ output = utfCvrtTo<std::string>(value);
+ }
+ bool readText(const std::string& input, T& value) const
+ {
+ value = utfCvrtTo<T>(input);
+ return true;
+ }
+};
+
+
+//partial specialization: unknown type
+template <class T>
+struct ConvertText<T, TEXT_TYPE_OTHER>
+{
+ //###########################################################################################################################################
+ assert_static(sizeof(T) == -1);
+ /*
+ ATTENTION: The data type T is yet unknown to the zenXML framework!
+
+ Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes
+
+ template <> void zen::writeText(const T& value, std::string& output)
+ template <> bool zen::readText(const std::string& input, T& value)
+
+ If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements:
+
+ template <> void zen::writeStruc(const T& value, XmlElement& output)
+ template <> bool zen::readStruc(const XmlElement& input, T& value)
+ */
+ //###########################################################################################################################################
+};
+
+
+template <class T> inline
+void writeText(const T& value, std::string& output)
+{
+ ConvertText<T, GetTextType<T>::value>().writeText(value, output);
+}
+
+
+template <class T> inline
+bool readText(const std::string& input, T& value)
+{
+ return ConvertText<T, GetTextType<T>::value>().readText(input, value);
+}
+}
+
+#endif //ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
diff --git a/zenxml/dom.h b/zenxml/dom.h
new file mode 100644
index 00000000..fbbd6fb0
--- /dev/null
+++ b/zenxml/dom.h
@@ -0,0 +1,324 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_DOM_HEADER_82085720723894567204564256
+#define ZEN_XML_DOM_HEADER_82085720723894567204564256
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <map>
+#include "cvrt_text.h" //"readText/writeText"
+
+namespace zen
+{
+class XmlDoc;
+
+/// An XML element
+class XmlElement
+{
+ struct PrivateConstruction {};
+public:
+ //Construct an empty XML element
+ //This constructor should be private, however std::make_shared() requires public access
+ //Therefore at least prevent users from calling it via private dummy type PrivateConstruction
+ template <class String>
+ XmlElement(const String& name, XmlElement* parentElement, PrivateConstruction) : name_(utfCvrtTo<std::string>(name)), parent_(parentElement) {}
+
+ ///Retrieve the name of this XML element.
+ /**
+ \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
+ \returns Name of the XML element
+ */
+ template <class String>
+ String getNameAs() const { return utfCvrtTo<String>(name_); }
+
+ ///Get the value of this element as a user type.
+ /**
+ \tparam T Arbitrary user data type: e.g. any string class, all built-in arithmetic numbers, STL container, ...
+ \returns "true" if Xml element was successfully converted to value, cannot fail for string-like types
+ */
+ template <class T>
+ bool getValue(T& value) const { return readStruc(*this, value); }
+
+ ///Set the value of this element.
+ /**
+ \tparam T Arbitrary user data type: e.g. any string-like type, all built-in arithmetic numbers, STL container, ...
+ */
+ template <class T>
+ void setValue(const T& value) { writeStruc(value, *this); }
+
+ ///Retrieve an attribute by name.
+ /**
+ \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 class, all built-in arithmetic numbers
+ \param name The name of the attribute to retrieve.
+ \param value The value of the attribute converted to T.
+ \return "true" if value was retrieved successfully.
+ */
+ template <class String, class T>
+ bool getAttribute(const String& name, T& value) const
+ {
+ auto it = attributes.find(utfCvrtTo<std::string>(name));
+ return it == attributes.end() ? false : readText(it->second, value);
+ }
+
+ ///Create or update an XML attribute.
+ /**
+ \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
+ \param name The name of the attribute to create or update.
+ \param value The value to set.
+ */
+ template <class String, class T>
+ void setAttribute(const String& name, const T& value)
+ {
+ std::string attrValue;
+ writeText(value, attrValue);
+ attributes[utfCvrtTo<std::string>(name)] = attrValue;
+ } //create or update
+
+ ///Remove the attribute with the given name.
+ /**
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ */
+ template <class String>
+ void removeAttribute(const String& name) { attributes.erase(utfCvrtTo<std::string>(name)); }
+
+ ///Create a new child element and return a reference to it.
+ /**
+ \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 to be created.
+ */
+ template <class String>
+ XmlElement& addChild(const String& name)
+ {
+ std::string utf8Name = utfCvrtTo<std::string>(name);
+ auto newElement = std::make_shared<XmlElement>(utf8Name, this, PrivateConstruction());
+ //std::shared_ptr<XmlElement> newElement(new XmlElement(utf8Name, this));
+ childElements.push_back(newElement);
+ childElementsSorted.insert(std::make_pair(utf8Name, newElement));
+ return *newElement;
+ }
+
+ ///Retrieve a child element with the given 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 to be retrieved.
+ \return A pointer to the child element or nullptr if none was found
+ */
+ template <class String>
+ const XmlElement* getChild(const String& name) const
+ {
+ auto it = childElementsSorted.find(utfCvrtTo<std::string>(name));
+ return it == childElementsSorted.end() ? nullptr : &*(it->second);
+ }
+
+ ///\sa getChild
+ template <class String>
+ XmlElement* getChild(const String& name)
+ {
+ 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 : public std::iterator<std::input_iterator_tag, T>, private AccessPolicy //get rid of shared_ptr indirection
+ {
+ public:
+ 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_; }
+ inline friend bool operator!=(const PtrIter& lhs, const PtrIter& rhs) { return !(lhs == rhs); }
+ T& operator* () { return AccessPolicy::template objectRef<T>(it_); }
+ T* operator->() { return &AccessPolicy::template objectRef<T>(it_); }
+ private:
+ IterTy it_;
+ };
+
+ struct AccessPtrMap
+ {
+ template <class T, class IterTy>
+ T& objectRef(const IterTy& it) { return *(it->second); }
+ };
+
+ typedef PtrIter<std::multimap<std::string, std::shared_ptr<XmlElement>>::iterator, XmlElement, AccessPtrMap> ChildIter2;
+ typedef PtrIter<std::multimap<std::string, std::shared_ptr<XmlElement>>::const_iterator, const XmlElement, AccessPtrMap> ChildIterConst2;
+
+ ///Access all child elements with the given name via STL iterators.
+ /**
+ \code
+ auto iterPair = 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 all child elements sequentially.
+ */
+ template <class String>
+ std::pair<ChildIterConst2, ChildIterConst2> getChildren(const String& name) const { return childElementsSorted.equal_range(utfCvrtTo<std::string>(name)); }
+
+ ///\sa getChildren
+ template <class String>
+ std::pair<ChildIter2, ChildIter2> getChildren(const String& name) { return childElementsSorted.equal_range(utfCvrtTo<std::string>(name)); }
+
+ struct AccessPtrVec
+ {
+ template <class T, class IterTy>
+ T& objectRef(const IterTy& it) { return **it; }
+ };
+
+ typedef PtrIter<std::vector<std::shared_ptr<XmlElement>>::iterator, XmlElement, AccessPtrVec> ChildIter;
+ typedef PtrIter<std::vector<std::shared_ptr<XmlElement>>::const_iterator, const XmlElement, AccessPtrVec> ChildIterConst;
+
+ ///Access all child elements sequentially via STL iterators.
+ /**
+ \code
+ auto iterPair = elem.getChildren();
+ std::for_each(iterPair.first, iterPair.second,
+ [](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 std::make_pair(childElements.begin(), childElements.end()); }
+
+ ///\sa getChildren
+ std::pair<ChildIter, ChildIter> getChildren() { return std::make_pair(childElements.begin(), childElements.end()); }
+
+ ///Get parent XML element, may be nullptr for root element
+ XmlElement* parent() { return parent_; };
+ ///Get parent XML element, may be nullptr for root element
+ const XmlElement* parent() const { return parent_; };
+
+
+ typedef std::map<std::string, std::string>::const_iterator AttrIter;
+
+ /* -> disabled documentation extraction
+ \brief Get all attributes associated with the element.
+ \code
+ auto iterPair = elem.getAttributes();
+ for (auto it = iterPair.first; it != iterPair.second; ++it)
+ std::cout << "name: " << it->first << " value: " << it->second << "\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.
+ */
+ std::pair<AttrIter, AttrIter> getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); }
+
+ //Transactionally swap two elements. -> disabled documentation extraction
+ void swap(XmlElement& other)
+ {
+ name_ .swap(other.name_);
+ value_ .swap(other.value_);
+ attributes.swap(other.attributes);
+ childElements.swap(other.childElements);
+ childElementsSorted.swap(other.childElementsSorted);
+ //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead:
+ std::for_each( childElements.begin(), childElements.end(), [&](const std::shared_ptr<XmlElement>& child) { child->parent_ = this; });
+ std::for_each(other.childElements.begin(), other.childElements.end(), [&](const std::shared_ptr<XmlElement>& child) { child->parent_ = &other; });
+ }
+
+private:
+ friend class XmlDoc;
+
+ XmlElement(const XmlElement&); //not implemented
+ XmlElement& operator=(const XmlElement&); //
+
+ std::string name_;
+ std::string value_;
+ std::map<std::string, std::string> attributes;
+ std::vector<std::shared_ptr<XmlElement>> childElements; //all child elements in order of creation
+ std::multimap<std::string, std::shared_ptr<XmlElement>> childElementsSorted; //alternate key: sorted by element name
+ XmlElement* parent_;
+};
+
+
+//XmlElement::setValue<T>() calls zen::writeStruc() which calls XmlElement::setValue() ... => these two specializations end the circle
+template <> inline
+void XmlElement::setValue(const std::string& value) { value_ = value; }
+
+template <> inline
+bool XmlElement::getValue(std::string& value) const { value = value_; return true; }
+
+
+///The complete XML document
+class XmlDoc
+{
+public:
+ ///Default constructor setting up an empty XML document with a standard declaration: <?xml version="1.0" encoding="UTF-8" ?>
+ XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root", nullptr, XmlElement::PrivateConstruction()) {}
+
+ //Setup an empty XML document
+ /**
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ \param rootName The name of the XML document's root element
+ */
+ template <class String>
+ XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName, nullptr, XmlElement::PrivateConstruction()) {}
+
+ ///Get a const reference to the document's root element.
+ const XmlElement& root() const { return rootElement; }
+ ///Get a reference to the document's root element.
+ XmlElement& root() { return rootElement; }
+
+ ///Get the version used in the XML declaration.
+ /**
+ \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
+ */
+ template <class String>
+ String getVersionAs() const { return utfCvrtTo<String>(version_); }
+
+ ///Set the version used in the XML declaration.
+ /**
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ */
+ template <class String>
+ void setVersion(const String& version) { version_ = utfCvrtTo<std::string>(version); }
+
+ ///Get the encoding used in the XML declaration.
+ /**
+ \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
+ */
+ template <class String>
+ String getEncodingAs() const { return utfCvrtTo<String>(encoding_); }
+
+ ///Set the encoding used in the XML declaration.
+ /**
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ */
+ template <class String>
+ void setEncoding(const String& encoding) { encoding_ = utfCvrtTo<std::string>(encoding); }
+
+ ///Get the standalone string used in the XML declaration.
+ /**
+ \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
+ */
+ template <class String>
+ String getStandaloneAs() const { return utfCvrtTo<String>(standalone_); }
+
+ ///Set the standalone string used in the XML declaration.
+ /**
+ \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+ */
+ template <class String>
+ void setStandalone(const String& standalone) { standalone_ = utfCvrtTo<std::string>(standalone); }
+
+private:
+ XmlDoc(const XmlDoc&); //not implemented, thanks to XmlElement::parent_
+ XmlDoc& operator=(const XmlDoc&); //
+
+ std::string version_;
+ std::string encoding_;
+ std::string standalone_;
+
+ XmlElement rootElement;
+};
+
+}
+
+#endif //ZEN_XML_DOM_HEADER_82085720723894567204564256
diff --git a/zenxml/error.h b/zenxml/error.h
new file mode 100644
index 00000000..a90dd35a
--- /dev/null
+++ b/zenxml/error.h
@@ -0,0 +1,19 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_ERROR_HEADER_018734618433021489473214873214
+#define ZEN_XML_ERROR_HEADER_018734618433021489473214873214
+
+namespace zen
+{
+///Exception base class for zenXML
+struct XmlError
+{
+ virtual ~XmlError() {}
+};
+}
+
+#endif //ZEN_XML_ERROR_HEADER_018734618433021489473214873214
diff --git a/zenxml/io.h b/zenxml/io.h
new file mode 100644
index 00000000..4286ae6c
--- /dev/null
+++ b/zenxml/io.h
@@ -0,0 +1,124 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_IO_HEADER_8917640501480763248343343
+#define ZEN_XML_IO_HEADER_8917640501480763248343343
+
+#include <cstdio>
+#include <cerrno>
+#include <zen/scope_guard.h>
+#include <zen/utf.h>
+#include "error.h"
+
+namespace zen
+{
+/**
+\file
+\brief Save and load byte streams from files
+*/
+
+#if !defined(ZEN_PLATFORM_WINDOWS) && !defined(ZEN_PLATFORM_OTHER)
+#error Please specify your platform: #define ZEN_PLATFORM_WINDOWS or ZEN_PLATFORM_OTHER
+#endif
+
+///Exception thrown due to failed file I/O
+struct XmlFileError : public XmlError
+{
+ typedef int ErrorCode;
+
+ explicit XmlFileError(ErrorCode ec) : lastError(ec) {}
+ ///Native error code: errno
+ ErrorCode lastError;
+};
+
+
+#ifdef ZEN_PLATFORM_WINDOWS
+namespace implemenation //sad but true
+{
+template <class String> inline
+FILE* fopen(const String& filename, const wchar_t* mode)
+{
+#ifdef _MSC_VER
+ FILE* handle = nullptr;
+ errno_t rv = ::_wfopen_s(&handle, utfCvrtTo<std::wstring>(filename).c_str(), mode); //more secure?
+ (void)rv;
+ return handle;
+#else
+ return ::_wfopen(utfCvrtTo<std::wstring>(filename).c_str(), mode);
+#endif
+}
+}
+#endif
+
+
+///Save byte stream to a file
+/**
+\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+\param stream Input byte stream
+\param filename Output file name
+\throw XmlFileError
+*/
+template <class String>
+void saveStream(const std::string& stream, const String& filename) //throw XmlFileError
+{
+#ifdef ZEN_PLATFORM_WINDOWS
+ FILE* handle = implemenation::fopen(utfCvrtTo<std::wstring>(filename).c_str(), L"wb");
+#else
+ FILE* handle = ::fopen(utfCvrtTo<std::string>(filename).c_str(), "w");
+#endif
+ if (handle == nullptr)
+ throw XmlFileError(errno);
+ ZEN_ON_SCOPE_EXIT(::fclose(handle));
+
+ const size_t bytesWritten = ::fwrite(stream.c_str(), 1, stream.size(), handle);
+ if (::ferror(handle) != 0)
+ throw XmlFileError(errno);
+
+ (void)bytesWritten;
+ assert(bytesWritten == stream.size());
+}
+
+
+///Load byte stream from a file
+/**
+\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
+\param filename Input file name
+\return Output byte stream
+\throw XmlFileError
+*/
+template <class String>
+std::string loadStream(const String& filename) //throw XmlFileError
+{
+#ifdef ZEN_PLATFORM_WINDOWS
+ FILE* handle = implemenation::fopen(utfCvrtTo<std::wstring>(filename).c_str(), L"rb");
+#else
+ FILE* handle = ::fopen(utfCvrtTo<std::string>(filename).c_str(), "r");
+#endif
+ if (handle == nullptr)
+ throw XmlFileError(errno);
+ ZEN_ON_SCOPE_EXIT(::fclose(handle));
+
+ std::string stream;
+ const size_t blockSize = 64 * 1024;
+ do
+ {
+ stream.resize(stream.size() + blockSize); //let's pray std::string implements exponential growth!
+
+ const size_t bytesRead = ::fread(&*(stream.begin() + stream.size() - blockSize), 1, blockSize, handle);
+ if (::ferror(handle))
+ throw XmlFileError(errno);
+ if (bytesRead > blockSize)
+ throw XmlFileError(0);
+ if (bytesRead < blockSize)
+ stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+ }
+ while (!::feof(handle));
+
+ return stream;
+}
+}
+
+#endif //ZEN_XML_IO_HEADER_8917640501480763248343343
diff --git a/zenxml/parser.h b/zenxml/parser.h
new file mode 100644
index 00000000..823cd793
--- /dev/null
+++ b/zenxml/parser.h
@@ -0,0 +1,609 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_PARSER_HEADER_81248670213764583021432
+#define ZEN_XML_PARSER_HEADER_81248670213764583021432
+
+#include <cstdio>
+#include <cstddef> //ptrdiff_t; req. on Linux
+#include <zen/string_traits.h>
+#include "dom.h"
+#include "error.h"
+
+namespace zen
+{
+/**
+\file
+\brief Convert an XML document object model (class XmlDoc) to and from a byte stream representation.
+*/
+
+///Save XML document as a byte stream
+/**
+\param doc Input XML document
+\param lineBreak Line break, default: carriage return + new line
+\param indent Indentation, default: four space characters
+\return Output byte stream
+*/
+std::string serialize(const XmlDoc& doc,
+ const std::string& lineBreak = "\r\n",
+ const std::string& indent = " "); //throw ()
+
+///Exception thrown due to an XML parsing error
+struct XmlParsingError : public XmlError
+{
+ XmlParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {}
+ ///Input file row where the parsing error occured
+ size_t row; //beginning with 0
+ ///Input file column where the parsing error occured
+ size_t col; //
+};
+
+
+///Load XML document from a byte stream
+/**
+\param stream Input byte stream
+\param doc Output XML document
+\throw XmlParsingError
+*/
+void parse(const std::string& stream, XmlDoc& doc); //throw XmlParsingError
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//---------------------------- implementation ----------------------------
+//see: http://www.w3.org/TR/xml/
+
+namespace implementation
+{
+inline
+std::pair<char, char> hexify(unsigned char c)
+{
+ auto hexifyDigit = [](int num) -> char //input [0, 15], output 0-9, A-F
+ {
+ assert(0 <= num && num <= 15); //guaranteed by design below!
+ return static_cast<char>(num <= 9 ? //no signed/unsigned char problem here!
+ '0' + num :
+ 'A' + (num - 10));
+ };
+ return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16));
+}
+
+
+inline
+char unhexify(char high, char low)
+{
+ auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15]
+ {
+ if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here!
+ return hex - '0';
+ else if ('A' <= hex && hex <= 'F')
+ return (hex - 'A') + 10;
+ else if ('a' <= hex && hex <= 'f')
+ return (hex - 'a') + 10;
+ assert(false);
+ return 0;
+ };
+ return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed)
+};
+
+
+template <class Predicate> inline
+std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex
+{
+ std::string output;
+ std::for_each(str.begin(), str.end(),
+ [&](char c)
+ {
+ if (c == '&') //
+ output += "&amp;";
+ else if (c == '<') //normalization mandatory: http://www.w3.org/TR/xml/#syntax
+ output += "&lt;";
+ else if (c == '>') //
+ output += "&gt;";
+ else if (pred(c))
+ {
+ if (c == '\'')
+ output += "&apos;";
+ else if (c == '\"')
+ output += "&quot;";
+ else
+ {
+ output += "&#x";
+ const auto hexDigits = hexify(c); //hexify beats "printNumber<std::string>("&#x%02X;", c)" by a nice factor of 3!
+ output += hexDigits.first;
+ output += hexDigits.second;
+ output += ';';
+ }
+ }
+ else
+ output += c;
+ });
+ return output;
+}
+
+inline
+std::string normalizeName(const std::string& str)
+{
+ return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; });
+}
+
+inline
+std::string normalizeElementValue(const std::string& str)
+{
+ return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32; });
+}
+
+inline
+std::string normalizeAttribValue(const std::string& str)
+{
+ return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32 || c == '\'' || c == '\"'; });
+}
+
+
+namespace
+{
+std::string denormalize(const std::string& str)
+{
+ std::string output;
+ for (auto it = str.begin(); it != str.end(); ++it)
+ {
+ const char c = *it;
+
+ if (c == '&')
+ {
+ auto checkEntity = [&](const char* placeholder, char realVal) -> bool
+ {
+ size_t strLen = strLength(placeholder);
+
+ if (str.end() - it >= static_cast<int>(strLen) && std::equal(it, it + strLen, placeholder))
+ {
+ output += realVal;
+ it += strLen - 1;
+ return true;
+ }
+ return false;
+ };
+
+ if (checkEntity("&amp;", '&'))
+ continue;
+ if (checkEntity("&lt;", '<'))
+ continue;
+ if (checkEntity("&gt;", '>'))
+ continue;
+ if (checkEntity("&apos;", '\''))
+ continue;
+ if (checkEntity("&quot;", '\"'))
+ continue;
+
+ if (str.end() - it >= 6 &&
+ it[1] == '#' &&
+ it[2] == 'x' &&
+ it[5] == ';')
+ {
+ output += unhexify(it[3], it[4]);
+ it += 5;
+ continue;
+ //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!!
+ }
+
+ output += c; //unexpected char!
+ }
+ else if (c == '\r') //map all end-of-line characters to \n http://www.w3.org/TR/xml/#sec-line-ends
+ {
+ auto itNext = it + 1;
+ if (itNext != str.end() && *itNext == '\n')
+ ++it;
+ output += '\n';
+ }
+ else
+ output += c;
+ };
+ return output;
+}
+
+
+void serialize(const XmlElement& element, std::string& stream,
+ const std::string& lineBreak,
+ const std::string& indent,
+ size_t indentLevel)
+{
+ const std::string& nameFmt = normalizeName(element.getNameAs<std::string>());
+
+ for (size_t i = 0; i < indentLevel; ++i)
+ stream += indent;
+
+ stream += '<' + nameFmt;
+
+ auto attr = element.getAttributes();
+ for (auto it = attr.first; it != attr.second; ++it)
+ stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + "\"";
+
+ //no support for mixed-mode content
+ auto iterPair = element.getChildren();
+ if (iterPair.first != iterPair.second) //structured element
+ {
+ stream += '>' + lineBreak;
+
+ std::for_each(iterPair.first, iterPair.second,
+ [&](const XmlElement & el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); });
+
+ for (size_t i = 0; i < indentLevel; ++i)
+ stream += indent;
+ stream += "</" + nameFmt + '>' + lineBreak;
+ }
+ else
+ {
+ std::string value;
+ element.getValue(value);
+
+ if (!value.empty()) //value element
+ stream += '>' + normalizeElementValue(value) + "</" + nameFmt + '>' + lineBreak;
+ else //empty element
+ stream += "/>" + lineBreak;
+ }
+}
+
+std::string serialize(const XmlDoc& doc,
+ const std::string& lineBreak,
+ const std::string& indent)
+{
+ std::string version = doc.getVersionAs<std::string>();
+ if (!version.empty())
+ version = " version=\"" + normalizeAttribValue(version) + "\"";
+
+ std::string encoding = doc.getEncodingAs<std::string>();
+ if (!encoding.empty())
+ encoding = " encoding=\"" + normalizeAttribValue(encoding) + "\"";
+
+ std::string standalone = doc.getStandaloneAs<std::string>();
+ if (!standalone.empty())
+ standalone = " standalone=\"" + normalizeAttribValue(standalone) + "\"";
+
+ std::string output = "<?xml" + version + encoding + standalone + "?>" + lineBreak;
+ serialize(doc.root(), output, lineBreak, indent, 0);
+ return output;
+}
+}
+}
+
+inline
+std::string serialize(const XmlDoc& doc,
+ const std::string& lineBreak,
+ const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); }
+
+/*
+Grammar for XML parser
+-------------------------------
+document-expression:
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ element-expression:
+
+element-expression:
+ <string attributes-expression/>
+ <string attributes-expression> pm-expression </string>
+
+element-list-expression:
+ <empty>
+ element-expression element-list-expression
+
+attributes-expression:
+ <empty>
+ string="string" attributes-expression
+
+pm-expression:
+ string
+ element-list-expression
+*/
+
+namespace implementation
+{
+struct Token
+{
+ enum Type
+ {
+ TK_LESS,
+ TK_GREATER,
+ TK_LESS_SLASH,
+ TK_SLASH_GREATER,
+ TK_EQUAL,
+ TK_QUOTE,
+ TK_DECL_BEGIN,
+ TK_DECL_END,
+ TK_NAME,
+ TK_END
+ };
+
+ Token(Type t) : type(t) {}
+ Token(const std::string& txt) : type(TK_NAME), name(txt) {}
+
+ Type type;
+ std::string name; //filled if type == TK_NAME
+};
+
+class Scanner
+{
+public:
+ Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin())
+ {
+ if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8))
+ pos += strLength(BYTE_ORDER_MARK_UTF8);
+
+ tokens.push_back(std::make_pair("<?xml", Token::TK_DECL_BEGIN));
+ tokens.push_back(std::make_pair("?>", Token::TK_DECL_END));
+ tokens.push_back(std::make_pair("</", Token::TK_LESS_SLASH));
+ tokens.push_back(std::make_pair("/>", Token::TK_SLASH_GREATER));
+ tokens.push_back(std::make_pair("<" , Token::TK_LESS)); //evaluate after TK_DECL_BEGIN!
+ tokens.push_back(std::make_pair(">" , Token::TK_GREATER));
+ tokens.push_back(std::make_pair("=" , Token::TK_EQUAL));
+ tokens.push_back(std::make_pair("\"", Token::TK_QUOTE));
+ tokens.push_back(std::make_pair("\'", Token::TK_QUOTE));
+ }
+
+ Token nextToken() //throw XmlParsingError
+ {
+ //skip whitespace
+ pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); });
+
+ if (pos == stream_.end())
+ return Token::TK_END;
+
+ for (auto it = tokens.begin(); it != tokens.end(); ++it)
+ if (startsWith(pos, it->first))
+ {
+ pos += it->first.size();
+ return it->second;
+ }
+
+ auto nameEnd = std::find_if(pos, stream_.end(), [](char c)
+ {
+ return c == '<' ||
+ c == '>' ||
+ c == '=' ||
+ c == '/' ||
+ c == '\'' ||
+ c == '\"' ||
+ zen::isWhiteSpace(c);
+ });
+
+ if (nameEnd != pos)
+ {
+ std::string name(&*pos, nameEnd - pos);
+ pos = nameEnd;
+ return implementation::denormalize(name);
+ }
+
+ //unknown token
+ throw XmlParsingError(posRow(), posCol());
+ }
+
+ std::string extractElementValue()
+ {
+ auto it = std::find_if(pos, stream_.end(), [](char c)
+ {
+ return c == '<' ||
+ c == '>';
+ });
+ std::string output(pos, it);
+ pos = it;
+ return implementation::denormalize(output);
+ }
+
+ std::string extractAttributeValue()
+ {
+ auto it = std::find_if(pos, stream_.end(), [](char c)
+ {
+ return c == '<' ||
+ c == '>' ||
+ c == '\'' ||
+ c == '\"';
+ });
+ std::string output(pos, it);
+ pos = it;
+ return implementation::denormalize(output);
+ }
+
+ size_t posRow() const //current row beginning with 0
+ {
+ const size_t crSum = std::count(stream_.begin(), pos, '\r'); //carriage returns
+ const size_t nlSum = std::count(stream_.begin(), pos, '\n'); //new lines
+ assert(crSum == 0 || nlSum == 0 || crSum == nlSum);
+ return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win
+ }
+
+ size_t posCol() const //current col beginning with 0
+ {
+ //seek beginning of line
+ for (auto it = pos; it != stream_.begin(); )
+ {
+ --it;
+ if (*it == '\r' || *it == '\n')
+ return pos - it - 1;
+ }
+ return pos - stream_.begin();
+ }
+
+private:
+ Scanner(const Scanner&);
+ Scanner& operator=(const Scanner&);
+
+ bool startsWith(std::string::const_iterator it, const std::string& prefix) const
+ {
+ if (stream_.end() - it < static_cast<ptrdiff_t>(prefix.size()))
+ return false;
+ return std::equal(prefix.begin(), prefix.end(), it);
+ }
+
+ typedef std::vector<std::pair<std::string, Token::Type> > TokenList;
+ TokenList tokens;
+
+ const std::string stream_;
+ std::string::const_iterator pos;
+};
+
+
+class XmlParser
+{
+public:
+ XmlParser(const std::string& stream) :
+ scn(stream),
+ tk(scn.nextToken()) {}
+
+ void parse(XmlDoc& doc) //throw XmlParsingError
+ {
+ //declaration (optional)
+ if (token().type == Token::TK_DECL_BEGIN)
+ {
+ nextToken();
+
+ while (token().type == Token::TK_NAME)
+ {
+ std::string attribName = token().name;
+ nextToken();
+
+ consumeToken(Token::TK_EQUAL);
+ expectToken(Token::TK_QUOTE);
+ std::string attribValue = scn.extractAttributeValue();
+ nextToken();
+
+ consumeToken(Token::TK_QUOTE);
+
+ if (attribName == "version")
+ doc.setVersion(attribValue);
+ else if (attribName == "encoding")
+ doc.setEncoding(attribValue);
+ else if (attribName == "standalone")
+ doc.setStandalone(attribValue);
+ }
+ consumeToken(Token::TK_DECL_END);
+ }
+
+ XmlDoc dummy;
+ XmlElement& elemTmp = dummy.root();
+ parseChildElements(elemTmp);
+
+ auto iterPair = elemTmp.getChildren();
+ if (iterPair.first != iterPair.second)
+ doc.root().swap(*iterPair.first);
+
+ expectToken(Token::TK_END);
+ };
+
+private:
+ XmlParser(const XmlParser&);
+ XmlParser& operator=(const XmlParser&);
+
+ void parseChildElements(XmlElement& parent)
+ {
+ while (token().type == Token::TK_LESS)
+ {
+ nextToken();
+
+ expectToken(Token::TK_NAME);
+ std::string elementName = token().name;
+ nextToken();
+
+ XmlElement& newElement = parent.addChild(elementName);
+
+ parseAttributes(newElement);
+
+ if (token().type == Token::TK_SLASH_GREATER) //empty element
+ {
+ nextToken();
+ continue;
+ }
+
+ expectToken(Token::TK_GREATER);
+ std::string elementValue = scn.extractElementValue();
+ nextToken();
+
+ //no support for mixed-mode content
+ if (token().type == Token::TK_LESS) //structured element
+ parseChildElements(newElement);
+ else //value element
+ newElement.setValue(elementValue);
+
+ consumeToken(Token::TK_LESS_SLASH);
+
+ if (token().type != Token::TK_NAME ||
+ elementName != token().name)
+ throw XmlParsingError(scn.posRow(), scn.posCol());
+ nextToken();
+
+ consumeToken(Token::TK_GREATER);
+ }
+ };
+
+ void parseAttributes(XmlElement& element)
+ {
+ while (token().type == Token::TK_NAME)
+ {
+ std::string attribName = token().name;
+ nextToken();
+
+ consumeToken(Token::TK_EQUAL);
+ expectToken(Token::TK_QUOTE);
+ std::string attribValue = scn.extractAttributeValue();
+ nextToken();
+
+ consumeToken(Token::TK_QUOTE);
+ element.setAttribute(attribName, attribValue);
+ }
+ }
+
+ const Token& token() const { return tk; }
+ void nextToken() { tk = scn.nextToken(); }
+
+ void consumeToken(Token::Type t) //throw XmlParsingError
+ {
+ expectToken(t); //throw XmlParsingError
+ nextToken();
+ }
+
+ void expectToken(Token::Type t) //throw XmlParsingError
+ {
+ if (token().type != t)
+ throw XmlParsingError(scn.posRow(), scn.posCol());
+ }
+
+ Scanner scn;
+ Token tk;
+};
+}
+
+inline
+void parse(const std::string& stream, XmlDoc& doc) //throw XmlParsingError
+{
+ implementation::XmlParser(stream).parse(doc); //throw XmlParsingError
+}
+}
+
+#endif //ZEN_XML_PARSER_HEADER_81248670213764583021432
diff --git a/zenxml/summary.dox b/zenxml/summary.dox
new file mode 100644
index 00000000..73a09bcd
--- /dev/null
+++ b/zenxml/summary.dox
@@ -0,0 +1,680 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+/**
+\mainpage Overview
+
+\li \ref sec_Rationale
+\li \ref sec_Quick_Start
+\li \ref sec_Supported_Platforms
+\li \ref sec_Flexible_Programming_Model
+\li \ref sec_Structured_XML_element_access
+\li \ref sec_Access_XML_attributes
+\li \ref sec_Automatic_conversion_built_in
+\li \ref sec_Automatic_conversion_string
+\li \ref sec_Automatic_conversion_STL
+\li \ref sec_Support_user_defined
+\li \ref sec_Structured_user_types
+\li \ref sec_Type_Safety
+
+\section sec_Rationale Rationale
+
+zenXML is an XML library that enables serialization of structured user data in a convenient way.
+Using compile-time information gathered by techniques of template metaprogramming it minimizes the manual overhead required and frees the user from applying fundamental type conversions
+by himself. Basic data types such as \b all built-in arithmetic numbers, \b all kinds of string classes and "string-like" types, \b all types defined as STL containers are processed automatically.
+Thereby a large number of recurring problems is finally solved by the library:
+- generic number to string conversions
+- generic char to wchar_t conversions for custom string classes in a platform independent manner
+- serialization of STL container types
+- simple integration: header-only, no extra dependencies, fully portable
+- support (but not enforce) wide characters everywhere: for file names, XML element names, attribute names, values, ...
+- integrate XML library with focus on elegance, minimal code size, flexibility and performance
+- nonintrusive API: allow for internationalization, fine-granular error handling, and custom file I/O
+- it's a toolkit, not a framework: different layers of software architecture offer, but do not enforce, specific programming models
+
+The design follows the philosophy of the Loki library: \n
+http://loki-lib.sourceforge.net/index.php?n=Main.Philosophy
+
+\section sec_Quick_Start Quick Start
+
+1. Download zenXML: http://sourceforge.net/projects/zenxml
+
+2. Setup a preprocessor macro for your project to identify the platform (this is required for C-stream file IO only)
+\code
+ ZEN_PLATFORM_WINDOWS
+ or
+ ZEN_PLATFORM_OTHER
+\endcode
+
+3. For optimal performance define this global macro in release build: (following convention of the <tt>assert</tt> macro)
+\code
+ NDEBUG
+\endcode
+
+4. Include the main header:
+\code
+#include <zenxml/xml.h>
+\endcode
+
+5. Start serializing user data:
+
+\code
+size_t a = 10;
+double b = 2.0;
+int c = -1;
+\endcode
+
+\code
+zen::XmlDoc doc; //empty XML document
+
+zen::XmlOut out(doc); //fill the document via a data output proxy
+out["elem1"](a); //
+out["elem2"](b); //map data types to XML elements
+out["elem3"](c); //
+
+try
+{
+ save(doc, "file.xml"); //throw zen::XmlFileError
+}
+catch (const zen::XmlFileError& e) { /* handle error */ }
+\endcode
+
+The following XML file will be created:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <elem1>10</elem1>
+ <elem2>2.000000</elem2>
+ <elem3>-1</elem3>
+</Root>
+\endverbatim
+
+Load an XML file and map its content to user data:
+\code
+zen::XmlDoc doc; //empty XML document
+
+try
+{
+ load("file.xml", doc); //throw XmlFileError, XmlParsingError
+}
+catch (const zen::XmlError& e) { /* handle error */ }
+
+zen::XmlIn in(doc); //read document into user data via an input proxy
+in["elem1"](a); //
+in["elem2"](b); //map XML elements into user data
+in["elem3"](c); //
+
+//check for mapping errors, i.e. missing elements or conversion errors: these MAY be considered warnings only
+if (in.errorsOccured())
+{
+ std::vector<std::wstring> failedElements = in.getErrorsAs<std::wstring>();
+ /* show mapping errors */
+}
+\endcode
+
+
+\section sec_Supported_Platforms Supported Platforms
+
+zenXML is written in a platform independent manner and runs on any rudimentary C++11 compliant compiler.
+It has been tested successfully under:
+
+- Windows:
+ -# Visual C++ 2010 - 32 bit
+ -# Visual C++ 2010 - 64 bit
+ -# MinGW: GCC 4.5.2 - 32 bit
+
+- Linux (Ubuntu):
+ -# GCC 4.5.2 - 32 bit
+ -# GCC 4.5.2 - 64 bit
+
+<b>Note:</b> In order to enable C++11 features in GCC it is required to specify either of the following compiler options:
+\verbatim
+-std=c++0x
+-std=gnu++0x
+\endverbatim
+
+
+\section sec_Flexible_Programming_Model Flexible Programming Model
+
+Depending on what granularity of control is required in a particular application, zenXML allows the user to choose between full control or simplicity.
+\n\n
+The library is structured into the following parts, each of which can be used in isolation:
+\n\n
+\b \<File\> \n
+|\n
+| io.h\n
+|\n
+<b>\<Byte Stream\></b>\n
+|\n
+| parser.h\n
+|\n
+<b>\<Document Object Model\></b>\n
+|\n
+| bind.h\n
+|\n
+<b>\<C++ user data\></b>
+\n\n
+
+- Save an XML document to memory
+\code
+zen::XmlDoc doc;
+ ...
+std::string stream = serialize(doc); //throw ()
+
+/* have fun with stream */
+
+//default behavior - already available via zen::save()
+saveStream(stream, "file.xml"); //throw XmlFileError
+\endcode
+
+- Load XML document from memory
+\code
+/* get XML byte stream */
+//e.g. from a file - already available via zen::load()
+std::string stream = loadStream("file.xml"); //throw XmlFileError
+
+zen::XmlDoc doc;
+//parse byte stream into an XML document
+parse(stream, doc); //throw XmlParsingError
+
+/* process XML document */
+\endcode
+
+- Fine-granular error checking
+\code
+zen::XmlIn in(doc);
+//map XML elements into user data
+if (!in["elem1"](a))
+ throw MyCustomException();
+if (!in["elem2"](b))
+ throw MyCustomException();
+if (!in["elem3"](c))
+ throw MyCustomException();
+
+//if (in.errorsOccured()) ... <- not required anymore since each conversion was already checked
+\endcode
+
+- Document Object Model centered programming model
+\n\n
+The full power of type conversions which is available via the input/output proxy classes zen::XmlIn and zen::XmlOut is also available for the document object model!
+\code
+using namespace zen;
+XmlDoc doc;
+
+XmlElement& child = doc.root().addChild("elem1");
+child.setValue(1234);
+
+zen::save(doc, "file.xml"); //throw XmlFileError
+\endcode
+\n
+\code
+using namespace zen;
+
+XmlDoc doc;
+load("file.xml", doc); //throw XmlFileError, XmlParsingError
+
+XmlElement* child = doc.root().getChild("elem1");
+if (child)
+{
+ int value = -1;
+ if (!child->getValue(value))
+ /* handle error */
+}
+else
+ ...
+\endcode
+
+
+\section sec_Structured_XML_element_access Structured XML element access
+
+\code
+//write value into one deeply nested XML element - note the different types used seamlessly: char[], wchar_t[], char, wchar_t, int
+zen::XmlOut out(doc);
+out["elemento1"][L"элемент2"][L"要素3"][L"στοιχείο4"]["elem5"][L"元素6"][L'元']['z'](-1234);
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <elemento1>
+ <элемент2>
+ <要素3>
+ <στοιχείο4>
+ <elem5>
+ <元素6>
+ <元>
+ <z>-1234</z>
+ </元>
+ </元素6>
+ </elem5>
+ </στοιχείο4>
+ </要素3>
+ </элемент2>
+ </elemento1>
+</Root>
+\endverbatim
+
+
+\section sec_Access_XML_attributes Access XML attributes
+
+\code
+zen::XmlDoc doc;
+
+zen::XmlOut out(doc);
+out["elem"].attribute("attr1", -1); //
+out["elem"].attribute("attr2", 2.1); //write data into XML attributes
+out["elem"].attribute("attr3", true); //
+
+save(doc, "file.xml"); //throw XmlFileError
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <elem attr1="-1" attr2="2.1" attr3="true"/>
+</Root>
+\endverbatim
+
+
+\section sec_Automatic_conversion_built_in Automatic conversion for built-in arithmetic types
+
+All built-in arithmetic types and <tt>bool</tt> are detected at compile time and a proper conversion is applied.
+Common conversions for integer-like types such as <tt>long</tt>, <tt>long long</tt>, <tt>__int64</tt> or <tt>size_t</tt> as well as floating point types are optimized for maximum performance.
+
+\code
+zen::XmlOut out(doc);
+
+out["int"] (-1234);
+out["double"] (1.23);
+out["float"] (4.56f);
+out["usignlong"](1234UL);
+out["bool"] (false);
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <int>-1234</int>
+ <double>1.230000</double>
+ <float>4.560000</float>
+ <usignlong>1234</usignlong>
+ <bool>false</bool>
+</Root>
+\endverbatim
+
+
+\section sec_Automatic_conversion_string Automatic conversion for string-like types
+
+The document object model of zenXML internally stores all names and values as a std::string. Consequently everything that is not a std::string but is "string-like" is converted automatically
+into a std::string representation. By default zenXML accepts all character arrays like <tt>char[]</tt>, <tt>wchar_t[]</tt>, <tt>char*</tt>, <tt>wchar_t*</tt>, single characters like
+<tt>char</tt>, <tt>wchar_t</tt>, standard string classes like <tt>std::string</tt>, <tt>std::wstring</tt> and user defined string classes.
+If the input string is based on <tt>char</tt>, it will simply be copied and thereby preserves any local encodings. If the input string is based on <tt>wchar_t</tt> it will
+be converted to an UTF-8 encoded <tt>std::string</tt>. The correct <tt>wchar_t</tt> encoding of the system will be detected at compile time, for example UTF-16 on Windows,
+UTF-32 on certain Linux variants.
+
+<b>Note:</b> User defined string classes are implicitly supported if they fulfill the following string concept by defining:
+ -# A typedef named <tt>value_type</tt> for the underlying character type: must be \p char or \p wchar_t
+ -# A member function <tt>c_str()</tt> returning something that can be converted into a <tt>const value_type*</tt>
+ -# A member function <tt>length()</tt> returning the number of characters returned by <tt>c_str()</tt>
+
+\code
+std::string elem1 = "elemento1";
+std::wstring elem2 = L"элемент2";
+wxString elem3 = L"要素3";
+MyString elem4 = L"στοιχείο4";
+
+zen::XmlOut out(doc);
+
+out["string"] (elem1);
+out["wstring"] (elem2);
+out["wxString"] (elem3);
+out["MyString"] (elem4);
+out["char[6]"] ("elem5");
+out["wchar_t[4]"](L"元素6");
+out["wchar_t"] (L'元');
+out["char"] ('z');
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <string>elemento1</string>
+ <wstring>элемент2</wstring>
+ <wxString>要素3</wxString>
+ <MyString>στοιχείο4</MyString>
+ <char[6]>elem5</char[6]>
+ <wchar_t[4]>元素6</wchar_t[4]>
+ <wchar_t>元</wchar_t>
+ <char>z</char>
+</Root>
+\endverbatim
+
+
+\section sec_Automatic_conversion_STL Automatic conversion for STL container types
+
+- User defined STL compatible types are implicitly supported if they fulfill the following container concept by defining:
+ -# A typedef named <tt>value_type</tt> for the underlying element type of the container
+ -# A typedef named <tt>iterator</tt> for a non-const iterator into the container
+ -# A typedef named <tt>const_iterator</tt> for a const iterator into the container
+\n\n
+ -# A member function <tt>begin()</tt> returning an iterator pointing to the first element in the container
+ -# A member function <tt>end()</tt> returning an iterator pointing just after the last element in the container
+ -# A member function <tt>insert()</tt> with the signature <tt>iterator insert(iterator position, const value_type& x)</tt>
+
+- In order to support combinations of user types and STL containers such as <tt>std::vector<MyType></tt> or <tt>std::vector<std::list<MyType>></tt> it is sufficient to
+integrate <tt>MyType</tt> into zenXML. \n
+See \ref sec_Support_user_defined
+
+\code
+std::deque <float> testDeque;
+std::list <size_t> testList;
+std::map <double, char> testMap;
+std::multimap<short, double> testMultiMap;
+std::set <int> testSet;
+std::multiset<std::string> testMultiSet;
+std::vector <wchar_t> testVector;
+std::vector <std::list<wchar_t>> testVectorList;
+std::pair <char, wchar_t> testPair;
+
+/* fill container */
+
+zen::XmlOut out(doc);
+
+out["deque"] (testDeque);
+out["list"] (testList);
+out["map"] (testMap);
+out["multimap"] (testMultiMap);
+out["set"] (testSet);
+out["multiset"] (testMultiSet);
+out["vector"] (testVector);
+out["vect_list"](testVectorList);
+out["pair" ] (testPair);
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <deque>
+ <Item>1.234000</Item>
+ <Item>5.678000</Item>
+ </deque>
+ <list>
+ <Item>1</Item>
+ <Item>2</Item>
+ </list>
+ <map>
+ <Item>
+ <one>1.100000</one>
+ <two>a</two>
+ </Item>
+ <Item>
+ <one>2.200000</one>
+ <two>b</two>
+ </Item>
+ </map>
+ <multimap>
+ <Item>
+ <one>3</one>
+ <two>99.000000</two>
+ </Item>
+ <Item>
+ <one>3</one>
+ <two>100.000000</two>
+ </Item>
+ <Item>
+ <one>4</one>
+ <two>101.000000</two>
+ </Item>
+ </multimap>
+ <set>
+ <Item>1</Item>
+ <Item>2</Item>
+ </set>
+ <multiset>
+ <Item>1</Item>
+ <Item>1</Item>
+ <Item>2</Item>
+ </multiset>
+ <vector>
+ <Item>Ä</Item>
+ <Item>Ö</Item>
+ </vector>
+ <vect_list>
+ <Item>
+ <Item>ä</Item>
+ <Item>ö</Item>
+ <Item>ü</Item>
+ </Item>
+ <Item>
+ <Item>ä</Item>
+ <Item>ö</Item>
+ <Item>ü</Item>
+ </Item>
+ </vect_list>
+ <pair>
+ <one>a</one>
+ <two>â</two>
+ </pair>
+</Root>
+\endverbatim
+
+
+\section sec_Support_user_defined Support for user defined types
+
+User types can be integrated into zenXML by providing specializations of zen::readText() and zen::writeText() or zen::readStruc() and zen::writeStruc().
+The first pair should be used for all non-structured types that can be represented as a simple text string. This specialization is then used to convert the type to XML elements
+and XML attributes. The second pair should be specialized for structured types that require an XML representation as a hierarchy of elements. This specialization is used when converting
+the type to XML elements only.
+\n\n
+See section \ref sec_Type_Safety for a discussion of type categories.
+\n\n
+<b>Example: Specialization for an enum type</b>
+\code
+enum UnitTime
+{
+ UNIT_SECOND,
+ UNIT_MINUTE,
+ UNIT_HOUR
+};
+
+namespace zen
+{
+template <> inline
+void writeText(const UnitTime& value, std::string& output)
+{
+ switch (value)
+ {
+ case UNIT_SECOND: output = "second"; break;
+ case UNIT_MINUTE: output = "minute"; break;
+ case UNIT_HOUR: output = "hour" ; break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, UnitTime& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "second")
+ value = UNIT_SECOND;
+ else if (tmp == "minute")
+ value = UNIT_MINUTE;
+ else if (tmp == "hour")
+ value = UNIT_HOUR;
+ else
+ return false;
+ return true;
+}
+}
+\endcode
+
+<b>Example: Brute-force specialization for an enum type</b>
+\code
+namespace zen
+{
+template <> inline
+void writeText(const EnumType& value, std::string& output)
+{
+ output = zen::toString<std::string>(value); //treat enum as an integer
+}
+
+template <> inline
+bool readText(const std::string& input, EnumType& value)
+{
+ value = static_cast<EnumType>(zen::toNumber<int>(input)); //treat enum as an integer
+ return true;
+}
+}
+\endcode
+
+<b>Example: Specialization for a structured user type</b>
+\code
+struct Config
+{
+ int a;
+ std::wstring b;
+};
+
+namespace zen
+{
+template <> inline
+void writeStruc(const Config& value, XmlElement& output)
+{
+ XmlOut out(output);
+ out["number" ](value.a);
+ out["address"](value.b);
+}
+
+template <> inline
+bool readStruc(const XmlElement& input, Config& value)
+{
+ XmlIn in(input);
+ bool rv1 = in["number" ](value.a);
+ bool rv2 = in["address"](value.b);
+ return rv1 && rv2;
+}
+}
+
+int main()
+{
+ Config cfg;
+ cfg.a = 2;
+ ...
+ std::vector<Config> cfgList;
+ cfgList.push_back(cfg);
+
+ zen::XmlDoc doc;
+ zen::XmlOut out(doc);
+ out["config"](cfgList);
+ save(doc, "file.xml"); //throw XmlFileError
+}
+\endcode
+
+The resulting XML:
+\verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<Root>
+ <config>
+ <Item>
+ <number>2</number>
+ <address>Abc 3</address>
+ </Item>
+ </config>
+</Root>
+\endverbatim
+
+
+\section sec_Structured_user_types Structured user types
+
+Although it is possible to enable conversion of structured user types by specializing zen::readStruc() and zen::writeStruc() (see \ref sec_Support_user_defined),
+this approach has one drawback: If a mapping error occurs when converting an XML element to structured user data, like one child-element is missing,
+the input proxy class zen::XmlIn is only able to detect that the whole conversion failed. It cannot say which child-elements in particular failed to convert.
+\n\n
+Therefore it may be appropriate to convert structured types by calling subroutines in order to enable fine-granular logging:
+
+\code
+void readConfig(const zen::XmlIn& in, Config& cfg)
+{
+ in["number" ](value.a); //failed conversion will now be logged for each single item by XmlIn
+ in["address"](value.b); //instead of once for the complete Config type!
+}
+\endcode
+\n
+\code
+void readConfig(const wxString& filename, Config& cfg)
+{
+ zen::XmlDoc doc; //empty XML document
+
+ try
+ {
+ load(filename, doc); //throw XmlFileError, XmlParsingError
+ }
+ catch (const zen::XmlError& e) { /* handle error */ }
+
+ zen::XmlIn in(doc);
+
+ zen::XmlIn inConfig = in["config"]; //get input proxy for child element "config"
+
+ readConfig(inConfig, cfg); //map child element to user data by calling subroutine
+
+ //check for mapping errors: errors occuring in subroutines are considered, too!
+ if (in.errorsOccured())
+ /* show mapping errors */
+}
+\endcode
+
+
+\section sec_Type_Safety Type Safety
+
+zenXML heavily utilizes methods of compile-time introspection in order to free the user from managing basic type conversions by himself.
+Thereby it is important to find the right balance between automatic conversions and type safety so that program correctness is not compromized.
+In the context of XML processing three fundamental type categories can be recognized:
+
+- <b>string-like types</b>: <tt>std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...</tt>
+- <b>to-string-convertible types</b>: any string-like type, all built-in arithmetic numbers, <tt>bool</tt>
+- <b>structured types</b>: any to-string-convertible type, STL containers, <tt>std::pair</tt>, structured user types
+
+These categories can be seen as a sequence of inclusive sets:
+\verbatim
+-----------------------------
+| structured | Used as: XML element value
+| ------------------------- | Conversion via: readStruc(), writeStruc() - may be specialized for user-defined types!
+| | to-string-convertible | | Used as: XML element/attribute value
+| | --------------- | | Conversion via: readText(), writeText() - may be specialized for user-defined types!
+| | | string-like | | | Used as: XML element/attribute value or element name
+| | --------------- | | Conversion via: utfCvrtTo<>()
+| ------------------------- |
+-----------------------------
+\endverbatim
+
+A practical implication of this design is that conversions that do not make sense in a particular context simply lead to compile-time errors:
+\code
+zen::XmlOut out(doc);
+out[L'Z'](someValue); //fine: a wchar_t is acceptable as an element name
+out[1234](someValue); //compiler error: an integer is NOT "string-like"!
+\endcode
+\n
+\code
+int valInt = 0;
+std::vector<int> valVec;
+
+zen::XmlOut out(doc);
+out["elem1"](valInt); //fine: both valInt and valVec can be converted to an XML element
+out["elem2"](valVec); //
+
+out["elem"].attribute("attr1", valInt); //fine: an integer can be converted to an XML attribute
+out["elem"].attribute("attr2", valVec); //compiler error: a std::vector<int> is NOT "to-string-convertible"!
+\endcode
+
+ \author \b Zenju
+ \n\n
+ <b>Email:</b> zenju AT gmx DOT de
+*/ \ No newline at end of file
diff --git a/zenxml/unit_test.cpp b/zenxml/unit_test.cpp
new file mode 100644
index 00000000..a671c9ee
--- /dev/null
+++ b/zenxml/unit_test.cpp
@@ -0,0 +1,95 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include <set>
+#include <map>
+#include <deque>
+#include <vector>
+#include <list>
+#include <utility>
+#include "xml.h"
+
+using namespace zen;
+
+namespace
+{
+void unit_test()
+{
+ class Dummy {};
+
+ //compile time checks only
+
+ assert_static(!IsStlContainer<wchar_t> ::value);
+ assert_static(!IsStlContainer<char> ::value);
+ assert_static(!IsStlContainer<Dummy> ::value);
+ assert_static(!IsStlContainer<NullType> ::value);
+ assert_static(IsStlContainer<std::set <int>> ::value);
+ assert_static(IsStlContainer<std::deque <float>> ::value);
+ assert_static(IsStlContainer<std::list <size_t>> ::value);
+ assert_static((IsStlContainer<std::map <double, char>> ::value));
+ assert_static((IsStlContainer<std::multimap<short, double>>::value));
+ assert_static(IsStlContainer <std::vector <wchar_t>> ::value);
+ assert_static((IsStlPair <std::pair<int, double>> ::value));
+ assert_static(!IsStlPair<Dummy> ::value);
+
+ assert_static(!IsStringLike<Dummy>::value);
+ assert_static(!IsStringLike<int>::value);
+ assert_static(!IsStringLike<double>::value);
+ assert_static(!IsStringLike<short>::value);
+ assert_static(IsStringLike<char>::value);
+ assert_static(IsStringLike<const wchar_t>::value);
+ assert_static(IsStringLike<const char>::value);
+ assert_static(IsStringLike<wchar_t>::value);
+ assert_static(IsStringLike<char*>::value);
+ assert_static(IsStringLike<wchar_t*>::value);
+ assert_static(IsStringLike<char* const>::value);
+ assert_static(IsStringLike<wchar_t* const>::value);
+ assert_static(IsStringLike<const char*>::value);
+ assert_static(IsStringLike<const char* const>::value);
+ assert_static(IsStringLike<const wchar_t*>::value);
+ assert_static(IsStringLike<const wchar_t* const>::value);
+ assert_static(IsStringLike<const char[4]>::value);
+ assert_static(IsStringLike<const wchar_t[4]>::value);
+ assert_static(IsStringLike<char[4]>::value);
+ assert_static(IsStringLike<wchar_t[4]>::value);
+ assert_static(IsStringLike<std::string>::value);
+ assert_static(IsStringLike<std::wstring>::value);
+ assert_static(IsStringLike<const std::string>::value);
+ assert_static(IsStringLike<const std::wstring>::value);
+ assert_static(IsStringLike<const std::string&>::value);
+ assert_static(IsStringLike<const std::wstring&>::value);
+ assert_static(IsStringLike<std::string&>::value);
+ assert_static(IsStringLike<std::wstring&>::value);
+
+ assert_static(!(IsSameType<GetCharType<int>::Type, char>::value));
+ assert_static(!(IsSameType<GetCharType<double>::Type, char>::value));
+ assert_static(!(IsSameType<GetCharType<short>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<char>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<wchar_t>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const char>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const wchar_t>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<char*>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<wchar_t*>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<char* const>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<wchar_t* const>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const char*>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const char* const>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const wchar_t*>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const wchar_t* const>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const char[4]>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const wchar_t[4]>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<char[4]>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<wchar_t[4]>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<std::string>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<std::wstring>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const std::string>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const std::wstring>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<const std::string&>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<const std::wstring&>::Type, wchar_t>::value));
+ assert_static((IsSameType<GetCharType<std::string&>::Type, char>::value));
+ assert_static((IsSameType<GetCharType<std::wstring&>::Type, wchar_t>::value));
+}
+}
diff --git a/zenxml/xml.h b/zenxml/xml.h
new file mode 100644
index 00000000..0fd954ae
--- /dev/null
+++ b/zenxml/xml.h
@@ -0,0 +1,15 @@
+// **************************************************************************
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef ZEN_XML_HEADER_349578228034572457454554
+#define ZEN_XML_HEADER_349578228034572457454554
+
+#include "bind.h"
+
+/// The zenXML namespace
+namespace zen {}
+
+#endif //ZEN_XML_HEADER_349578228034572457454554 \ No newline at end of file
bgstack15