| #ifndef RAPIDXML_PRINT_HPP_INCLUDED |
| #define RAPIDXML_PRINT_HPP_INCLUDED |
| |
| // Copyright (C) 2006, 2009 Marcin Kalicinski |
| // Version 1.13 |
| // Revision $DateTime: 2009/05/13 01:46:17 $ |
| //! \file rapidxml_print.hpp This file contains rapidxml printer implementation |
| |
| #include "rapidxml.hpp" |
| |
| // Only include streams if not disabled |
| #ifndef RAPIDXML_NO_STREAMS |
| #include <ostream> |
| #include <iterator> |
| #endif |
| |
| namespace rapidxml |
| { |
| |
| /////////////////////////////////////////////////////////////////////// |
| // Printing flags |
| |
| const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. |
| |
| /////////////////////////////////////////////////////////////////////// |
| // Internal |
| |
| //! \cond internal |
| namespace internal |
| { |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Internal character operations |
| |
| // Copy characters from given range to given output iterator |
| template<class OutIt, class Ch> |
| inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) |
| { |
| while (begin != end) |
| *out++ = *begin++; |
| return out; |
| } |
| |
| // Copy characters from given range to given output iterator and expand |
| // characters into references (< > ' " &) |
| template<class OutIt, class Ch> |
| inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) |
| { |
| while (begin != end) |
| { |
| if (*begin == noexpand) |
| { |
| *out++ = *begin; // No expansion, copy character |
| } |
| else |
| { |
| switch (*begin) |
| { |
| case Ch('<'): |
| *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); |
| break; |
| case Ch('>'): |
| *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); |
| break; |
| case Ch('\''): |
| *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); |
| break; |
| case Ch('"'): |
| *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); |
| break; |
| case Ch('&'): |
| *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); |
| break; |
| default: |
| *out++ = *begin; // No expansion, copy character |
| } |
| } |
| ++begin; // Step to next character |
| } |
| return out; |
| } |
| |
| // Fill given output iterator with repetitions of the same character |
| template<class OutIt, class Ch> |
| inline OutIt fill_chars(OutIt out, int n, Ch ch) |
| { |
| for (int i = 0; i < n; ++i) |
| *out++ = ch; |
| return out; |
| } |
| |
| // Find character |
| template<class Ch, Ch ch> |
| inline bool find_char(const Ch *begin, const Ch *end) |
| { |
| while (begin != end) |
| if (*begin++ == ch) |
| return true; |
| return false; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Internal printing operations |
| |
| // Print node |
| template<class OutIt, class Ch> |
| inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| // Print proper node type |
| switch (node->type()) |
| { |
| |
| // Document |
| case node_document: |
| out = print_children(out, node, flags, indent); |
| break; |
| |
| // Element |
| case node_element: |
| out = print_element_node(out, node, flags, indent); |
| break; |
| |
| // Data |
| case node_data: |
| out = print_data_node(out, node, flags, indent); |
| break; |
| |
| // CDATA |
| case node_cdata: |
| out = print_cdata_node(out, node, flags, indent); |
| break; |
| |
| // Declaration |
| case node_declaration: |
| out = print_declaration_node(out, node, flags, indent); |
| break; |
| |
| // Comment |
| case node_comment: |
| out = print_comment_node(out, node, flags, indent); |
| break; |
| |
| // Doctype |
| case node_doctype: |
| out = print_doctype_node(out, node, flags, indent); |
| break; |
| |
| // Pi |
| case node_pi: |
| out = print_pi_node(out, node, flags, indent); |
| break; |
| |
| // Unknown |
| default: |
| assert(0); |
| break; |
| } |
| |
| // If indenting not disabled, add line break after node |
| if (!(flags & print_no_indenting)) |
| *out = Ch('\n'), ++out; |
| |
| // Return modified iterator |
| return out; |
| } |
| |
| // Print children of the node |
| template<class OutIt, class Ch> |
| inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling()) |
| out = print_node(out, child, flags, indent); |
| return out; |
| } |
| |
| // Print attributes of the node |
| template<class OutIt, class Ch> |
| inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags) |
| { |
| for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) |
| { |
| if (attribute->name() && attribute->value()) |
| { |
| // Print attribute name |
| *out = Ch(' '), ++out; |
| out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); |
| *out = Ch('='), ++out; |
| // Print attribute value using appropriate quote type |
| if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size())) |
| { |
| *out = Ch('\''), ++out; |
| out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); |
| *out = Ch('\''), ++out; |
| } |
| else |
| { |
| *out = Ch('"'), ++out; |
| out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); |
| *out = Ch('"'), ++out; |
| } |
| } |
| } |
| return out; |
| } |
| |
| // Print data node |
| template<class OutIt, class Ch> |
| inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_data); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); |
| return out; |
| } |
| |
| // Print data node |
| template<class OutIt, class Ch> |
| inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_cdata); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'); ++out; |
| *out = Ch('!'); ++out; |
| *out = Ch('['); ++out; |
| *out = Ch('C'); ++out; |
| *out = Ch('D'); ++out; |
| *out = Ch('A'); ++out; |
| *out = Ch('T'); ++out; |
| *out = Ch('A'); ++out; |
| *out = Ch('['); ++out; |
| out = copy_chars(node->value(), node->value() + node->value_size(), out); |
| *out = Ch(']'); ++out; |
| *out = Ch(']'); ++out; |
| *out = Ch('>'); ++out; |
| return out; |
| } |
| |
| // Print element node |
| template<class OutIt, class Ch> |
| inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_element); |
| |
| // Print element name and attributes, if any |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'), ++out; |
| out = copy_chars(node->name(), node->name() + node->name_size(), out); |
| out = print_attributes(out, node, flags); |
| |
| // If node is childless |
| if (node->value_size() == 0 && !node->first_node()) |
| { |
| // Print childless node tag ending |
| *out = Ch('/'), ++out; |
| *out = Ch('>'), ++out; |
| } |
| else |
| { |
| // Print normal node tag ending |
| *out = Ch('>'), ++out; |
| |
| // Test if node contains a single data node only (and no other nodes) |
| xml_node<Ch> *child = node->first_node(); |
| if (!child) |
| { |
| // If node has no children, only print its value without indenting |
| out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); |
| } |
| else if (child->next_sibling() == 0 && child->type() == node_data) |
| { |
| // If node has a sole data child, only print its value without indenting |
| out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); |
| } |
| else |
| { |
| // Print all children with full indenting |
| if (!(flags & print_no_indenting)) |
| *out = Ch('\n'), ++out; |
| out = print_children(out, node, flags, indent + 1); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| } |
| |
| // Print node end |
| *out = Ch('<'), ++out; |
| *out = Ch('/'), ++out; |
| out = copy_chars(node->name(), node->name() + node->name_size(), out); |
| *out = Ch('>'), ++out; |
| } |
| return out; |
| } |
| |
| // Print declaration node |
| template<class OutIt, class Ch> |
| inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| // Print declaration start |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'), ++out; |
| *out = Ch('?'), ++out; |
| *out = Ch('x'), ++out; |
| *out = Ch('m'), ++out; |
| *out = Ch('l'), ++out; |
| |
| // Print attributes |
| out = print_attributes(out, node, flags); |
| |
| // Print declaration end |
| *out = Ch('?'), ++out; |
| *out = Ch('>'), ++out; |
| |
| return out; |
| } |
| |
| // Print comment node |
| template<class OutIt, class Ch> |
| inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_comment); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'), ++out; |
| *out = Ch('!'), ++out; |
| *out = Ch('-'), ++out; |
| *out = Ch('-'), ++out; |
| out = copy_chars(node->value(), node->value() + node->value_size(), out); |
| *out = Ch('-'), ++out; |
| *out = Ch('-'), ++out; |
| *out = Ch('>'), ++out; |
| return out; |
| } |
| |
| // Print doctype node |
| template<class OutIt, class Ch> |
| inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_doctype); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'), ++out; |
| *out = Ch('!'), ++out; |
| *out = Ch('D'), ++out; |
| *out = Ch('O'), ++out; |
| *out = Ch('C'), ++out; |
| *out = Ch('T'), ++out; |
| *out = Ch('Y'), ++out; |
| *out = Ch('P'), ++out; |
| *out = Ch('E'), ++out; |
| *out = Ch(' '), ++out; |
| out = copy_chars(node->value(), node->value() + node->value_size(), out); |
| *out = Ch('>'), ++out; |
| return out; |
| } |
| |
| // Print pi node |
| template<class OutIt, class Ch> |
| inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) |
| { |
| assert(node->type() == node_pi); |
| if (!(flags & print_no_indenting)) |
| out = fill_chars(out, indent, Ch('\t')); |
| *out = Ch('<'), ++out; |
| *out = Ch('?'), ++out; |
| out = copy_chars(node->name(), node->name() + node->name_size(), out); |
| *out = Ch(' '), ++out; |
| out = copy_chars(node->value(), node->value() + node->value_size(), out); |
| *out = Ch('?'), ++out; |
| *out = Ch('>'), ++out; |
| return out; |
| } |
| |
| } |
| //! \endcond |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Printing |
| |
| //! Prints XML to given output iterator. |
| //! \param out Output iterator to print to. |
| //! \param node Node to be printed. Pass xml_document to print entire document. |
| //! \param flags Flags controlling how XML is printed. |
| //! \return Output iterator pointing to position immediately after last character of printed text. |
| template<class OutIt, class Ch> |
| inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0) |
| { |
| return internal::print_node(out, &node, flags, 0); |
| } |
| |
| #ifndef RAPIDXML_NO_STREAMS |
| |
| //! Prints XML to given output stream. |
| //! \param out Output stream to print to. |
| //! \param node Node to be printed. Pass xml_document to print entire document. |
| //! \param flags Flags controlling how XML is printed. |
| //! \return Output stream. |
| template<class Ch> |
| inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0) |
| { |
| print(std::ostream_iterator<Ch>(out), node, flags); |
| return out; |
| } |
| |
| //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. |
| //! \param out Output stream to print to. |
| //! \param node Node to be printed. |
| //! \return Output stream. |
| template<class Ch> |
| inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node) |
| { |
| return print(out, node); |
| } |
| |
| #endif |
| |
| } |
| |
| #endif |