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 (< > ' " &) 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 // http://stackoverflow.com/questions/14113923/rapidxml-print-header-has-undefined-methods 106 template<class OutIt, class Ch> 107 inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent); 108 109 template<class OutIt, class Ch> 110 inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node); 111 112 template<class OutIt, class Ch> 113 inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 114 115 template<class OutIt, class Ch> 116 inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 117 118 template<class OutIt, class Ch> 119 inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 120 121 template<class OutIt, class Ch> 122 inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 123 124 template<class OutIt, class Ch> 125 inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 126 127 template<class OutIt, class Ch> 128 inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 129 130 template<class OutIt, class Ch> 131 inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent); 132 133 // Print node 134 template<class OutIt, class Ch> print_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)135 inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 136 { 137 // Print proper node type 138 switch (node->type()) 139 { 140 141 // Document 142 case node_document: 143 out = print_children(out, node, flags, indent); 144 break; 145 146 // Element 147 case node_element: 148 out = print_element_node(out, node, flags, indent); 149 break; 150 151 // Data 152 case node_data: 153 out = print_data_node(out, node, flags, indent); 154 break; 155 156 // CDATA 157 case node_cdata: 158 out = print_cdata_node(out, node, flags, indent); 159 break; 160 161 // Declaration 162 case node_declaration: 163 out = print_declaration_node(out, node, flags, indent); 164 break; 165 166 // Comment 167 case node_comment: 168 out = print_comment_node(out, node, flags, indent); 169 break; 170 171 // Doctype 172 case node_doctype: 173 out = print_doctype_node(out, node, flags, indent); 174 break; 175 176 // Pi 177 case node_pi: 178 out = print_pi_node(out, node, flags, indent); 179 break; 180 181 // Unknown 182 default: 183 assert(0); 184 break; 185 } 186 187 // If indenting not disabled, add line break after node 188 if (!(flags & print_no_indenting)) 189 *out = Ch('\n'), ++out; 190 191 // Return modified iterator 192 return out; 193 } 194 195 // Print children of the node 196 template<class OutIt, class Ch> print_children(OutIt out,const xml_node<Ch> * node,int flags,int indent)197 inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent) 198 { 199 for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling()) 200 out = print_node(out, child, flags, indent); 201 return out; 202 } 203 204 // Print attributes of the node 205 template<class OutIt, class Ch> print_attributes(OutIt out,const xml_node<Ch> * node)206 inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node) 207 { 208 for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) 209 { 210 if (attribute->name() && attribute->value()) 211 { 212 // Print attribute name 213 *out = Ch(' '), ++out; 214 out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); 215 *out = Ch('='), ++out; 216 // Print attribute value using appropriate quote type 217 if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size())) 218 { 219 *out = Ch('\''), ++out; 220 out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); 221 *out = Ch('\''), ++out; 222 } 223 else 224 { 225 *out = Ch('"'), ++out; 226 out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); 227 *out = Ch('"'), ++out; 228 } 229 } 230 } 231 return out; 232 } 233 234 // Print data node 235 template<class OutIt, class Ch> print_data_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)236 inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 237 { 238 assert(node->type() == node_data); 239 if (!(flags & print_no_indenting)) { 240 //out = fill_chars(out, indent, Ch('\t')); 241 out = fill_chars(out, indent, Ch(' ')); 242 out = fill_chars(out, indent, Ch(' ')); 243 } 244 out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); 245 return out; 246 } 247 248 // Print data node 249 template<class OutIt, class Ch> print_cdata_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)250 inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 251 { 252 assert(node->type() == node_cdata); 253 if (!(flags & print_no_indenting)) { 254 //out = fill_chars(out, indent, Ch('\t')); 255 out = fill_chars(out, indent, Ch(' ')); 256 out = fill_chars(out, indent, Ch(' ')); 257 } 258 *out = Ch('<'); ++out; 259 *out = Ch('!'); ++out; 260 *out = Ch('['); ++out; 261 *out = Ch('C'); ++out; 262 *out = Ch('D'); ++out; 263 *out = Ch('A'); ++out; 264 *out = Ch('T'); ++out; 265 *out = Ch('A'); ++out; 266 *out = Ch('['); ++out; 267 out = copy_chars(node->value(), node->value() + node->value_size(), out); 268 *out = Ch(']'); ++out; 269 *out = Ch(']'); ++out; 270 *out = Ch('>'); ++out; 271 return out; 272 } 273 274 // Print element node 275 template<class OutIt, class Ch> print_element_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)276 inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 277 { 278 assert(node->type() == node_element); 279 280 // Print element name and attributes, if any 281 if (!(flags & print_no_indenting)) { 282 //out = fill_chars(out, indent, Ch('\t')); 283 out = fill_chars(out, indent, Ch(' ')); 284 out = fill_chars(out, indent, Ch(' ')); 285 } 286 *out = Ch('<'), ++out; 287 out = copy_chars(node->name(), node->name() + node->name_size(), out); 288 out = print_attributes(out, node); 289 290 // If node is childless 291 if (node->value_size() == 0 && !node->first_node()) 292 { 293 // Print childless node tag ending 294 *out = Ch('/'), ++out; 295 *out = Ch('>'), ++out; 296 } 297 else 298 { 299 // Print normal node tag ending 300 *out = Ch('>'), ++out; 301 302 // Test if node contains a single data node only (and no other nodes) 303 xml_node<Ch> *child = node->first_node(); 304 if (!child) 305 { 306 // If node has no children, only print its value without indenting 307 out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); 308 } 309 else if (child->next_sibling() == 0 && child->type() == node_data) 310 { 311 // If node has a sole data child, only print its value without indenting 312 out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); 313 } 314 else 315 { 316 // Print all children with full indenting 317 if (!(flags & print_no_indenting)) 318 *out = Ch('\n'), ++out; 319 out = print_children(out, node, flags, indent + 1); 320 if (!(flags & print_no_indenting)) { 321 //out = fill_chars(out, indent, Ch('\t')); 322 out = fill_chars(out, indent, Ch(' ')); 323 out = fill_chars(out, indent, Ch(' ')); 324 } 325 } 326 327 // Print node end 328 *out = Ch('<'), ++out; 329 *out = Ch('/'), ++out; 330 out = copy_chars(node->name(), node->name() + node->name_size(), out); 331 *out = Ch('>'), ++out; 332 } 333 return out; 334 } 335 336 // Print declaration node 337 template<class OutIt, class Ch> print_declaration_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)338 inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 339 { 340 // Print declaration start 341 if (!(flags & print_no_indenting)) { 342 //out = fill_chars(out, indent, Ch('\t')); 343 out = fill_chars(out, indent, Ch(' ')); 344 out = fill_chars(out, indent, Ch(' ')); 345 } 346 *out = Ch('<'), ++out; 347 *out = Ch('?'), ++out; 348 *out = Ch('x'), ++out; 349 *out = Ch('m'), ++out; 350 *out = Ch('l'), ++out; 351 352 // Print attributes 353 out = print_attributes(out, node); 354 355 // Print declaration end 356 *out = Ch('?'), ++out; 357 *out = Ch('>'), ++out; 358 359 return out; 360 } 361 362 // Print comment node 363 template<class OutIt, class Ch> print_comment_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)364 inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 365 { 366 assert(node->type() == node_comment); 367 if (!(flags & print_no_indenting)) { 368 //out = fill_chars(out, indent, Ch('\t')); 369 out = fill_chars(out, indent, Ch(' ')); 370 out = fill_chars(out, indent, Ch(' ')); 371 } 372 *out = Ch('<'), ++out; 373 *out = Ch('!'), ++out; 374 *out = Ch('-'), ++out; 375 *out = Ch('-'), ++out; 376 out = copy_chars(node->value(), node->value() + node->value_size(), out); 377 *out = Ch('-'), ++out; 378 *out = Ch('-'), ++out; 379 *out = Ch('>'), ++out; 380 return out; 381 } 382 383 // Print doctype node 384 template<class OutIt, class Ch> print_doctype_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)385 inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 386 { 387 assert(node->type() == node_doctype); 388 if (!(flags & print_no_indenting)) { 389 //out = fill_chars(out, indent, Ch('\t')); 390 out = fill_chars(out, indent, Ch(' ')); 391 out = fill_chars(out, indent, Ch(' ')); 392 } 393 *out = Ch('<'), ++out; 394 *out = Ch('!'), ++out; 395 *out = Ch('D'), ++out; 396 *out = Ch('O'), ++out; 397 *out = Ch('C'), ++out; 398 *out = Ch('T'), ++out; 399 *out = Ch('Y'), ++out; 400 *out = Ch('P'), ++out; 401 *out = Ch('E'), ++out; 402 *out = Ch(' '), ++out; 403 out = copy_chars(node->value(), node->value() + node->value_size(), out); 404 *out = Ch('>'), ++out; 405 return out; 406 } 407 408 // Print pi node 409 template<class OutIt, class Ch> print_pi_node(OutIt out,const xml_node<Ch> * node,int flags,int indent)410 inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent) 411 { 412 assert(node->type() == node_pi); 413 if (!(flags & print_no_indenting)) { 414 //out = fill_chars(out, indent, Ch('\t')); 415 out = fill_chars(out, indent, Ch(' ')); 416 out = fill_chars(out, indent, Ch(' ')); 417 } 418 *out = Ch('<'), ++out; 419 *out = Ch('?'), ++out; 420 out = copy_chars(node->name(), node->name() + node->name_size(), out); 421 *out = Ch(' '), ++out; 422 out = copy_chars(node->value(), node->value() + node->value_size(), out); 423 *out = Ch('?'), ++out; 424 *out = Ch('>'), ++out; 425 return out; 426 } 427 428 } 429 //! \endcond 430 431 /////////////////////////////////////////////////////////////////////////// 432 // Printing 433 434 //! Prints XML to given output iterator. 435 //! \param out Output iterator to print to. 436 //! \param node Node to be printed. Pass xml_document to print entire document. 437 //! \param flags Flags controlling how XML is printed. 438 //! \return Output iterator pointing to position immediately after last character of printed text. 439 template<class OutIt, class Ch> print(OutIt out,const xml_node<Ch> & node,int flags=0)440 inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0) 441 { 442 return internal::print_node(out, &node, flags, 0); 443 } 444 445 #ifndef RAPIDXML_NO_STREAMS 446 447 //! Prints XML to given output stream. 448 //! \param out Output stream to print to. 449 //! \param node Node to be printed. Pass xml_document to print entire document. 450 //! \param flags Flags controlling how XML is printed. 451 //! \return Output stream. 452 template<class Ch> print(std::basic_ostream<Ch> & out,const xml_node<Ch> & node,int flags=0)453 inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0) 454 { 455 print(std::ostream_iterator<Ch>(out), node, flags); 456 return out; 457 } 458 459 //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. 460 //! \param out Output stream to print to. 461 //! \param node Node to be printed. 462 //! \return Output stream. 463 template<class Ch> operator <<(std::basic_ostream<Ch> & out,const xml_node<Ch> & node)464 inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node) 465 { 466 return print(out, node); 467 } 468 469 #endif 470 471 } 472 473 #endif 474