Ticket #12259: ini_parser.diff

File ini_parser.diff, 10.4 KB (added by sab914@…, 4 years ago)

refactoring

  • ini_parser.hpp

    old new  
    2222
    2323namespace boost { namespace property_tree { namespace ini_parser
    2424{
     25    template <class Str>
     26    inline Str comment_key()
     27    {
     28        return Str("inicomment");
     29    }
    2530
    2631    /**
    2732     * Determines whether the @c flags are valid for use with the ini_parser.
     
    6065     * @throw ini_parser_error If a format violation is found.
    6166     * @param stream Stream from which to read in the property tree.
    6267     * @param[out] pt The property tree to populate.
     68     *
     69     * Comments will be stored in the propertyTree alongside the actual property
     70     * in an additional entry \c key.inicomment (wie von comment_key() returned).
    6371     */
    6472    template<class Ptree>
    6573    void read_ini(std::basic_istream<
     
    7280        const Ch hash = stream.widen('#');
    7381        const Ch lbracket = stream.widen('[');
    7482        const Ch rbracket = stream.widen(']');
     83        const Str commentKey = comment_key<Str>();
    7584
    7685        Ptree local;
    7786        unsigned long line_no = 0;
    7887        Ptree *section = 0;
    7988        Str line;
     89        Str lastComment;
    8090
    8191        // For all lines
    8292        while (stream.good())
     
    96106                // Comment, section or key?
    97107                if (line[0] == semicolon || line[0] == hash)
    98108                {
    99                     // Ignore comments
     109                    // Save comments to intermediate storage
     110                    if (!lastComment.empty())
     111                        lastComment += Ch('\n');
     112                    lastComment += line.substr(1);
    100113                }
    101114                else if (line[0] == lbracket)
    102115                {
     
    114127                            "duplicate section name", "", line_no));
    115128                    section = &local.push_back(
    116129                        std::make_pair(key, Ptree()))->second;
     130                    if (!lastComment.empty())
     131                    {
     132                        section->put(commentKey, lastComment);
     133                        lastComment.clear();
     134                    }
    117135                }
    118136                else
    119137                {
     
    132150                    if (container.find(key) != container.not_found())
    133151                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
    134152                            "duplicate key name", "", line_no));
    135                     container.push_back(std::make_pair(key, Ptree(data)));
     153                    Ptree* section = &container.push_back(std::make_pair(key, Ptree(data)))->second;
     154                    if (!lastComment.empty())
     155                    {
     156                        section->put(commentKey, lastComment);
     157                        lastComment.clear();
     158                    }
    136159                }
    137160            }
    138161        }
     
    193216            }
    194217        }
    195218
     219        template<class Ptree>
     220        void write_comment(std::basic_ostream<
     221                               typename Ptree::key_type::value_type
     222                           > &stream,
     223                           const typename Ptree::key_type& comment,
     224                           const typename Ptree::key_type& commentStart)
     225        {
     226            typedef typename Ptree::key_type::value_type Ch;
     227            if (comment.empty())
     228                return;
     229            typename Ptree::key_type line;
     230            bool hadNonEmptyLine = false;
     231            for (size_t i = 0; i < comment.size(); i++)
     232            {
     233                if (comment[i] == Ch('\n'))
     234                {
     235                    if (!line.empty() || hadNonEmptyLine)
     236                    {
     237                        // dump comment line
     238                        stream << commentStart << line << Ch('\n');
     239                    }
     240                    else
     241                    {
     242                        // preceding empty lines are output without the commentStart
     243                        // dump comment line
     244                        stream << Ch('\n');
     245                    }
     246                    line.clear();
     247                    hadNonEmptyLine |= (!line.empty());
     248                }
     249                else if (comment[i] != Ch('\r'))
     250                {
     251                    // Ignore '\r' (for Windows!)
     252                    line += comment[i];
     253                }
     254            }
     255            if (!line.empty() || hadNonEmptyLine)
     256            {
     257                // dump comment line
     258                stream << commentStart << line << Ch('\n');
     259            }
     260            else
     261            {
     262                // preceding empty lines are output without the commentStart
     263                // dump comment line
     264                stream << Ch('\n');
     265            }
     266        }
     267
    196268        template <typename Ptree>
    197269        void write_keys(std::basic_ostream<
    198270                                      typename Ptree::key_type::value_type
    199271                                  > &stream,
    200272                                  const Ptree& pt,
    201                                   bool throw_on_children)
     273                                  bool throw_on_children,
     274                                  const typename Ptree::key_type& commentKey,
     275                                  const typename Ptree::key_type& commentStart)
    202276        {
    203277            typedef typename Ptree::key_type::value_type Ch;
    204278            for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
    205279                 it != end; ++it)
    206280            {
    207                 if (!it->second.empty()) {
    208                     if (throw_on_children) {
     281                if (it->first != commentKey)
     282                {
     283                    // We ignore the ".comment"-nodes, as these have special meaning!
     284
     285                    // check for existence of comment node
     286                    boost::optional<typename Ptree::key_type> comment = it->second.template get_optional<typename Ptree::key_type>(commentKey);
     287
     288                    if (!it->second.empty() && !(it->second.size() == 1 && comment))
     289                    {
     290                        //only two depth-levels are allowd in INI-files ... but we also have to filter out the additional .comment nodes
     291                        if (throw_on_children)
     292                        {
    209293                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
    210                             "ptree is too deep", "", 0));
     294                                "ptree is too deep (only two depth steps alowed in INI files)", "", 0));
    211295                    }
    212296                    continue;
    213297                }
     298                    if (comment)
     299                    {
     300                        // ... if it exists: output the comment
     301                        write_comment<Ptree>(stream, *comment, commentStart);
     302                    }
    214303                stream << it->first << Ch(' ') << Ch('=') << Ch(' ')
    215304                    << it->second.template get_value<
    216305                        std::basic_string<Ch> >()
    217306                    << Ch('\n');
    218307            }
    219             stream << Ch('\n');
     308            }
    220309        }
    221310
    222311        template <typename Ptree>
    223312        void write_top_level_keys(std::basic_ostream<
    224313                                      typename Ptree::key_type::value_type
    225314                                  > &stream,
    226                                   const Ptree& pt)
     315                                  const Ptree& pt,
     316                                  const typename Ptree::key_type& commentKey,
     317                                  const typename Ptree::key_type& commentStart)
    227318        {
    228             write_keys(stream, pt, false);
     319            write_keys(stream, pt, false, commentKey, commentStart);
    229320        }
    230321
    231322        template <typename Ptree>
    232323        void write_sections(std::basic_ostream<
    233324                                typename Ptree::key_type::value_type
    234325                            > &stream,
    235                             const Ptree& pt)
     326                            const Ptree& pt,
     327                            const typename Ptree::key_type& commentKey,
     328                            const typename Ptree::key_type& commentStart)
    236329        {
    237330            typedef typename Ptree::key_type::value_type Ch;
    238331            for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
     
    243336                    if (!it->second.data().empty())
    244337                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(
    245338                            "mixed data and children", "", 0));
     339                    // empty lines in front of a new section to better separate it from other sections
     340                    stream  << Ch('\n');
     341                    // check for existence of comment node
     342                    boost::optional<typename Ptree::key_type> comment = pt.template get_optional<typename Ptree::key_type>(it->first + "." + commentKey);
     343                    if (comment)
     344                    {
     345                        std::string c = *comment;
     346                        // eat linebreak from start of comment to account for the two explicit \n inserted above!
     347                        while (!c.empty() && c[0] == Ch('\n'))
     348                            c = c.substr(1);
     349                        // ... if it exists: output the comment
     350                        if (!c.empty())
     351                            write_comment<Ptree>(stream, c, commentStart);
     352                    }
    246353                    stream << Ch('[') << it->first << Ch(']') << Ch('\n');
    247                     write_keys(stream, it->second, true);
     354                    write_keys(stream, it->second, true, commentKey, commentStart);
    248355                }
    249356            }
    250357        }
     
    275382        BOOST_ASSERT(validate_flags(flags));
    276383        (void)flags;
    277384
     385        const typename Ptree::key_type commentKey = comment_key<typename Ptree::key_type>();
     386        const typename Ptree::key_type commentStart = "#";
     387
    278388        if (!pt.data().empty())
    279389            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
    280390                "ptree has data on root", "", 0));
    281391        detail::check_dupes(pt);
    282392
    283         detail::write_top_level_keys(stream, pt);
    284         detail::write_sections(stream, pt);
     393        detail::write_top_level_keys(stream, pt, commentKey, commentStart);
     394        detail::write_sections(stream, pt, commentKey, commentStart);
    285395    }
    286396
    287397    /**