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