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 // 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