From 29e7b7bfd39d8acea0346c236a7c11d15d5376e2 Mon Sep 17 00:00:00 2001 From: "arseny.kapoulkine" Date: Mon, 5 Jan 2009 22:16:46 +0000 Subject: Node/document saving is now performed via new xml_writer interface, save_file now works without STL git-svn-id: http://pugixml.googlecode.com/svn/trunk@97 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 423 ++++++++++++++++++++++++++++++++++++-------------------- src/pugixml.hpp | 84 +++++++++-- 2 files changed, 346 insertions(+), 161 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 30ed7aa..b98bf4f 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -434,60 +434,6 @@ namespace template const bool opt4_to_type<_1, _2, _3, _4>::o3 = _3; template const bool opt4_to_type<_1, _2, _3, _4>::o4 = _4; -#ifndef PUGIXML_NO_STL - template void text_output_escaped(std::ostream& os, const char* s, opt1) - { - const bool attribute = opt1::o1; - - while (*s) - { - const char* prev = s; - - // While *s is a usual symbol - while (*s && *s != '&' && *s != '<' && *s != '>' && (*s != '"' || !attribute) - && (*s < 0 || *s >= 32 || (*s == '\r' && !attribute) || (*s == '\n' && !attribute) || *s == '\t')) - ++s; - - if (prev != s) os.write(prev, static_cast(s - prev)); - - switch (*s) - { - case 0: break; - case '&': - os << "&"; - ++s; - break; - case '<': - os << "<"; - ++s; - break; - case '>': - os << ">"; - ++s; - break; - case '"': - os << """; - ++s; - break; - case '\r': - os << " "; - ++s; - break; - case '\n': - os << " "; - ++s; - break; - default: // s is not a usual symbol - { - unsigned int ch = (unsigned char)*s++; - - os << "&#" << ch << ";"; - } - } - } - } -#endif - struct gap { char* end; @@ -1361,6 +1307,236 @@ namespace return find; } } + + // Output facilities + struct xml_buffered_writer + { + xml_buffered_writer(const xml_buffered_writer&); + xml_buffered_writer& operator=(const xml_buffered_writer&); + + xml_buffered_writer(xml_writer& writer): writer(writer), bufsize(0) + { + } + + ~xml_buffered_writer() + { + flush(); + } + + void flush() + { + if (bufsize > 0) writer.write(buffer, bufsize); + bufsize = 0; + } + + void write(const void* data, size_t size) + { + if (bufsize + size > sizeof(buffer)) + { + flush(); + + if (size > sizeof(buffer)) + { + writer.write(data, size); + return; + } + } + else + { + memcpy(buffer + bufsize, data, size); + bufsize += size; + } + } + + void write(const char* data) + { + write(data, strlen(data)); + } + + void write(char data) + { + if (bufsize + 1 > sizeof(buffer)) flush(); + + buffer[bufsize++] = data; + } + + xml_writer& writer; + char buffer[8192]; + size_t bufsize; + }; + + template void text_output_escaped(xml_buffered_writer& writer, const char* s, opt1) + { + const bool attribute = opt1::o1; + + while (*s) + { + const char* prev = s; + + // While *s is a usual symbol + while (*s && *s != '&' && *s != '<' && *s != '>' && (*s != '"' || !attribute) + && (*s < 0 || *s >= 32 || (*s == '\r' && !attribute) || (*s == '\n' && !attribute) || *s == '\t')) + ++s; + + writer.write(prev, static_cast(s - prev)); + + switch (*s) + { + case 0: break; + case '&': + writer.write("&"); + ++s; + break; + case '<': + writer.write("<"); + ++s; + break; + case '>': + writer.write(">"); + ++s; + break; + case '"': + writer.write("""); + ++s; + break; + case '\r': + writer.write(" "); + ++s; + break; + case '\n': + writer.write(" "); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = (unsigned char)*s++; + + char buf[8]; + sprintf(buf, "&#%d;", ch); + + writer.write(buf); + } + } + } + } + + void node_output(xml_buffered_writer& writer, const xml_node& node, const char* indent, unsigned int flags, unsigned int depth) + { + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + switch (node.type()) + { + case node_document: + { + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth); + break; + } + + case node_element: + { + writer.write('<'); + writer.write(node.name()); + + for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) + { + writer.write(' '); + writer.write(a.name()); + writer.write('='); + writer.write('"'); + + text_output_escaped(writer, a.value(), opt1_to_type<1>()); + + writer.write('"'); + } + + if (flags & format_raw) + { + if (!node.first_child()) + writer.write(" />"); + else + { + writer.write('>'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + writer.write('<'); + writer.write('/'); + writer.write(node.name()); + writer.write('>'); + } + } + else if (!node.first_child()) + writer.write(" />\n"); + else if (node.first_child() == node.last_child() && node.first_child().type() == node_pcdata) + { + writer.write('>'); + + text_output_escaped(writer, node.first_child().value(), opt1_to_type<0>()); + + writer.write('<'); + writer.write('/'); + writer.write(node.name()); + writer.write('>'); + writer.write('\n'); + } + else + { + writer.write('>'); + writer.write('\n'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + writer.write('<'); + writer.write('/'); + writer.write(node.name()); + writer.write('>'); + writer.write('\n'); + } + + break; + } + + case node_pcdata: + text_output_escaped(writer, node.value(), opt1_to_type<0>()); + break; + + case node_cdata: + writer.write(""); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_comment: + writer.write(""); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_pi: + writer.write(""); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + default: + ; + } + } } namespace pugi @@ -1386,6 +1562,26 @@ namespace pugi } } + xml_writer_file::xml_writer_file(void* file): file(file) + { + } + + void xml_writer_file::write(const void* data, size_t size) + { + fwrite(data, size, 1, static_cast(file)); + } + +#ifndef PUGIXML_NO_STL + xml_writer_stream::xml_writer_stream(std::ostream& stream): stream(&stream) + { + } + + void xml_writer_stream::write(const void* data, size_t size) + { + stream->write(reinterpret_cast(data), size); + } +#endif + xml_tree_walker::xml_tree_walker(): _depth(0) { } @@ -2181,100 +2377,14 @@ namespace pugi } } -#ifndef PUGIXML_NO_STL - void xml_node::print(std::ostream& os, const char* indent, unsigned int flags, unsigned int depth) + void xml_node::print(xml_writer& writer, const char* indent, unsigned int flags, unsigned int depth) { if (empty()) return; - if ((flags & format_indent) != 0 && (flags & format_raw) == 0) - for (unsigned int i = 0; i < depth; ++i) os << indent; + xml_buffered_writer buffered_writer(writer); - switch (type()) - { - case node_document: - { - for (xml_node n = first_child(); n; n = n.next_sibling()) - n.print(os, indent, flags, depth); - break; - } - - case node_element: - { - os << '<' << name(); - - for (xml_attribute a = first_attribute(); a; a = a.next_attribute()) - { - os << ' ' << a.name() << "=\""; - - text_output_escaped(os, a.value(), opt1_to_type<1>()); - - os << "\""; - } - - if (flags & format_raw) - { - if (!_root->first_child) // 0 children - os << " />"; - else - { - os << ">"; - for (xml_node n = first_child(); n; n = n.next_sibling()) - n.print(os, indent, flags, depth + 1); - os << ""; - } - } - else if (!_root->first_child) // 0 children - os << " />\n"; - else if (_root->first_child == _root->last_child && first_child().type() == node_pcdata) - { - os << ">"; - - text_output_escaped(os, first_child().value(), opt1_to_type<0>()); - - os << "\n"; - } - else - { - os << ">\n"; - - for (xml_node n = first_child(); n; n = n.next_sibling()) - n.print(os, indent, flags, depth + 1); - - if ((flags & format_indent) != 0 && (flags & format_raw) == 0) - for (unsigned int i = 0; i < depth; ++i) os << indent; - - os << "\n"; - } - - break; - } - - case node_pcdata: - text_output_escaped(os, value(), opt1_to_type<0>()); - break; - - case node_cdata: - os << ""; - if ((flags & format_raw) == 0) os << "\n"; - break; - - case node_comment: - os << ""; - if ((flags & format_raw) == 0) os << "\n"; - break; - - case node_pi: - os << ""; - if ((flags & format_raw) == 0) os << "\n"; - break; - - default: - ; - } + node_output(buffered_writer, *this, indent, flags, depth); } -#endif #ifdef __BORLANDC__ bool operator&&(const xml_node& lhs, bool rhs) @@ -2599,25 +2709,34 @@ namespace pugi return res; } -#ifndef PUGIXML_NO_STL - bool xml_document::save_file(const char* name, const char* indent, unsigned int flags) + void xml_document::save(xml_writer& writer, const char* indent, unsigned int flags) { - std::ofstream out(name, std::ios::out); - if (!out) return false; + xml_buffered_writer buffered_writer(writer); if (flags & format_write_bom_utf8) { static const unsigned char utf8_bom[] = {0xEF, 0xBB, 0xBF}; - out.write(reinterpret_cast(utf8_bom), 3); + buffered_writer.write(utf8_bom, 3); } - out << ""; - if (!(flags & format_raw)) out << "\n"; - print(out, indent, flags); + buffered_writer.write(""); + if (!(flags & format_raw)) buffered_writer.write("\n"); + node_output(buffered_writer, *this, indent, flags, 0); + } + + bool xml_document::save_file(const char* name, const char* indent, unsigned int flags) + { + FILE* file = fopen(name, "wb"); + if (!file) return false; + + xml_writer_file writer(file); + save(writer, indent, flags); + + fclose(file); + return true; } -#endif void xml_document::precompute_document_order() { diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 927d18e..bebc246 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -288,6 +288,67 @@ namespace pugi }; #endif + /** + * Abstract writer class + * \see xml_node::print + */ + class xml_writer + { + public: + /** + * Virtual dtor + */ + virtual ~xml_writer() {} + + /** + * Write memory chunk into stream/file/whatever + * + * \param data - data pointer + * \param size - data size + */ + virtual void write(const void* data, size_t size) = 0; + }; + + /** xml_writer implementation for FILE* + * \see xml_writer + */ + class xml_writer_file: public xml_writer + { + public: + /** + * Construct writer instance + * + * \param file - this is FILE* object, void* is used to avoid header dependencies on stdio + */ + xml_writer_file(void* file); + + virtual void write(const void* data, size_t size); + + private: + void* file; + }; + + #ifndef PUGIXML_NO_STL + /** xml_writer implementation for streams + * \see xml_writer + */ + class xml_writer_stream: public xml_writer + { + public: + /** + * Construct writer instance + * + * \param stream - output stream object + */ + xml_writer_stream(std::ostream& stream); + + virtual void write(const void* data, size_t size); + + private: + std::ostream* stream; + }; + #endif + /** * A light-weight wrapper for manipulating attributes in DOM tree. * Note: xml_attribute does not allocate any memory for the attribute it wraps; it only wraps a @@ -947,7 +1008,7 @@ namespace pugi */ template xml_node find_node(Predicate pred) const; -#ifndef PUGIXML_NO_STL + #ifndef PUGIXML_NO_STL /** * Get the absolute node path from root as a text string. * @@ -955,7 +1016,7 @@ namespace pugi * \return path string (e.g. '/bookstore/book/author'). */ std::string path(char delimiter = '/') const; -#endif + #endif /** * Search for a node by path. @@ -1014,17 +1075,15 @@ namespace pugi /// \internal Document order or 0 if not set unsigned int document_order() const; - #ifndef PUGIXML_NO_STL /** - * Print subtree to stream + * Print subtree to writer * - * \param os - output stream + * \param writer - writer object * \param indent - indentation string * \param flags - formatting flags * \param depth - starting depth (used for indentation) */ - void print(std::ostream& os, const char* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0); - #endif + void print(xml_writer& writer, const char* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0); }; #ifdef __BORLANDC__ @@ -1381,7 +1440,15 @@ namespace pugi */ bool parse(const transfer_ownership_tag&, char* xmlstr, unsigned int options = parse_default); -#ifndef PUGIXML_NO_STL + /** + * Save XML to writer + * + * \param writer - writer object + * \param indent - indentation string + * \param flags - formatting flags + */ + void save(xml_writer& writer, const char* indent = "\t", unsigned int flags = format_default); + /** * Save XML to file * @@ -1391,7 +1458,6 @@ namespace pugi * \return success flag */ bool save_file(const char* name, const char* indent = "\t", unsigned int flags = format_default); -#endif /** * Compute document order for the whole tree -- cgit v1.2.3