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:
parent
e59c153d97
commit
29e7b7bfd3
423
src/pugixml.cpp
423
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()
|
||||
{
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user