/*============================================================================= Copyright (c) 2001-2008 Hartmut Kaiser Copyright (c) 2001-2003 Daniel Nuffer http://spirit.sourceforge.net/ Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #if !defined(TREE_TO_XML_IPP) #define TREE_TO_XML_IPP #include #include #include #include #include #include #include #include #ifdef BOOST_NO_STRINGSTREAM #include #define BOOST_SPIRIT_OSSTREAM std::ostrstream inline std::string BOOST_SPIRIT_GETSTRING(std::ostrstream& ss) { ss << std::ends; std::string rval = ss.str(); ss.freeze(false); return rval; } #else #include #define BOOST_SPIRIT_GETSTRING(ss) ss.str() #define BOOST_SPIRIT_OSSTREAM std::basic_ostringstream #endif namespace boost { namespace spirit { namespace impl { /////////////////////////////////////////////////////////////////////////// template struct string_lit; template <> struct string_lit { static char get(char c) { return c; } static std::string get(char const* str = "") { return str; } }; template <> struct string_lit { static wchar_t get(char c) { typedef std::ctype ctype_t; return std::use_facet(std::locale()).widen(c); } static std::basic_string get(char const* source = "") { using namespace std; // some systems have size_t in ns std size_t len = strlen(source); std::auto_ptr result (new wchar_t[len+1]); result.get()[len] = '\0'; // working with wide character streams is supported only if the // platform provides the std::ctype facet BOOST_ASSERT(std::has_facet >(std::locale())); std::use_facet >(std::locale()) .widen(source, source + len, result.get()); return result.get(); } }; } // xml formatting helper classes namespace xml { template inline void encode (std::basic_string &str, char s, char const *r, int len) { typedef typename std::basic_string::size_type size_type; size_type pos = 0; while ((pos = str.find_first_of (impl::string_lit::get(s), pos)) != size_type(std::basic_string::npos)) { str.replace (pos, 1, impl::string_lit::get(r)); pos += len; } } template inline std::basic_string encode (std::basic_string str) { encode(str, '&', "&", 3); encode(str, '<', "<", 2); encode(str, '>', ">", 2); encode(str, '\r', "\\r", 1); encode(str, '\n', "\\n", 1); return str; } template inline std::basic_string encode (CharT const *text) { return encode (std::basic_string(text)); } // format a xml attribute template struct attribute { attribute() { } attribute (std::basic_string const& key_, std::basic_string const& value_) : key (key_), value(value_) { } bool has_value() { return value.size() > 0; } std::basic_string key; std::basic_string value; }; template inline std::basic_ostream& operator<< (std::basic_ostream &ostrm, attribute const &attr) { if (0 == attr.key.size()) return ostrm; ostrm << impl::string_lit::get(" ") << encode(attr.key) << impl::string_lit::get("=\"") << encode(attr.value) << impl::string_lit::get("\""); return ostrm; } // output a xml element (base class, not used directly) template class element { protected: element(std::basic_ostream &ostrm_, bool incr_indent_ = true) : ostrm(ostrm_), incr_indent(incr_indent_) { if (incr_indent) ++get_indent(); } ~element() { if (incr_indent) --get_indent(); } public: void output_space () { for (int i = 0; i < get_indent(); i++) ostrm << impl::string_lit::get(" "); } protected: int &get_indent() { static int indent; return indent; } std::basic_ostream &ostrm; bool incr_indent; }; // a xml node template class node : public element { public: node (std::basic_ostream &ostrm_, std::basic_string const& tag_, attribute &attr) : element(ostrm_), tag(tag_) { this->output_space(); this->ostrm << impl::string_lit::get("<") << tag_ << attr << impl::string_lit::get(">\n"); } node (std::basic_ostream &ostrm_, std::basic_string const& tag_) : element(ostrm_), tag(tag_) { this->output_space(); this->ostrm << impl::string_lit::get("<") << tag_ << impl::string_lit::get(">\n"); } ~node() { this->output_space(); this->ostrm << impl::string_lit::get("::get(">\n"); } private: std::basic_string tag; }; template class text : public element { public: text (std::basic_ostream &ostrm_, std::basic_string const& tag, std::basic_string const& textlit) : element(ostrm_) { this->output_space(); this->ostrm << impl::string_lit::get("<") << tag << impl::string_lit::get(">") << encode(textlit) << impl::string_lit::get("::get(">\n"); } text (std::basic_ostream &ostrm_, std::basic_string const& tag, std::basic_string const& textlit, attribute &attr) : element(ostrm_) { this->output_space(); this->ostrm << impl::string_lit::get("<") << tag << attr << impl::string_lit::get(">") << encode(textlit) << impl::string_lit::get("::get(">\n"); } text (std::basic_ostream &ostrm_, std::basic_string const& tag, std::basic_string const& textlit, attribute &attr1, attribute &attr2) : element(ostrm_) { this->output_space(); this->ostrm << impl::string_lit::get("<") << tag << attr1 << attr2 << impl::string_lit::get(">") << encode(textlit) << impl::string_lit::get("::get(">\n"); } }; // a xml comment template class comment : public element { public: comment (std::basic_ostream &ostrm_, std::basic_string const& commentlit) : element(ostrm_, false) { if ('\0' != commentlit[0]) { this->output_space(); this->ostrm << impl::string_lit::get("\n"); } } }; // a xml document template class document : public element { public: document (std::basic_ostream &ostrm_) : element(ostrm_) { this->get_indent() = -1; this->ostrm << impl::string_lit::get( "\n"); } document (std::basic_ostream &ostrm_, std::basic_string const& mainnode, std::basic_string const& dtd) : element(ostrm_) { this->get_indent() = -1; this->ostrm << impl::string_lit::get( "\n"); this->output_space(); this->ostrm << impl::string_lit::get("::get(" SYSTEM \"") << dtd << impl::string_lit::get("\">\n"); } ~document() { BOOST_SPIRIT_ASSERT(-1 == this->get_indent()); } }; } // end of namespace xml namespace impl { /////////////////////////////////////////////////////////////////////////// // look up the rule name from the given parser_id template inline typename AssocContainerT::value_type::second_type get_rulename (AssocContainerT const &id_to_name_map, boost::spirit::parser_id const &id) { typename AssocContainerT::const_iterator it = id_to_name_map.find(id); if (it != id_to_name_map.end()) return (*it).second; typedef typename AssocContainerT::value_type::second_type second_t; return second_t(); } // dump a parse tree as xml template < typename CharT, typename IteratorT, typename GetIdT, typename GetValueT > inline void token_to_xml (std::basic_ostream &ostrm, IteratorT const &it, bool is_root, GetIdT const &get_token_id, GetValueT const &get_token_value) { BOOST_SPIRIT_OSSTREAM stream; stream << get_token_id(*it) << std::ends; xml::attribute token_id ( impl::string_lit::get("id"), BOOST_SPIRIT_GETSTRING(stream).c_str()); xml::attribute is_root_attr ( impl::string_lit::get("is_root"), impl::string_lit::get(is_root ? "1" : "")); xml::attribute nil; xml::text(ostrm, impl::string_lit::get("token"), get_token_value(*it).c_str(), token_id, is_root_attr.has_value() ? is_root_attr : nil); } template < typename CharT, typename TreeNodeT, typename AssocContainerT, typename GetIdT, typename GetValueT > inline void tree_node_to_xml (std::basic_ostream &ostrm, TreeNodeT const &node, AssocContainerT const& id_to_name_map, GetIdT const &get_token_id, GetValueT const &get_token_value) { typedef typename TreeNodeT::const_iterator node_iter_t; typedef typename TreeNodeT::value_type::parse_node_t::const_iterator_t value_iter_t; xml::attribute nil; node_iter_t end = node.end(); for (node_iter_t it = node.begin(); it != end; ++it) { // output a node xml::attribute id ( impl::string_lit::get("rule"), get_rulename(id_to_name_map, (*it).value.id()).c_str()); xml::node currnode (ostrm, impl::string_lit::get("parsenode"), (*it).value.id() != 0 && id.has_value() ? id : nil); // first dump the value std::size_t cnt = std::distance((*it).value.begin(), (*it).value.end()); if (1 == cnt) { token_to_xml (ostrm, (*it).value.begin(), (*it).value.is_root(), get_token_id, get_token_value); } else if (cnt > 1) { xml::node value (ostrm, impl::string_lit::get("value")); bool is_root = (*it).value.is_root(); value_iter_t val_end = (*it).value.end(); for (value_iter_t val_it = (*it).value.begin(); val_it != val_end; ++val_it) { token_to_xml (ostrm, val_it, is_root, get_token_id, get_token_value); } } tree_node_to_xml(ostrm, (*it).children, id_to_name_map, get_token_id, get_token_value); // dump all subnodes } } template inline void tree_node_to_xml (std::basic_ostream &ostrm, TreeNodeT const &node, AssocContainerT const& id_to_name_map) { typedef typename TreeNodeT::const_iterator node_iter_t; xml::attribute nil; node_iter_t end = node.end(); for (node_iter_t it = node.begin(); it != end; ++it) { // output a node xml::attribute id ( impl::string_lit::get("rule"), get_rulename(id_to_name_map, (*it).value.id()).c_str()); xml::node currnode (ostrm, impl::string_lit::get("parsenode"), (*it).value.id() != parser_id() && id.has_value() ? id : nil); // first dump the value if ((*it).value.begin() != (*it).value.end()) { std::basic_string tokens ((*it).value.begin(), (*it).value.end()); if (tokens.size() > 0) { // output all subtokens as one string (for better readability) xml::attribute is_root ( impl::string_lit::get("is_root"), impl::string_lit::get((*it).value.is_root() ? "1" : "")); xml::text(ostrm, impl::string_lit::get("value"), tokens.c_str(), is_root.has_value() ? is_root : nil); } } // dump all subnodes tree_node_to_xml(ostrm, (*it).children, id_to_name_map); } } } // namespace impl /////////////////////////////////////////////////////////////////////////////// // dump a parse tree as a xml stream (generic variant) template < typename CharT, typename TreeNodeT, typename AssocContainerT, typename GetIdT, typename GetValueT > inline void basic_tree_to_xml (std::basic_ostream &ostrm, TreeNodeT const &tree, std::basic_string const &input_line, AssocContainerT const& id_to_name, GetIdT const &get_token_id, GetValueT const &get_token_value) { // generate xml dump xml::document doc (ostrm, impl::string_lit::get("parsetree"), impl::string_lit::get("parsetree.dtd")); xml::comment input (ostrm, input_line.c_str()); xml::attribute ver ( impl::string_lit::get("version"), impl::string_lit::get("1.0")); xml::node mainnode (ostrm, impl::string_lit::get("parsetree"), ver); impl::tree_node_to_xml (ostrm, tree, id_to_name, get_token_id, get_token_value); } // dump a parse tree as a xml steam (for character based parsers) template inline void basic_tree_to_xml (std::basic_ostream &ostrm, TreeNodeT const &tree, std::basic_string const &input_line, AssocContainerT const& id_to_name) { // generate xml dump xml::document doc (ostrm, impl::string_lit::get("parsetree"), impl::string_lit::get("parsetree.dtd")); xml::comment input (ostrm, input_line.c_str()); xml::attribute ver ( impl::string_lit::get("version"), impl::string_lit::get("1.0")); xml::node mainnode (ostrm, impl::string_lit::get("parsetree"), ver); impl::tree_node_to_xml(ostrm, tree, id_to_name); } template inline void basic_tree_to_xml (std::basic_ostream &ostrm, TreeNodeT const &tree, std::basic_string const &input_line) { return basic_tree_to_xml(ostrm, tree, input_line, std::map >()); } }} // namespace boost::spirit #undef BOOST_SPIRIT_OSSTREAM #undef BOOST_SPIRIT_GETSTRING #endif // !defined(PARSE_TREE_XML_HPP)