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 (< > ' " &)
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