0
0
mirror of https://github.com/zeux/pugixml.git synced 2025-01-14 09:57:57 +08:00

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
This commit is contained in:
arseny.kapoulkine 2009-01-05 22:16:46 +00:00
parent e59c153d97
commit 29e7b7bfd3
2 changed files with 346 additions and 161 deletions

View File

@ -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 << "&amp;";
++s;
break;
case '<':
os << "&lt;";
++s;
break;
case '>':
os << "&gt;";
++s;
break;
case '"':
os << "&quot;";
++s;
break;
case '\r':
os << "&#13;";
++s;
break;
case '\n':
os << "&#10;";
++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("&amp;");
++s;
break;
case '<':
writer.write("&lt;");
++s;
break;
case '>':
writer.write("&gt;");
++s;
break;
case '"':
writer.write("&quot;");
++s;
break;
case '\r':
writer.write("&#13;");
++s;
break;
case '\n':
writer.write("&#10;");
++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()
{

View File

@ -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 <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