/* * (c) Copyright Ascensio System SIA 2010-2023 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ #pragma once #include #include #include #include #include #include namespace xml { namespace writer { template struct chars { }; template <> struct chars { static const char eq = '='; static const char quote = '\"'; static const char space = ' '; static const char left_brocket = '<'; static const char right_brocket = '>'; static const char slash = '/'; static const char colon = ':'; static const char amp = '&'; static const char apos = '\''; static const char * cdata_open() { return ""; } static const char * amp_str(){ return "&"; } static const char * left_brocket_str() { return "<"; } static const char * right_brocket_str() { return ">"; } static const char * apos_str() { return "'"; } static const char * quote_str() { return """; } }; template <> struct chars { static const wchar_t eq = L'='; static const wchar_t quote = L'\"'; static const wchar_t space = L' '; static const wchar_t left_brocket = L'<'; static const wchar_t right_brocket = L'>'; static const wchar_t slash = L'/'; static const wchar_t colon = L':'; static const wchar_t amp = L'&'; static const wchar_t apos = L'\''; static const wchar_t * cdata_open() { return L""; } static const wchar_t * amp_str(){ return L"&"; } static const wchar_t * left_brocket_str() { return L"<"; } static const wchar_t * right_brocket_str() { return L">"; } static const wchar_t * apos_str() { return L"'"; } static const wchar_t * quote_str() { return L"""; } }; template class element; // // xml::writer class // template class writer { public: // writer must be bound to an ostream writer(std::basic_ostream& os, bool need_header = false) : level_(0), os_(os), need_header_(need_header) {} ~writer(void) { assert(elements_.empty()); } private: size_t level_; std::basic_ostream& os_; // output stream bool need_header_; // have we written an XML header yet? std::stack*> elements_; // stack of open element tags // write XML header, if necessary writer& header() { if (need_header_) { // TODO //os_ << ""; need_header_ = false; } return *this; } // write a single character to the output stream writer& putc(T c) { os_.put(c); return *this; } // write a string to the output stream writer& puts(const T* str) { os_ << str; return *this; } template friend class element; }; // // xml::element class // template class element { public: // create a new element tag, bound to an xml::writer element(writer& wr, std::basic_string, std::allocator > const & name) : wr_(wr), name_(name) { wr_.level_++; check_parent(); wr_.header().putc('<').puts(name_.c_str()); tagopen_ = true; wr_.elements_.push(this); } // close the current element tag ~element() { if (!wr_.elements_.empty() && wr_.elements_.top() == this) { wr_.elements_.pop(); if (tagopen_) wr_.putc(chars::slash) .putc(chars::right_brocket); else wr_.putc(chars::left_brocket) .putc(chars::slash) .puts(name_.c_str()) .putc(chars::right_brocket); } wr_.level_--; } // write an attribute for the current element element& attr(const T* name, const T* value) { assert(name != 0); assert(value != 0); assert(tagopen_); wr_.putc(chars::space) .puts(name) .putc(chars::eq) .putc(chars::quote); qputs(value); wr_.putc(chars::quote); return *this; } // attr() overload for std::string type element& attr(const T* name, const std::basic_string, std::allocator >& value) { return attr(name, value.c_str()); } // attr() function template for all streamable types template element& attr(const T* name, V value) { std::basic_stringstream, std::allocator > ss; ss << value; attr(name, ss.str()); return *this; } // write text content for the current element element& contents(const T* str) { assert(str != 0); check_parent(); qputs(str); return *this; } // contents() overload for std::string type element& contents(const std::basic_string, std::allocator >& str) { return contents(str.c_str()); } // contents() function template for all streamable types template element& contents(V value) { std::basic_stringstream, std::allocator > ss; ss << value; contents(ss.str()); return *this; } std::basic_ostream& stream() { check_parent(); return wr_.os_; } // write CDATA section element& cdata(const T * str) { assert(str != 0); check_parent(); wr_.puts(chars::cdata_open()); wr_.puts(str); wr_.puts(chars::cdata_close()); return *this; } // cdata() overload for std::string type element& cdata(const std::basic_string, std::allocator >& str) { return cdata(str.c_str()); } private: writer& wr_; // bound XML writer //const T* name_; // name of current element const std::basic_string, std::allocator > name_; bool tagopen_; // is the element tag for this element still open? // write a string quoting characters which have meaning in xml element& qputs(const T* str) { for (; *str; ++str) { switch (*str) { case chars::amp: wr_.puts(chars::amp_str()); break; case chars::left_brocket: wr_.puts(chars::left_brocket_str()); break; case chars::right_brocket: wr_.puts(chars::right_brocket_str()); break; case chars::apos: wr_.puts(chars::apos_str()); break; case chars::quote: wr_.puts(chars::quote_str()); break; default: wr_.putc(*str); break; } } return *this; } // check to see if we have a parent tag which needs to be closed void check_parent() { if (!wr_.elements_.empty() && wr_.elements_.top()->tagopen_) { wr_.putc(chars::right_brocket); wr_.elements_.top()->tagopen_ = false; } } }; } } typedef xml::writer::writer xml_writer; typedef xml::writer::element xml_element; #define CP_XML_WRITER(STRM) if (bool _b_ = false) {} else for (xml_writer _xml_wr_((STRM));!_b_;_b_=true) #define CP_XML_NODE(NAME) if (bool _b_ = false) {} else for (xml_element _xml_node_(_xml_wr_, (NAME));!_b_;_b_=true) #define CP_XML_ATTR(NAME, VAL) _xml_node_.attr((NAME),(VAL)) #define CP_XML_CONTENT(VAL) _xml_node_.contents((VAL)) #define CP_XML_STREAM() _xml_node_.stream() #define CP_GET_XML_NODE() _xml_node_ #define CP_ATTR_NODE xml_element& _xml_node_ #define CP_XML_ATTR_OPT(NAME, VAL) if (VAL)CP_XML_ATTR(NAME, (*VAL)) #define CP_XML_NODE_SIMPLE() std::string NS_NAME = std::string(ns) + std::string(L":") + std::string(name); CP_XML_NODE(NS_NAME)