1 #ifndef RAPIDXML_PRINT_HPP_INCLUDED
2 #define RAPIDXML_PRINT_HPP_INCLUDED
3 
4 // Copyright (C) 2006, 2009 Marcin Kalicinski
5 // Version 1.13
6 // Revision $DateTime: 2009/05/13 01:46:17 $
7 //! \file rapidxml_print.hpp This file contains rapidxml printer implementation
8 
9 #include "rapidxml.hpp"
10 
11 // Only include streams if not disabled
12 #ifndef RAPIDXML_NO_STREAMS
13     #include <ostream>
14     #include <iterator>
15 #endif
16 
17 namespace rapidxml
18 {
19 
20     ///////////////////////////////////////////////////////////////////////
21     // Printing flags
22 
23     const int print_no_indenting = 0x1;   //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
24 
25     ///////////////////////////////////////////////////////////////////////
26     // Internal
27 
28     //! \cond internal
29     namespace internal
30     {
31 
32         ///////////////////////////////////////////////////////////////////////////
33         // Internal character operations
34 
35         // Copy characters from given range to given output iterator
36         template<class OutIt, class Ch>
copy_chars(const Ch * begin,const Ch * end,OutIt out)37         inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
38         {
39             while (begin != end)
40                 *out++ = *begin++;
41             return out;
42         }
43 
44         // Copy characters from given range to given output iterator and expand
45         // characters into references (&lt; &gt; &apos; &quot; &amp;)
46         template<class OutIt, class Ch>
copy_and_expand_chars(const Ch * begin,const Ch * end,Ch noexpand,OutIt out)47         inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
48         {
49             while (begin != end)
50             {
51                 if (*begin == noexpand)
52                 {
53                     *out++ = *begin;    // No expansion, copy character
54                 }
55                 else
56                 {
57                     switch (*begin)
58                     {
59                     case Ch('<'):
60                         *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
61                         break;
62                     case Ch('>'):
63                         *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
64                         break;
65                     case Ch('\''):
66                         *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
67                         break;
68                     case Ch('"'):
69                         *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
70                         break;
71                     case Ch('&'):
72                         *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
73                         break;
74                     default:
75                         *out++ = *begin;    // No expansion, copy character
76                     }
77                 }
78                 ++begin;    // Step to next character
79             }
80             return out;
81         }
82 
83         // Fill given output iterator with repetitions of the same character
84         template<class OutIt, class Ch>
fill_chars(OutIt out,int n,Ch ch)85         inline OutIt fill_chars(OutIt out, int n, Ch ch)
86         {
87             for (int i = 0; i < n; ++i)
88                 *out++ = ch;
89             return out;
90         }
91 
92         // Find character
93         template<class Ch, Ch ch>
find_char(const Ch * begin,const Ch * end)94         inline bool find_char(const Ch *begin, const Ch *end)
95         {
96             while (begin != end)
97                 if (*begin++ == ch)
98                     return true;
99             return false;
100         }
101 
102         ///////////////////////////////////////////////////////////////////////////
103         // Internal printing operations
104 
105         // Print node
106         template<class OutIt, class Ch>
print_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)107         inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
108         {
109             // Print proper node type
110             switch (node->type())
111             {
112 
113             // Document
114             case node_document:
115                 out = print_children(out, node, flags, indent);
116                 break;
117 
118             // Element
119             case node_element:
120                 out = print_element_node(out, node, flags, indent);
121                 break;
122 
123             // Data
124             case node_data:
125                 out = print_data_node(out, node, flags, indent);
126                 break;
127 
128             // CDATA
129             case node_cdata:
130                 out = print_cdata_node(out, node, flags, indent);
131                 break;
132 
133             // Declaration
134             case node_declaration:
135                 out = print_declaration_node(out, node, flags, indent);
136                 break;
137 
138             // Comment
139             case node_comment:
140                 out = print_comment_node(out, node, flags, indent);
141                 break;
142 
143             // Doctype
144             case node_doctype:
145                 out = print_doctype_node(out, node, flags, indent);
146                 break;
147 
148             // Pi
149             case node_pi:
150                 out = print_pi_node(out, node, flags, indent);
151                 break;
152 
153                 // Unknown
154             default:
155                 assert(0);
156                 break;
157             }
158 
159             // If indenting not disabled, add line break after node
160             if (!(flags & print_no_indenting))
161                 *out = Ch('\n'), ++out;
162 
163             // Return modified iterator
164             return out;
165         }
166 
167         // Print children of the node
168         template<class OutIt, class Ch>
print_children(OutIt out,const xml_node<Ch> * node,int flags,int indent)169         inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
170         {
171             for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
172                 out = print_node(out, child, flags, indent);
173             return out;
174         }
175 
176         // Print attributes of the node
177         template<class OutIt, class Ch>
print_attributes(OutIt out,const xml_node<Ch> * node,int flags)178         inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
179         {
180             for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
181             {
182                 if (attribute->name() && attribute->value())
183                 {
184                     // Print attribute name
185                     *out = Ch(' '), ++out;
186                     out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
187                     *out = Ch('='), ++out;
188                     // Print attribute value using appropriate quote type
189                     if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
190                     {
191                         *out = Ch('\''), ++out;
192                         out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
193                         *out = Ch('\''), ++out;
194                     }
195                     else
196                     {
197                         *out = Ch('"'), ++out;
198                         out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
199                         *out = Ch('"'), ++out;
200                     }
201                 }
202             }
203             return out;
204         }
205 
206         // Print data node
207         template<class OutIt, class Ch>
print_data_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)208         inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
209         {
210             assert(node->type() == node_data);
211             if (!(flags & print_no_indenting))
212                 out = fill_chars(out, indent, Ch('\t'));
213             out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
214             return out;
215         }
216 
217         // Print data node
218         template<class OutIt, class Ch>
print_cdata_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)219         inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
220         {
221             assert(node->type() == node_cdata);
222             if (!(flags & print_no_indenting))
223                 out = fill_chars(out, indent, Ch('\t'));
224             *out = Ch('<'); ++out;
225             *out = Ch('!'); ++out;
226             *out = Ch('['); ++out;
227             *out = Ch('C'); ++out;
228             *out = Ch('D'); ++out;
229             *out = Ch('A'); ++out;
230             *out = Ch('T'); ++out;
231             *out = Ch('A'); ++out;
232             *out = Ch('['); ++out;
233             out = copy_chars(node->value(), node->value() + node->value_size(), out);
234             *out = Ch(']'); ++out;
235             *out = Ch(']'); ++out;
236             *out = Ch('>'); ++out;
237             return out;
238         }
239 
240         // Print element node
241         template<class OutIt, class Ch>
print_element_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)242         inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
243         {
244             assert(node->type() == node_element);
245 
246             // Print element name and attributes, if any
247             if (!(flags & print_no_indenting))
248                 out = fill_chars(out, indent, Ch('\t'));
249             *out = Ch('<'), ++out;
250             out = copy_chars(node->name(), node->name() + node->name_size(), out);
251             out = print_attributes(out, node, flags);
252 
253             // If node is childless
254             if (node->value_size() == 0 && !node->first_node())
255             {
256                 // Print childless node tag ending
257                 *out = Ch('/'), ++out;
258                 *out = Ch('>'), ++out;
259             }
260             else
261             {
262                 // Print normal node tag ending
263                 *out = Ch('>'), ++out;
264 
265                 // Test if node contains a single data node only (and no other nodes)
266                 xml_node<Ch> *child = node->first_node();
267                 if (!child)
268                 {
269                     // If node has no children, only print its value without indenting
270                     out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
271                 }
272                 else if (child->next_sibling() == 0 && child->type() == node_data)
273                 {
274                     // If node has a sole data child, only print its value without indenting
275                     out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
276                 }
277                 else
278                 {
279                     // Print all children with full indenting
280                     if (!(flags & print_no_indenting))
281                         *out = Ch('\n'), ++out;
282                     out = print_children(out, node, flags, indent + 1);
283                     if (!(flags & print_no_indenting))
284                         out = fill_chars(out, indent, Ch('\t'));
285                 }
286 
287                 // Print node end
288                 *out = Ch('<'), ++out;
289                 *out = Ch('/'), ++out;
290                 out = copy_chars(node->name(), node->name() + node->name_size(), out);
291                 *out = Ch('>'), ++out;
292             }
293             return out;
294         }
295 
296         // Print declaration node
297         template<class OutIt, class Ch>
print_declaration_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)298         inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
299         {
300             // Print declaration start
301             if (!(flags & print_no_indenting))
302                 out = fill_chars(out, indent, Ch('\t'));
303             *out = Ch('<'), ++out;
304             *out = Ch('?'), ++out;
305             *out = Ch('x'), ++out;
306             *out = Ch('m'), ++out;
307             *out = Ch('l'), ++out;
308 
309             // Print attributes
310             out = print_attributes(out, node, flags);
311 
312             // Print declaration end
313             *out = Ch('?'), ++out;
314             *out = Ch('>'), ++out;
315 
316             return out;
317         }
318 
319         // Print comment node
320         template<class OutIt, class Ch>
print_comment_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)321         inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
322         {
323             assert(node->type() == node_comment);
324             if (!(flags & print_no_indenting))
325                 out = fill_chars(out, indent, Ch('\t'));
326             *out = Ch('<'), ++out;
327             *out = Ch('!'), ++out;
328             *out = Ch('-'), ++out;
329             *out = Ch('-'), ++out;
330             out = copy_chars(node->value(), node->value() + node->value_size(), out);
331             *out = Ch('-'), ++out;
332             *out = Ch('-'), ++out;
333             *out = Ch('>'), ++out;
334             return out;
335         }
336 
337         // Print doctype node
338         template<class OutIt, class Ch>
print_doctype_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)339         inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
340         {
341             assert(node->type() == node_doctype);
342             if (!(flags & print_no_indenting))
343                 out = fill_chars(out, indent, Ch('\t'));
344             *out = Ch('<'), ++out;
345             *out = Ch('!'), ++out;
346             *out = Ch('D'), ++out;
347             *out = Ch('O'), ++out;
348             *out = Ch('C'), ++out;
349             *out = Ch('T'), ++out;
350             *out = Ch('Y'), ++out;
351             *out = Ch('P'), ++out;
352             *out = Ch('E'), ++out;
353             *out = Ch(' '), ++out;
354             out = copy_chars(node->value(), node->value() + node->value_size(), out);
355             *out = Ch('>'), ++out;
356             return out;
357         }
358 
359         // Print pi node
360         template<class OutIt, class Ch>
print_pi_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)361         inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
362         {
363             assert(node->type() == node_pi);
364             if (!(flags & print_no_indenting))
365                 out = fill_chars(out, indent, Ch('\t'));
366             *out = Ch('<'), ++out;
367             *out = Ch('?'), ++out;
368             out = copy_chars(node->name(), node->name() + node->name_size(), out);
369             *out = Ch(' '), ++out;
370             out = copy_chars(node->value(), node->value() + node->value_size(), out);
371             *out = Ch('?'), ++out;
372             *out = Ch('>'), ++out;
373             return out;
374         }
375 
376     }
377     //! \endcond
378 
379     ///////////////////////////////////////////////////////////////////////////
380     // Printing
381 
382     //! Prints XML to given output iterator.
383     //! \param out Output iterator to print to.
384     //! \param node Node to be printed. Pass xml_document to print entire document.
385     //! \param flags Flags controlling how XML is printed.
386     //! \return Output iterator pointing to position immediately after last character of printed text.
387     template<class OutIt, class Ch>
print(OutIt out,const xml_node<Ch> & node,int flags=0)388     inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
389     {
390         return internal::print_node(out, &node, flags, 0);
391     }
392 
393 #ifndef RAPIDXML_NO_STREAMS
394 
395     //! Prints XML to given output stream.
396     //! \param out Output stream to print to.
397     //! \param node Node to be printed. Pass xml_document to print entire document.
398     //! \param flags Flags controlling how XML is printed.
399     //! \return Output stream.
400     template<class Ch>
print(std::basic_ostream<Ch> & out,const xml_node<Ch> & node,int flags=0)401     inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
402     {
403         print(std::ostream_iterator<Ch>(out), node, flags);
404         return out;
405     }
406 
407     //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
408     //! \param out Output stream to print to.
409     //! \param node Node to be printed.
410     //! \return Output stream.
411     template<class Ch>
operator <<(std::basic_ostream<Ch> & out,const xml_node<Ch> & node)412     inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
413     {
414         return print(out, node);
415     }
416 
417 #endif
418 
419 }
420 
421 #endif
422