• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, '&', "&amp;", 3);
109        encode(str, '<', "&lt;", 2);
110        encode(str, '>', "&gt;", 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