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 /src | |
| 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
Diffstat (limited to 'src')
| -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  | 
