diff options
author | arseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640> | 2009-01-05 22:16:46 +0000 |
---|---|---|
committer | arseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640> | 2009-01-05 22:16:46 +0000 |
commit | 29e7b7bfd39d8acea0346c236a7c11d15d5376e2 (patch) | |
tree | 0cd33840e2094b0a2a6dde507202f4662f53404e | |
parent | e59c153d9741f76ed6b07ea405484c3374decd8b (diff) |
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
-rw-r--r-- | src/pugixml.cpp | 423 | ||||
-rw-r--r-- | 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 <bool _1, bool _2, bool _3, bool _4> const bool opt4_to_type<_1, _2, _3, _4>::o3 = _3;
template <bool _1, bool _2, bool _3, bool _4> const bool opt4_to_type<_1, _2, _3, _4>::o4 = _4;
-#ifndef PUGIXML_NO_STL
- template <typename opt1> 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<std::streamsize>(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 <typename opt1> 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<size_t>(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("<![CDATA[");
+ writer.write(node.value());
+ writer.write("]]>");
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_comment:
+ writer.write("<!--");
+ writer.write(node.value());
+ writer.write("-->");
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_pi:
+ writer.write("<?");
+ writer.write(node.name());
+ if (node.value()[0])
+ {
+ writer.write(' ');
+ writer.write(node.value());
+ }
+ 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*>(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<const char*>(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 << "</" << name() << ">";
- }
- }
- 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 << "</" << name() << ">\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 << "</" << name() << ">\n";
- }
-
- break;
- }
-
- case node_pcdata:
- text_output_escaped(os, value(), opt1_to_type<0>());
- break;
-
- case node_cdata:
- os << "<![CDATA[" << value() << "]]>";
- if ((flags & format_raw) == 0) os << "\n";
- break;
-
- case node_comment:
- os << "<!--" << value() << "-->";
- if ((flags & format_raw) == 0) os << "\n";
- break;
-
- case node_pi:
- os << "<?" << name();
- if (value()[0]) os << ' ' << value();
- 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<const char*>(utf8_bom), 3);
+ buffered_writer.write(utf8_bom, 3);
}
- out << "<?xml version=\"1.0\"?>";
- if (!(flags & format_raw)) out << "\n";
- print(out, indent, flags);
+ buffered_writer.write("<?xml version=\"1.0\"?>");
+ 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 @@ -289,6 +289,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 * pointer to existing attribute. @@ -947,7 +1008,7 @@ namespace pugi */ template <typename Predicate> 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 |