1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2002-2006 Marcin Kalicinski
3 // Copyright (C) 2013 Sebastian Redl
4 //
5 // Distributed under the Boost Software License, Version 1.0.
6 // (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // For more information, see www.boost.org
10 // ----------------------------------------------------------------------------
11 #ifndef BOOST_PROPERTY_TREE_DETAIL_XML_PARSER_WRITE_HPP_INCLUDED
12 #define BOOST_PROPERTY_TREE_DETAIL_XML_PARSER_WRITE_HPP_INCLUDED
13 
14 #include <boost/property_tree/ptree.hpp>
15 #include <boost/property_tree/detail/xml_parser_utils.hpp>
16 #include <string>
17 #include <ostream>
18 #include <iomanip>
19 
20 namespace boost { namespace property_tree { namespace xml_parser
21 {
22     template<class Str>
write_xml_indent(std::basic_ostream<typename Str::value_type> & stream,int indent,const xml_writer_settings<Str> & settings)23     void write_xml_indent(std::basic_ostream<typename Str::value_type> &stream,
24           int indent,
25           const xml_writer_settings<Str> & settings
26           )
27     {
28         stream << std::basic_string<typename Str::value_type>(indent * settings.indent_count, settings.indent_char);
29     }
30 
31     template<class Str>
write_xml_comment(std::basic_ostream<typename Str::value_type> & stream,const Str & s,int indent,bool separate_line,const xml_writer_settings<Str> & settings)32     void write_xml_comment(std::basic_ostream<typename Str::value_type> &stream,
33                            const Str &s,
34                            int indent,
35                            bool separate_line,
36                            const xml_writer_settings<Str> & settings
37                            )
38     {
39 	typedef typename Str::value_type Ch;
40         if (separate_line)
41             write_xml_indent(stream,indent,settings);
42         stream << Ch('<') << Ch('!') << Ch('-') << Ch('-');
43         stream << s;
44         stream << Ch('-') << Ch('-') << Ch('>');
45         if (separate_line)
46             stream << Ch('\n');
47     }
48 
49     template<class Str>
write_xml_text(std::basic_ostream<typename Str::value_type> & stream,const Str & s,int indent,bool separate_line,const xml_writer_settings<Str> & settings)50     void write_xml_text(std::basic_ostream<typename Str::value_type> &stream,
51                         const Str &s,
52                         int indent,
53                         bool separate_line,
54                         const xml_writer_settings<Str> & settings
55                         )
56     {
57 	typedef typename Str::value_type Ch;
58         if (separate_line)
59             write_xml_indent(stream,indent,settings);
60         stream << encode_char_entities(s);
61         if (separate_line)
62             stream << Ch('\n');
63     }
64 
65     template<class Ptree>
write_xml_element(std::basic_ostream<typename Ptree::key_type::value_type> & stream,const typename Ptree::key_type & key,const Ptree & pt,int indent,const xml_writer_settings<typename Ptree::key_type> & settings)66     void write_xml_element(std::basic_ostream<typename Ptree::key_type::value_type> &stream,
67                            const typename Ptree::key_type &key,
68                            const Ptree &pt,
69                            int indent,
70                            const xml_writer_settings<typename Ptree::key_type> & settings)
71     {
72         typedef typename Ptree::key_type::value_type Ch;
73         typedef typename Ptree::key_type Str;
74         typedef typename Ptree::const_iterator It;
75 
76         bool want_pretty = settings.indent_count > 0;
77         // Find if elements present
78         bool has_elements = false;
79         bool has_attrs_only = pt.data().empty();
80         for (It it = pt.begin(), end = pt.end(); it != end; ++it)
81         {
82             if (it->first != xmlattr<Str>() )
83             {
84                 has_attrs_only = false;
85                 if (it->first != xmltext<Str>())
86                 {
87                     has_elements = true;
88                     break;
89                 }
90             }
91         }
92 
93         // Write element
94         if (pt.data().empty() && pt.empty())    // Empty key
95         {
96             if (indent >= 0)
97             {
98                 write_xml_indent(stream,indent,settings);
99                 stream << Ch('<') << key <<
100                           Ch('/') << Ch('>');
101                 if (want_pretty)
102                     stream << Ch('\n');
103             }
104         }
105         else    // Nonempty key
106         {
107 
108             // Write opening tag, attributes and data
109             if (indent >= 0)
110             {
111 
112                 // Write opening brace and key
113                 write_xml_indent(stream,indent,settings);
114                 stream << Ch('<') << key;
115 
116                 // Write attributes
117                 if (optional<const Ptree &> attribs = pt.get_child_optional(xmlattr<Str>()))
118                     for (It it = attribs.get().begin(); it != attribs.get().end(); ++it)
119                         stream << Ch(' ') << it->first << Ch('=')
120                                << Ch('"')
121                                << encode_char_entities(
122                                     it->second.template get_value<Str>())
123                                << Ch('"');
124 
125                 if ( has_attrs_only )
126                 {
127                     // Write closing brace
128                     stream << Ch('/') << Ch('>');
129                     if (want_pretty)
130                         stream << Ch('\n');
131                 }
132                 else
133                 {
134                     // Write closing brace
135                     stream << Ch('>');
136 
137                     // Break line if needed and if we want pretty-printing
138                     if (has_elements && want_pretty)
139                         stream << Ch('\n');
140                 }
141             }
142 
143             // Write data text, if present
144             if (!pt.data().empty())
145                 write_xml_text(stream,
146                     pt.template get_value<Str>(),
147                     indent + 1, has_elements && want_pretty, settings);
148 
149             // Write elements, comments and texts
150             for (It it = pt.begin(); it != pt.end(); ++it)
151             {
152                 if (it->first == xmlattr<Str>())
153                     continue;
154                 else if (it->first == xmlcomment<Str>())
155                     write_xml_comment(stream,
156                         it->second.template get_value<Str>(),
157                         indent + 1, want_pretty, settings);
158                 else if (it->first == xmltext<Str>())
159                     write_xml_text(stream,
160                         it->second.template get_value<Str>(),
161                         indent + 1, has_elements && want_pretty, settings);
162                 else
163                     write_xml_element(stream, it->first, it->second,
164                         indent + 1, settings);
165             }
166 
167             // Write closing tag
168             if (indent >= 0 && !has_attrs_only)
169             {
170                 if (has_elements)
171                     write_xml_indent(stream,indent,settings);
172                 stream << Ch('<') << Ch('/') << key << Ch('>');
173                 if (want_pretty)
174                     stream << Ch('\n');
175             }
176 
177         }
178     }
179 
180     template<class Ptree>
write_xml_internal(std::basic_ostream<typename Ptree::key_type::value_type> & stream,const Ptree & pt,const std::string & filename,const xml_writer_settings<typename Ptree::key_type> & settings)181     void write_xml_internal(std::basic_ostream<typename Ptree::key_type::value_type> &stream,
182                             const Ptree &pt,
183                             const std::string &filename,
184                             const xml_writer_settings<typename Ptree::key_type> & settings)
185     {
186         typedef typename Ptree::key_type Str;
187         stream  << detail::widen<Str>("<?xml version=\"1.0\" encoding=\"")
188                 << settings.encoding
189                 << detail::widen<Str>("\"?>\n");
190         write_xml_element(stream, Str(), pt, -1, settings);
191         if (!stream)
192             BOOST_PROPERTY_TREE_THROW(xml_parser_error("write error", filename, 0));
193     }
194 
195 } } }
196 
197 #endif
198