PageRenderTime 53ms CodeModel.GetById 10ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Dependencies/Boost/boost/property_tree/ini_parser.hpp

http://hadesmem.googlecode.com/
C++ Header | 310 lines | 205 code | 23 blank | 82 comment | 42 complexity | d625d2cfb48e454253aee92fd876305b MD5 | raw file
  1// ----------------------------------------------------------------------------
  2// Copyright (C) 2002-2006 Marcin Kalicinski
  3// Copyright (C) 2009 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_INI_PARSER_HPP_INCLUDED
 12#define BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
 13
 14#include <boost/property_tree/ptree.hpp>
 15#include <boost/property_tree/detail/ptree_utils.hpp>
 16#include <boost/property_tree/detail/file_parser_error.hpp>
 17#include <fstream>
 18#include <string>
 19#include <sstream>
 20#include <stdexcept>
 21#include <locale>
 22
 23namespace boost { namespace property_tree { namespace ini_parser
 24{
 25
 26    /**
 27     * Determines whether the @c flags are valid for use with the ini_parser.
 28     * @param flags value to check for validity as flags to ini_parser.
 29     * @return true if the flags are valid, false otherwise.
 30     */
 31    inline bool validate_flags(int flags)
 32    {
 33        return flags == 0;
 34    }
 35
 36    /** Indicates an error parsing INI formatted data. */
 37    class ini_parser_error: public file_parser_error
 38    {
 39    public:
 40        /**
 41         * Construct an @c ini_parser_error
 42         * @param message Message describing the parser error.
 43         * @param filename The name of the file being parsed containing the
 44         *                 error.
 45         * @param line The line in the given file where an error was
 46         *             encountered.
 47         */
 48        ini_parser_error(const std::string &message,
 49                         const std::string &filename,
 50                         unsigned long line)
 51            : file_parser_error(message, filename, line)
 52        {
 53        }
 54    };
 55
 56    /**
 57     * Read INI from a the given stream and translate it to a property tree.
 58     * @note Clears existing contents of property tree. In case of error
 59     *       the property tree is not modified.
 60     * @throw ini_parser_error If a format violation is found.
 61     * @param stream Stream from which to read in the property tree.
 62     * @param[out] pt The property tree to populate.
 63     */
 64    template<class Ptree>
 65    void read_ini(std::basic_istream<
 66                    typename Ptree::key_type::value_type> &stream,
 67                  Ptree &pt)
 68    {
 69        typedef typename Ptree::key_type::value_type Ch;
 70        typedef std::basic_string<Ch> Str;
 71        const Ch semicolon = stream.widen(';');
 72        const Ch hash = stream.widen('#');
 73        const Ch lbracket = stream.widen('[');
 74        const Ch rbracket = stream.widen(']');
 75
 76        Ptree local;
 77        unsigned long line_no = 0;
 78        Ptree *section = 0;
 79        Str line;
 80
 81        // For all lines
 82        while (stream.good())
 83        {
 84
 85            // Get line from stream
 86            ++line_no;
 87            std::getline(stream, line);
 88            if (!stream.good() && !stream.eof())
 89                BOOST_PROPERTY_TREE_THROW(ini_parser_error(
 90                    "read error", "", line_no));
 91
 92            // If line is non-empty
 93            line = property_tree::detail::trim(line, stream.getloc());
 94            if (!line.empty())
 95            {
 96                // Comment, section or key?
 97                if (line[0] == semicolon || line[0] == hash)
 98                {
 99                    // Ignore comments
100                }
101                else if (line[0] == lbracket)
102                {
103                    // If the previous section was empty, drop it again.
104                    if (section && section->empty())
105                        local.pop_back();
106                    typename Str::size_type end = line.find(rbracket);
107                    if (end == Str::npos)
108                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
109                            "unmatched '['", "", line_no));
110                    Str key = property_tree::detail::trim(
111                        line.substr(1, end - 1), stream.getloc());
112                    if (local.find(key) != local.not_found())
113                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
114                            "duplicate section name", "", line_no));
115                    section = &local.push_back(
116                        std::make_pair(key, Ptree()))->second;
117                }
118                else
119                {
120                    Ptree &container = section ? *section : local;
121                    typename Str::size_type eqpos = line.find(Ch('='));
122                    if (eqpos == Str::npos)
123                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
124                            "'=' character not found in line", "", line_no));
125                    if (eqpos == 0)
126                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
127                            "key expected", "", line_no));
128                    Str key = property_tree::detail::trim(
129                        line.substr(0, eqpos), stream.getloc());
130                    Str data = property_tree::detail::trim(
131                        line.substr(eqpos + 1, Str::npos), stream.getloc());
132                    if (container.find(key) != container.not_found())
133                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
134                            "duplicate key name", "", line_no));
135                    container.push_back(std::make_pair(key, Ptree(data)));
136                }
137            }
138        }
139        // If the last section was empty, drop it again.
140        if (section && section->empty())
141            local.pop_back();
142
143        // Swap local ptree with result ptree
144        pt.swap(local);
145
146    }
147
148    /**
149     * Read INI from a the given file and translate it to a property tree.
150     * @note Clears existing contents of property tree.  In case of error the
151     *       property tree unmodified.
152     * @throw ini_parser_error In case of error deserializing the property tree.
153     * @param filename Name of file from which to read in the property tree.
154     * @param[out] pt The property tree to populate.
155     * @param loc The locale to use when reading in the file contents.
156     */
157    template<class Ptree>
158    void read_ini(const std::string &filename, 
159                  Ptree &pt,
160                  const std::locale &loc = std::locale())
161    {
162        std::basic_ifstream<typename Ptree::key_type::value_type>
163            stream(filename.c_str());
164        if (!stream)
165            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
166                "cannot open file", filename, 0));
167        stream.imbue(loc);
168        try {
169            read_ini(stream, pt);
170        }
171        catch (ini_parser_error &e) {
172            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
173                e.message(), filename, e.line()));
174        }
175    }
176
177    namespace detail
178    {
179        template<class Ptree>
180        void check_dupes(const Ptree &pt)
181        {
182            if(pt.size() <= 1)
183                return;
184            const typename Ptree::key_type *lastkey = 0;
185            typename Ptree::const_assoc_iterator it = pt.ordered_begin(),
186                                                 end = pt.not_found();
187            lastkey = &it->first;
188            for(++it; it != end; ++it) {
189                if(*lastkey == it->first)
190                    BOOST_PROPERTY_TREE_THROW(ini_parser_error(
191                        "duplicate key", "", 0));
192                lastkey = &it->first;
193            }
194        }
195    }
196
197    /**
198     * Translates the property tree to INI and writes it the given output
199     * stream.
200     * @pre @e pt cannot have data in its root.
201     * @pre @e pt cannot have keys both data and children.
202     * @pre @e pt cannot be deeper than two levels.
203     * @pre There cannot be duplicate keys on any given level of @e pt.
204     * @throw ini_parser_error In case of error translating the property tree to
205     *                         INI or writing to the output stream.
206     * @param stream The stream to which to write the INI representation of the 
207     *               property tree.
208     * @param pt The property tree to tranlsate to INI and output.
209     * @param flags The flags to use when writing the INI file.
210     *              No flags are currently supported.
211     */
212    template<class Ptree>
213    void write_ini(std::basic_ostream<
214                       typename Ptree::key_type::value_type
215                   > &stream,
216                   const Ptree &pt,
217                   int flags = 0)
218    {
219        using detail::check_dupes;
220
221        typedef typename Ptree::key_type::value_type Ch;
222        typedef std::basic_string<Ch> Str;
223
224        BOOST_ASSERT(validate_flags(flags));
225        (void)flags;
226
227        if (!pt.data().empty())
228            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
229                "ptree has data on root", "", 0));
230        check_dupes(pt);
231
232        for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
233             it != end; ++it)
234        {
235            check_dupes(it->second);
236            if (it->second.empty()) {
237                stream << it->first << Ch('=')
238                    << it->second.template get_value<
239                        std::basic_string<Ch> >()
240                    << Ch('\n');
241            } else {
242                if (!it->second.data().empty())
243                    BOOST_PROPERTY_TREE_THROW(ini_parser_error(
244                        "mixed data and children", "", 0));
245                stream << Ch('[') << it->first << Ch(']') << Ch('\n');
246                for (typename Ptree::const_iterator it2 = it->second.begin(),
247                         end2 = it->second.end(); it2 != end2; ++it2)
248                {
249                    if (!it2->second.empty())
250                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
251                            "ptree is too deep", "", 0));
252                    stream << it2->first << Ch('=')
253                        << it2->second.template get_value<
254                            std::basic_string<Ch> >()
255                        << Ch('\n');
256                }
257            }
258        }
259
260    }
261
262    /**
263     * Translates the property tree to INI and writes it the given file.
264     * @pre @e pt cannot have data in its root.
265     * @pre @e pt cannot have keys both data and children.
266     * @pre @e pt cannot be deeper than two levels.
267     * @pre There cannot be duplicate keys on any given level of @e pt.
268     * @throw info_parser_error In case of error translating the property tree
269     *                          to INI or writing to the file.
270     * @param filename The name of the file to which to write the INI
271     *                 representation of the property tree.
272     * @param pt The property tree to tranlsate to INI and output.
273     * @param flags The flags to use when writing the INI file.
274     *              The following flags are supported:
275     * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The
276     *     validity check covers the preconditions but takes <tt>O(n log n)</tt>
277     *     time.
278     * @param loc The locale to use when writing the file.
279     */
280    template<class Ptree>
281    void write_ini(const std::string &filename,
282                   const Ptree &pt,
283                   int flags = 0,
284                   const std::locale &loc = std::locale())
285    {
286        std::basic_ofstream<typename Ptree::key_type::value_type>
287            stream(filename.c_str());
288        if (!stream)
289            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
290                "cannot open file", filename, 0));
291        stream.imbue(loc);
292        try {
293            write_ini(stream, pt, flags);
294        }
295        catch (ini_parser_error &e) {
296            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
297                e.message(), filename, e.line()));
298        }
299    }
300
301} } }
302
303namespace boost { namespace property_tree
304{
305    using ini_parser::ini_parser_error;
306    using ini_parser::read_ini;
307    using ini_parser::write_ini;
308} }
309
310#endif