rapidxml_print.hpp
Go to the documentation of this file.
1 #ifndef RAPIDXML_PRINT_HPP_INCLUDED
2 #define RAPIDXML_PRINT_HPP_INCLUDED
3 #pragma once
4 
5 // Copyright (C) 2006, 2009 Marcin Kalicinski
6 // Version 1.13
7 // Revision $DateTime: 2009/05/13 01:46:17 $
8 //! \file rapidxml_print.hpp This file contains rapidxml printer implementation
9 
10 #include "rapidxml.hpp"
11 
12 // Only include streams if not disabled
13 #ifndef RAPIDXML_NO_STREAMS
14 #include <iterator>
15 #include <ostream>
16 #endif
17 
18 namespace rapidxml
19 {
20 
21  ///////////////////////////////////////////////////////////////////////
22  // Printing flags
23 
24  const int print_no_indenting =
25  0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
26 
27  ///////////////////////////////////////////////////////////////////////
28  // Internal
29 
30  //! \cond internal
31  namespace internal
32  {
33 
34  ///////////////////////////////////////////////////////////////////////////
35  // Internal character operations
36 
37  // Copy characters from given range to given output iterator
38  template <class OutIt, class Ch>
39  inline OutIt
40  copy_chars(const Ch* begin, const Ch* end, OutIt out)
41  {
42  while (begin != end)
43  {
44  *out++ = *begin++;
45  }
46 
47  return out;
48  }
49 
50  // Copy characters from given range to given output iterator and expand
51  // characters into references (&lt; &gt; &apos; &quot; &amp;)
52  template <class OutIt, class Ch>
53  inline OutIt
54  copy_and_expand_chars(const Ch* begin, const Ch* end, Ch noexpand, OutIt out)
55  {
56  while (begin != end)
57  {
58  if (*begin == noexpand)
59  {
60  *out++ = *begin; // No expansion, copy character
61  }
62  else
63  {
64  switch (*begin)
65  {
66  case Ch('<'):
67  *out++ = Ch('&');
68  *out++ = Ch('l');
69  *out++ = Ch('t');
70  *out++ = Ch(';');
71  break;
72 
73  case Ch('>'):
74  *out++ = Ch('&');
75  *out++ = Ch('g');
76  *out++ = Ch('t');
77  *out++ = Ch(';');
78  break;
79 
80  case Ch('\''):
81  *out++ = Ch('&');
82  *out++ = Ch('a');
83  *out++ = Ch('p');
84  *out++ = Ch('o');
85  *out++ = Ch('s');
86  *out++ = Ch(';');
87  break;
88 
89  case Ch('"'):
90  *out++ = Ch('&');
91  *out++ = Ch('q');
92  *out++ = Ch('u');
93  *out++ = Ch('o');
94  *out++ = Ch('t');
95  *out++ = Ch(';');
96  break;
97 
98  case Ch('&'):
99  *out++ = Ch('&');
100  *out++ = Ch('a');
101  *out++ = Ch('m');
102  *out++ = Ch('p');
103  *out++ = Ch(';');
104  break;
105 
106  default:
107  *out++ = *begin; // No expansion, copy character
108  }
109  }
110 
111  ++begin; // Step to next character
112  }
113 
114  return out;
115  }
116 
117  // Fill given output iterator with repetitions of the same character
118  template <class OutIt, class Ch>
119  inline OutIt
120  fill_chars(OutIt out, int n, Ch ch)
121  {
122  for (int i = 0; i < n; ++i)
123  {
124  *out++ = ch;
125  }
126 
127  return out;
128  }
129 
130  // Find character
131  template <class Ch, Ch ch>
132  inline bool
133  find_char(const Ch* begin, const Ch* end)
134  {
135  while (begin != end)
136  if (*begin++ == ch)
137  {
138  return true;
139  }
140 
141  return false;
142  }
143 
144  ///////////////////////////////////////////////////////////////////////////
145  // Internal printing operations
146 
147  /// Forward declarations
148 
149  template <class OutIt, class Ch>
150  OutIt print_children(OutIt out, const xml_node<Ch>* node, int flags, int indent);
151 
152  template <class OutIt, class Ch>
153  inline OutIt print_element_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
154 
155  template <class OutIt, class Ch>
156  inline OutIt print_attributes(OutIt out, const xml_node<Ch>* node, int flags);
157 
158  template <class OutIt, class Ch>
159  inline OutIt print_data_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
160 
161  template <class OutIt, class Ch>
162  inline OutIt print_cdata_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
163 
164  template <class OutIt, class Ch>
165  inline OutIt
166  print_declaration_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
167 
168  template <class OutIt, class Ch>
169  inline OutIt print_comment_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
170 
171  template <class OutIt, class Ch>
172  inline OutIt print_doctype_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
173 
174  template <class OutIt, class Ch>
175  inline OutIt print_pi_node(OutIt out, const xml_node<Ch>* node, int flags, int indent);
176 
177  // Print node
178  template <class OutIt, class Ch>
179  inline OutIt
180  print_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
181  {
182  // Print proper node type
183  switch (node->type())
184  {
185 
186  // Document
187  case node_document:
188  out = print_children(out, node, flags, indent);
189  break;
190 
191  // Element
192  case node_element:
193  out = print_element_node(out, node, flags, indent);
194  break;
195 
196  // Data
197  case node_data:
198  out = print_data_node(out, node, flags, indent);
199  break;
200 
201  // CDATA
202  case node_cdata:
203  out = print_cdata_node(out, node, flags, indent);
204  break;
205 
206  // Declaration
207  case node_declaration:
208  out = print_declaration_node(out, node, flags, indent);
209  break;
210 
211  // Comment
212  case node_comment:
213  out = print_comment_node(out, node, flags, indent);
214  break;
215 
216  // Doctype
217  case node_doctype:
218  out = print_doctype_node(out, node, flags, indent);
219  break;
220 
221  // Pi
222  case node_pi:
223  out = print_pi_node(out, node, flags, indent);
224  break;
225 
226  // Unknown
227  default:
228  assert(0);
229  break;
230  }
231 
232  // If indenting not disabled, add line break after node
233  if (!(flags & print_no_indenting))
234  {
235  *out = Ch('\n'), ++out;
236  }
237 
238  // Return modified iterator
239  return out;
240  }
241 
242  // Print children of the node
243  template <class OutIt, class Ch>
244  inline OutIt
245  print_children(OutIt out, const xml_node<Ch>* node, int flags, int indent)
246  {
247  for (xml_node<Ch>* child = node->first_node(); child; child = child->next_sibling())
248  {
249  out = print_node(out, child, flags, indent);
250  }
251 
252  return out;
253  }
254 
255  // Print attributes of the node
256  template <class OutIt, class Ch>
257  inline OutIt
258  print_attributes(OutIt out, const xml_node<Ch>* node, int flags)
259  {
260  for (xml_attribute<Ch>* attribute = node->first_attribute(); attribute;
261  attribute = attribute->next_attribute())
262  {
263  if (attribute->name() && attribute->value())
264  {
265  // Print attribute name
266  *out = Ch(' '), ++out;
267  out = copy_chars(
268  attribute->name(), attribute->name() + attribute->name_size(), out);
269  *out = Ch('='), ++out;
270 
271  // Print attribute value using appropriate quote type
272  if (find_char<Ch, Ch('"')>(attribute->value(),
273  attribute->value() + attribute->value_size()))
274  {
275  *out = Ch('\''), ++out;
276  out = copy_and_expand_chars(attribute->value(),
277  attribute->value() + attribute->value_size(),
278  Ch('"'),
279  out);
280  *out = Ch('\''), ++out;
281  }
282  else
283  {
284  *out = Ch('"'), ++out;
285  out = copy_and_expand_chars(attribute->value(),
286  attribute->value() + attribute->value_size(),
287  Ch('\''),
288  out);
289  *out = Ch('"'), ++out;
290  }
291  }
292  }
293 
294  return out;
295  }
296 
297  // Print data node
298  template <class OutIt, class Ch>
299  inline OutIt
300  print_data_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
301  {
302  assert(node->type() == node_data);
303 
304  if (!(flags & print_no_indenting))
305  {
306  out = fill_chars(out, indent, Ch('\t'));
307  }
308 
309  out = copy_and_expand_chars(
310  node->value(), node->value() + node->value_size(), Ch(0), out);
311  return out;
312  }
313 
314  // Print data node
315  template <class OutIt, class Ch>
316  inline OutIt
317  print_cdata_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
318  {
319  assert(node->type() == node_cdata);
320 
321  if (!(flags & print_no_indenting))
322  {
323  out = fill_chars(out, indent, Ch('\t'));
324  }
325 
326  *out = Ch('<');
327  ++out;
328  *out = Ch('!');
329  ++out;
330  *out = Ch('[');
331  ++out;
332  *out = Ch('C');
333  ++out;
334  *out = Ch('D');
335  ++out;
336  *out = Ch('A');
337  ++out;
338  *out = Ch('T');
339  ++out;
340  *out = Ch('A');
341  ++out;
342  *out = Ch('[');
343  ++out;
344  out = copy_chars(node->value(), node->value() + node->value_size(), out);
345  *out = Ch(']');
346  ++out;
347  *out = Ch(']');
348  ++out;
349  *out = Ch('>');
350  ++out;
351  return out;
352  }
353 
354  // Print element node
355  template <class OutIt, class Ch>
356  inline OutIt
357  print_element_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
358  {
359  assert(node->type() == node_element);
360 
361  // Print element name and attributes, if any
362  if (!(flags & print_no_indenting))
363  {
364  out = fill_chars(out, indent, Ch('\t'));
365  }
366 
367  *out = Ch('<'), ++out;
368  out = copy_chars(node->name(), node->name() + node->name_size(), out);
369  out = print_attributes(out, node, flags);
370 
371  // If node is childless
372  if (node->value_size() == 0 && !node->first_node())
373  {
374  // Print childless node tag ending
375  *out = Ch('/'), ++out;
376  *out = Ch('>'), ++out;
377  }
378  else
379  {
380  // Print normal node tag ending
381  *out = Ch('>'), ++out;
382 
383  // Test if node contains a single data node only (and no other nodes)
384  xml_node<Ch>* child = node->first_node();
385 
386  if (!child)
387  {
388  // If node has no children, only print its value without indenting
389  out = copy_and_expand_chars(
390  node->value(), node->value() + node->value_size(), Ch(0), out);
391  }
392  else if (child->next_sibling() == nullptr && child->type() == node_data)
393  {
394  // If node has a sole data child, only print its value without indenting
395  out = copy_and_expand_chars(
396  child->value(), child->value() + child->value_size(), Ch(0), out);
397  }
398  else
399  {
400  // Print all children with full indenting
401  if (!(flags & print_no_indenting))
402  {
403  *out = Ch('\n'), ++out;
404  }
405 
406  out = print_children(out, node, flags, indent + 1);
407 
408  if (!(flags & print_no_indenting))
409  {
410  out = fill_chars(out, indent, Ch('\t'));
411  }
412  }
413 
414  // Print node end
415  *out = Ch('<'), ++out;
416  *out = Ch('/'), ++out;
417  out = copy_chars(node->name(), node->name() + node->name_size(), out);
418  *out = Ch('>'), ++out;
419  }
420 
421  return out;
422  }
423 
424  // Print declaration node
425  template <class OutIt, class Ch>
426  inline OutIt
427  print_declaration_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
428  {
429  // Print declaration start
430  if (!(flags & print_no_indenting))
431  {
432  out = fill_chars(out, indent, Ch('\t'));
433  }
434 
435  *out = Ch('<'), ++out;
436  *out = Ch('?'), ++out;
437  *out = Ch('x'), ++out;
438  *out = Ch('m'), ++out;
439  *out = Ch('l'), ++out;
440 
441  // Print attributes
442  out = print_attributes(out, node, flags);
443 
444  // Print declaration end
445  *out = Ch('?'), ++out;
446  *out = Ch('>'), ++out;
447 
448  return out;
449  }
450 
451  // Print comment node
452  template <class OutIt, class Ch>
453  inline OutIt
454  print_comment_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
455  {
456  assert(node->type() == node_comment);
457 
458  if (!(flags & print_no_indenting))
459  {
460  out = fill_chars(out, indent, Ch('\t'));
461  }
462 
463  *out = Ch('<'), ++out;
464  *out = Ch('!'), ++out;
465  *out = Ch('-'), ++out;
466  *out = Ch('-'), ++out;
467  out = copy_chars(node->value(), node->value() + node->value_size(), out);
468  *out = Ch('-'), ++out;
469  *out = Ch('-'), ++out;
470  *out = Ch('>'), ++out;
471  return out;
472  }
473 
474  // Print doctype node
475  template <class OutIt, class Ch>
476  inline OutIt
477  print_doctype_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
478  {
479  assert(node->type() == node_doctype);
480 
481  if (!(flags & print_no_indenting))
482  {
483  out = fill_chars(out, indent, Ch('\t'));
484  }
485 
486  *out = Ch('<'), ++out;
487  *out = Ch('!'), ++out;
488  *out = Ch('D'), ++out;
489  *out = Ch('O'), ++out;
490  *out = Ch('C'), ++out;
491  *out = Ch('T'), ++out;
492  *out = Ch('Y'), ++out;
493  *out = Ch('P'), ++out;
494  *out = Ch('E'), ++out;
495  *out = Ch(' '), ++out;
496  out = copy_chars(node->value(), node->value() + node->value_size(), out);
497  *out = Ch('>'), ++out;
498  return out;
499  }
500 
501  // Print pi node
502  template <class OutIt, class Ch>
503  inline OutIt
504  print_pi_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
505  {
506  assert(node->type() == node_pi);
507 
508  if (!(flags & print_no_indenting))
509  {
510  out = fill_chars(out, indent, Ch('\t'));
511  }
512 
513  *out = Ch('<'), ++out;
514  *out = Ch('?'), ++out;
515  out = copy_chars(node->name(), node->name() + node->name_size(), out);
516  *out = Ch(' '), ++out;
517  out = copy_chars(node->value(), node->value() + node->value_size(), out);
518  *out = Ch('?'), ++out;
519  *out = Ch('>'), ++out;
520  return out;
521  }
522 
523  } // namespace internal
524 
525  //! \endcond
526 
527  ///////////////////////////////////////////////////////////////////////////
528  // Printing
529 
530  //! Prints XML to given output iterator.
531  //! \param out Output iterator to print to.
532  //! \param node Node to be printed. Pass xml_document to print entire document.
533  //! \param flags Flags controlling how XML is printed.
534  //! \return Output iterator pointing to position immediately after last character of printed text.
535  template <class OutIt, class Ch>
536  inline OutIt
537  print(OutIt out, const xml_node<Ch>& node, int flags = 0)
538  {
539  return internal::print_node(out, &node, flags, 0);
540  }
541 
542 #ifndef RAPIDXML_NO_STREAMS
543 
544  //! Prints XML to given output stream.
545  //! \param out Output stream to print to.
546  //! \param node Node to be printed. Pass xml_document to print entire document.
547  //! \param flags Flags controlling how XML is printed.
548  //! \return Output stream.
549  template <class Ch>
550  inline std::basic_ostream<Ch>&
551  print(std::basic_ostream<Ch>& out, const xml_node<Ch>& node, int flags = 0)
552  {
553  print(std::ostream_iterator<Ch>(out), node, flags);
554  return out;
555  }
556 
557  //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
558  //! \param out Output stream to print to.
559  //! \param node Node to be printed.
560  //! \return Output stream.
561  template <class Ch>
562  inline std::basic_ostream<Ch>&
563  operator<<(std::basic_ostream<Ch>& out, const xml_node<Ch>& node)
564  {
565  return print(out, node);
566  }
567 
568 #endif
569 
570 } // namespace rapidxml
571 #endif
rapidxml::print
OutIt print(OutIt out, const xml_node< Ch > &node, int flags=0)
Prints XML to given output iterator.
Definition: rapidxml_print.hpp:537
rapidxml::xml_node::next_sibling
xml_node< Ch > * next_sibling(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets next sibling node, optionally matching node name.
Definition: rapidxml.hpp:1189
rapidxml::node_data
@ node_data
A data node. Name is empty. Value contains data text.
Definition: rapidxml.hpp:152
rapidxml::operator<<
std::basic_ostream< Ch > & operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node)
Prints formatted XML to given output stream.
Definition: rapidxml_print.hpp:563
rapidxml::node_element
@ node_element
An element node. Name contains element name. Value contains text of first data node.
Definition: rapidxml.hpp:151
rapidxml::node_declaration
@ node_declaration
A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalon...
Definition: rapidxml.hpp:155
rapidxml::node_document
@ node_document
A document node. Name and value are empty.
Definition: rapidxml.hpp:150
rapidxml::xml_node::first_attribute
xml_attribute< Ch > * first_attribute(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets first attribute of node, optionally matching attribute name.
Definition: rapidxml.hpp:1224
rapidxml::xml_attribute
Class representing attribute node of XML document.
Definition: rapidxml.hpp:142
rapidxml
Definition: rapidxml.hpp:62
rapidxml::node_pi
@ node_pi
A PI node. Name contains target. Value contains instructions.
Definition: rapidxml.hpp:157
rapidxml::print_no_indenting
const int print_no_indenting
Printer flag instructing the printer to suppress indenting of XML. See print() function.
Definition: rapidxml_print.hpp:24
rapidxml::node_cdata
@ node_cdata
A CDATA node. Name is empty. Value contains data text.
Definition: rapidxml.hpp:153
rapidxml::xml_node::first_node
xml_node< Ch > * first_node(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets first child node, optionally matching node name.
Definition: rapidxml.hpp:1084
rapidxml::xml_node
Class representing a node of XML document.
Definition: rapidxml.hpp:140
rapidxml::xml_node::type
node_type type() const
Gets type of node.
Definition: rapidxml.hpp:1055
rapidxml::node_doctype
@ node_doctype
A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
Definition: rapidxml.hpp:156
rapidxml.hpp
rapidxml::node_comment
@ node_comment
A comment node. Name is empty. Value contains comment text.
Definition: rapidxml.hpp:154