1/*============================================================================= 2 Copyright (c) 2001-2008 Hartmut Kaiser 3 Copyright (c) 2001-2003 Daniel Nuffer 4 http://spirit.sourceforge.net/ 5 6 Use, modification and distribution is subject to the Boost Software 7 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 8 http://www.boost.org/LICENSE_1_0.txt) 9=============================================================================*/ 10 11#ifndef BOOST_SPIRIT_CLASSIC_TREE_IMPL_TREE_TO_XML_IPP 12#define BOOST_SPIRIT_CLASSIC_TREE_IMPL_TREE_TO_XML_IPP 13 14#include <cstdio> 15#include <cstdarg> 16#include <locale> 17#include <string> 18#include <cstring> 19 20#include <map> 21#include <iostream> 22#include <boost/config.hpp> 23#include <boost/assert.hpp> 24#include <boost/scoped_array.hpp> 25 26#ifdef BOOST_NO_STRINGSTREAM 27#include <strstream> 28#define BOOST_SPIRIT_OSSTREAM std::ostrstream 29inline 30std::string BOOST_SPIRIT_GETSTRING(std::ostrstream& ss) 31{ 32 ss << std::ends; 33 std::string rval = ss.str(); 34 ss.freeze(false); 35 return rval; 36} 37#else 38#include <sstream> 39#define BOOST_SPIRIT_GETSTRING(ss) ss.str() 40#define BOOST_SPIRIT_OSSTREAM std::basic_ostringstream<CharT> 41#endif 42 43namespace boost { namespace spirit { 44 45BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN 46 47namespace impl { 48 49 /////////////////////////////////////////////////////////////////////////// 50 template <typename CharT> 51 struct string_lit; 52 53 template <> 54 struct string_lit<char> 55 { 56 static char get(char c) { return c; } 57 static std::string get(char const* str = "") { return str; } 58 }; 59 60 template <> 61 struct string_lit<wchar_t> 62 { 63 static wchar_t get(char c) 64 { 65 typedef std::ctype<wchar_t> ctype_t; 66 return std::use_facet<ctype_t>(std::locale()).widen(c); 67 } 68 static std::basic_string<wchar_t> get(char const* source = "") 69 { 70 using namespace std; // some systems have size_t in ns std 71 size_t len = strlen(source); 72 boost::scoped_array<wchar_t> result (new wchar_t[len+1]); 73 result.get()[len] = '\0'; 74 75 // working with wide character streams is supported only if the 76 // platform provides the std::ctype<wchar_t> facet 77 BOOST_ASSERT(std::has_facet<std::ctype<wchar_t> >(std::locale())); 78 79 std::use_facet<std::ctype<wchar_t> >(std::locale()) 80 .widen(source, source + len, result.get()); 81 return result.get(); 82 } 83 }; 84} 85 86// xml formatting helper classes 87namespace xml { 88 89 template <typename CharT> 90 inline void 91 encode (std::basic_string<CharT> &str, char s, char const *r, int len) 92 { 93 typedef typename std::basic_string<CharT>::size_type size_type; 94 95 size_type pos = 0; 96 while ((pos = str.find_first_of (impl::string_lit<CharT>::get(s), pos)) != 97 size_type(std::basic_string<CharT>::npos)) 98 { 99 str.replace (pos, 1, impl::string_lit<CharT>::get(r)); 100 pos += len; 101 } 102 } 103 104 template <typename CharT> 105 inline std::basic_string<CharT> 106 encode (std::basic_string<CharT> str) 107 { 108 encode(str, '&', "&", 3); 109 encode(str, '<', "<", 2); 110 encode(str, '>', ">", 2); 111 encode(str, '\r', "\\r", 1); 112 encode(str, '\n', "\\n", 1); 113 return str; 114 } 115 116 template <typename CharT> 117 inline std::basic_string<CharT> 118 encode (CharT const *text) 119 { 120 return encode (std::basic_string<CharT>(text)); 121 } 122 123 // format a xml attribute 124 template <typename CharT> 125 struct attribute 126 { 127 attribute() 128 { 129 } 130 131 attribute (std::basic_string<CharT> const& key_, 132 std::basic_string<CharT> const& value_) 133 : key (key_), value(value_) 134 { 135 } 136 137 bool has_value() 138 { 139 return value.size() > 0; 140 } 141 142 std::basic_string<CharT> key; 143 std::basic_string<CharT> value; 144 }; 145 146 template <typename CharT> 147 inline std::basic_ostream<CharT>& 148 operator<< (std::basic_ostream<CharT> &ostrm, attribute<CharT> const &attr) 149 { 150 if (0 == attr.key.size()) 151 return ostrm; 152 ostrm << impl::string_lit<CharT>::get(" ") << encode(attr.key) 153 << impl::string_lit<CharT>::get("=\"") << encode(attr.value) 154 << impl::string_lit<CharT>::get("\""); 155 return ostrm; 156 } 157 158 // output a xml element (base class, not used directly) 159 template <typename CharT> 160 class element 161 { 162 protected: 163 element(std::basic_ostream<CharT> &ostrm_, bool incr_indent_ = true) 164 : ostrm(ostrm_), incr_indent(incr_indent_) 165 { 166 if (incr_indent) ++get_indent(); 167 } 168 ~element() 169 { 170 if (incr_indent) --get_indent(); 171 } 172 173 public: 174 void output_space () 175 { 176 for (int i = 0; i < get_indent(); i++) 177 ostrm << impl::string_lit<CharT>::get(" "); 178 } 179 180 protected: 181 int &get_indent() 182 { 183 static int indent; 184 185 return indent; 186 } 187 188 std::basic_ostream<CharT> &ostrm; 189 bool incr_indent; 190 }; 191 192 // a xml node 193 template <typename CharT> 194 class node : public element<CharT> 195 { 196 public: 197 node (std::basic_ostream<CharT> &ostrm_, 198 std::basic_string<CharT> const& tag_, attribute<CharT> &attr) 199 : element<CharT>(ostrm_), tag(tag_) 200 { 201 this->output_space(); 202 this->ostrm 203 << impl::string_lit<CharT>::get("<") << tag_ << attr 204 << impl::string_lit<CharT>::get(">\n"); 205 } 206 node (std::basic_ostream<CharT> &ostrm_, 207 std::basic_string<CharT> const& tag_) 208 : element<CharT>(ostrm_), tag(tag_) 209 { 210 this->output_space(); 211 this->ostrm 212 << impl::string_lit<CharT>::get("<") << tag_ 213 << impl::string_lit<CharT>::get(">\n"); 214 } 215 ~node() 216 { 217 this->output_space(); 218 this->ostrm 219 << impl::string_lit<CharT>::get("</") << tag 220 << impl::string_lit<CharT>::get(">\n"); 221 } 222 223 private: 224 std::basic_string<CharT> tag; 225 }; 226 227 template <typename CharT> 228 class text : public element<CharT> 229 { 230 public: 231 text (std::basic_ostream<CharT> &ostrm_, 232 std::basic_string<CharT> const& tag, 233 std::basic_string<CharT> const& textlit) 234 : element<CharT>(ostrm_) 235 { 236 this->output_space(); 237 this->ostrm 238 << impl::string_lit<CharT>::get("<") << tag 239 << impl::string_lit<CharT>::get(">") << encode(textlit) 240 << impl::string_lit<CharT>::get("</") << tag 241 << impl::string_lit<CharT>::get(">\n"); 242 } 243 244 text (std::basic_ostream<CharT> &ostrm_, 245 std::basic_string<CharT> const& tag, 246 std::basic_string<CharT> const& textlit, 247 attribute<CharT> &attr) 248 : element<CharT>(ostrm_) 249 { 250 this->output_space(); 251 this->ostrm 252 << impl::string_lit<CharT>::get("<") << tag << attr 253 << impl::string_lit<CharT>::get(">") << encode(textlit) 254 << impl::string_lit<CharT>::get("</") << tag 255 << impl::string_lit<CharT>::get(">\n"); 256 } 257 258 text (std::basic_ostream<CharT> &ostrm_, 259 std::basic_string<CharT> const& tag, 260 std::basic_string<CharT> const& textlit, 261 attribute<CharT> &attr1, attribute<CharT> &attr2) 262 : element<CharT>(ostrm_) 263 { 264 this->output_space(); 265 this->ostrm 266 << impl::string_lit<CharT>::get("<") << tag << attr1 << attr2 267 << impl::string_lit<CharT>::get(">") << encode(textlit) 268 << impl::string_lit<CharT>::get("</") << tag 269 << impl::string_lit<CharT>::get(">\n"); 270 } 271 }; 272 273 // a xml comment 274 template <typename CharT> 275 class comment : public element<CharT> 276 { 277 public: 278 comment (std::basic_ostream<CharT> &ostrm_, 279 std::basic_string<CharT> const& commentlit) 280 : element<CharT>(ostrm_, false) 281 { 282 if ('\0' != commentlit[0]) 283 { 284 this->output_space(); 285 this->ostrm << impl::string_lit<CharT>::get("<!-- ") 286 << encode(commentlit) 287 << impl::string_lit<CharT>::get(" -->\n"); 288 } 289 } 290 }; 291 292 // a xml document 293 template <typename CharT> 294 class document : public element<CharT> 295 { 296 public: 297 document (std::basic_ostream<CharT> &ostrm_) 298 : element<CharT>(ostrm_) 299 { 300 this->get_indent() = -1; 301 this->ostrm << impl::string_lit<CharT>::get( 302 "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); 303 } 304 305 document (std::basic_ostream<CharT> &ostrm_, 306 std::basic_string<CharT> const& mainnode, 307 std::basic_string<CharT> const& dtd) 308 : element<CharT>(ostrm_) 309 { 310 this->get_indent() = -1; 311 this->ostrm << impl::string_lit<CharT>::get( 312 "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); 313 314 this->output_space(); 315 this->ostrm << impl::string_lit<CharT>::get("<!DOCTYPE ") << mainnode 316 << impl::string_lit<CharT>::get(" SYSTEM \"") << dtd 317 << impl::string_lit<CharT>::get("\">\n"); 318 } 319 ~document() 320 { 321 BOOST_SPIRIT_ASSERT(-1 == this->get_indent()); 322 } 323 }; 324 325} // end of namespace xml 326 327namespace impl { 328 329 /////////////////////////////////////////////////////////////////////////// 330 // look up the rule name from the given parser_id 331 template <typename AssocContainerT> 332 inline typename AssocContainerT::value_type::second_type 333 get_rulename (AssocContainerT const &id_to_name_map, 334 BOOST_SPIRIT_CLASSIC_NS::parser_id const &id) 335 { 336 typename AssocContainerT::const_iterator it = id_to_name_map.find(id); 337 if (it != id_to_name_map.end()) 338 return (*it).second; 339 typedef typename AssocContainerT::value_type::second_type second_t; 340 return second_t(); 341 } 342 343 // dump a parse tree as xml 344 template < 345 typename CharT, typename IteratorT, typename GetIdT, typename GetValueT 346 > 347 inline void 348 token_to_xml (std::basic_ostream<CharT> &ostrm, IteratorT const &it, 349 bool is_root, GetIdT const &get_token_id, GetValueT const &get_token_value) 350 { 351 BOOST_SPIRIT_OSSTREAM stream; 352 353 stream << get_token_id(*it) << std::ends; 354 xml::attribute<CharT> token_id ( 355 impl::string_lit<CharT>::get("id"), 356 BOOST_SPIRIT_GETSTRING(stream).c_str()); 357 xml::attribute<CharT> is_root_attr ( 358 impl::string_lit<CharT>::get("is_root"), 359 impl::string_lit<CharT>::get(is_root ? "1" : "")); 360 xml::attribute<CharT> nil; 361 xml::text<CharT>(ostrm, 362 impl::string_lit<CharT>::get("token"), 363 get_token_value(*it).c_str(), 364 token_id, 365 is_root_attr.has_value() ? is_root_attr : nil); 366 } 367 368 template < 369 typename CharT, typename TreeNodeT, typename AssocContainerT, 370 typename GetIdT, typename GetValueT 371 > 372 inline void 373 tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, 374 AssocContainerT const& id_to_name_map, GetIdT const &get_token_id, 375 GetValueT const &get_token_value) 376 { 377 typedef typename TreeNodeT::const_iterator node_iter_t; 378 typedef 379 typename TreeNodeT::value_type::parse_node_t::const_iterator_t 380 value_iter_t; 381 382 xml::attribute<CharT> nil; 383 node_iter_t end = node.end(); 384 for (node_iter_t it = node.begin(); it != end; ++it) 385 { 386 // output a node 387 xml::attribute<CharT> id ( 388 impl::string_lit<CharT>::get("rule"), 389 get_rulename(id_to_name_map, (*it).value.id()).c_str()); 390 xml::node<CharT> currnode (ostrm, 391 impl::string_lit<CharT>::get("parsenode"), 392 (*it).value.id() != 0 && id.has_value() ? id : nil); 393 394 // first dump the value 395 std::size_t cnt = std::distance((*it).value.begin(), (*it).value.end()); 396 397 if (1 == cnt) 398 { 399 token_to_xml (ostrm, (*it).value.begin(), 400 (*it).value.is_root(), get_token_id, get_token_value); 401 } 402 else if (cnt > 1) 403 { 404 xml::node<CharT> value (ostrm, 405 impl::string_lit<CharT>::get("value")); 406 bool is_root = (*it).value.is_root(); 407 408 value_iter_t val_end = (*it).value.end(); 409 for (value_iter_t val_it = (*it).value.begin(); 410 val_it != val_end; ++val_it) 411 { 412 token_to_xml (ostrm, val_it, is_root, get_token_id, 413 get_token_value); 414 } 415 } 416 tree_node_to_xml(ostrm, (*it).children, id_to_name_map, 417 get_token_id, get_token_value); // dump all subnodes 418 } 419 } 420 421 template <typename CharT, typename TreeNodeT, typename AssocContainerT> 422 inline void 423 tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, 424 AssocContainerT const& id_to_name_map) 425 { 426 typedef typename TreeNodeT::const_iterator node_iter_t; 427 428 xml::attribute<CharT> nil; 429 node_iter_t end = node.end(); 430 for (node_iter_t it = node.begin(); it != end; ++it) 431 { 432 // output a node 433 xml::attribute<CharT> id ( 434 impl::string_lit<CharT>::get("rule"), 435 get_rulename(id_to_name_map, (*it).value.id()).c_str()); 436 xml::node<CharT> currnode (ostrm, 437 impl::string_lit<CharT>::get("parsenode"), 438 (*it).value.id() != parser_id() && id.has_value() ? id : nil); 439 440 // first dump the value 441 if ((*it).value.begin() != (*it).value.end()) 442 { 443 std::basic_string<CharT> tokens ((*it).value.begin(), (*it).value.end()); 444 445 if (tokens.size() > 0) 446 { 447 // output all subtokens as one string (for better readability) 448 xml::attribute<CharT> is_root ( 449 impl::string_lit<CharT>::get("is_root"), 450 impl::string_lit<CharT>::get((*it).value.is_root() ? "1" : "")); 451 xml::text<CharT>(ostrm, 452 impl::string_lit<CharT>::get("value"), tokens.c_str(), 453 is_root.has_value() ? is_root : nil); 454 } 455 456 } 457 // dump all subnodes 458 tree_node_to_xml(ostrm, (*it).children, id_to_name_map); 459 } 460 } 461 462} // namespace impl 463 464/////////////////////////////////////////////////////////////////////////////// 465// dump a parse tree as a xml stream (generic variant) 466template < 467 typename CharT, typename TreeNodeT, typename AssocContainerT, 468 typename GetIdT, typename GetValueT 469> 470inline void 471basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, 472std::basic_string<CharT> const &input_line, AssocContainerT const& id_to_name, 473 GetIdT const &get_token_id, GetValueT const &get_token_value) 474{ 475 // generate xml dump 476 xml::document<CharT> doc (ostrm, 477 impl::string_lit<CharT>::get("parsetree"), 478 impl::string_lit<CharT>::get("parsetree.dtd")); 479 xml::comment<CharT> input (ostrm, input_line.c_str()); 480 xml::attribute<CharT> ver ( 481 impl::string_lit<CharT>::get("version"), 482 impl::string_lit<CharT>::get("1.0")); 483 xml::node<CharT> mainnode (ostrm, 484 impl::string_lit<CharT>::get("parsetree"), ver); 485 486 impl::tree_node_to_xml (ostrm, tree, id_to_name, get_token_id, 487 get_token_value); 488} 489 490// dump a parse tree as a xml steam (for character based parsers) 491template <typename CharT, typename TreeNodeT, typename AssocContainerT> 492inline void 493basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, 494 std::basic_string<CharT> const &input_line, 495 AssocContainerT const& id_to_name) 496{ 497 // generate xml dump 498 xml::document<CharT> doc (ostrm, 499 impl::string_lit<CharT>::get("parsetree"), 500 impl::string_lit<CharT>::get("parsetree.dtd")); 501 xml::comment<CharT> input (ostrm, input_line.c_str()); 502 xml::attribute<CharT> ver ( 503 impl::string_lit<CharT>::get("version"), 504 impl::string_lit<CharT>::get("1.0")); 505 xml::node<CharT> mainnode (ostrm, 506 impl::string_lit<CharT>::get("parsetree"), ver); 507 508 impl::tree_node_to_xml(ostrm, tree, id_to_name); 509} 510 511template <typename CharT, typename TreeNodeT> 512inline void 513basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, 514 std::basic_string<CharT> const &input_line) 515{ 516 return basic_tree_to_xml<CharT>(ostrm, tree, input_line, 517 std::map<BOOST_SPIRIT_CLASSIC_NS::parser_id, std::basic_string<CharT> >()); 518} 519 520BOOST_SPIRIT_CLASSIC_NAMESPACE_END 521 522}} // namespace boost::spirit 523 524#undef BOOST_SPIRIT_OSSTREAM 525#undef BOOST_SPIRIT_GETSTRING 526 527#endif 528