mapnik/boost/property_tree/ini_parser.hpp
2006-10-03 09:06:13 +00:00

196 lines
7 KiB
C++

// ----------------------------------------------------------------------------
// Copyright (C) 2002-2005 Marcin Kalicinski
//
// Distributed under 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)
//
// For more information, see www.boost.org
// ----------------------------------------------------------------------------
#ifndef BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
#define BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/detail/ptree_utils.hpp>
#include <boost/property_tree/detail/file_parser_error.hpp>
#include <istream>
#include <string>
#include <sstream>
#include <stdexcept>
#include <locale>
namespace boost { namespace property_tree { namespace ini_parser
{
static const int skip_ini_validity_check = 1; // Skip check if ptree is a valid ini
inline bool validate_flags(int flags)
{
return (flags & ~skip_ini_validity_check) == 0;
}
//! Ini parser error
class ini_parser_error: public file_parser_error
{
public:
ini_parser_error(const std::string &message,
const std::string &filename,
unsigned long line):
file_parser_error(message, filename, line)
{
}
};
//! Read ini from stream
template<class Ptree>
void read_ini(std::basic_istream<typename Ptree::char_type> &stream,
Ptree &pt)
{
typedef typename Ptree::char_type Ch;
typedef std::basic_string<Ch> Str;
Ptree local;
unsigned long line_no = 0;
Ptree *section = 0;
Str line;
// For all lines
while (stream.good())
{
// Get line from stream
++line_no;
std::getline(stream, line);
if (!stream.good() && !stream.eof())
throw ini_parser_error("read error", "", line_no);
// If line is non-empty
line = detail::trim(line, stream.getloc());
if (!line.empty())
{
// Comment, section or key?
if (line[0] == Ch(';'))
{
// Ignore comments
}
else if (line[0] == Ch('['))
{
typename Str::size_type end = line.find(Ch(']'));
if (end == Str::npos)
throw ini_parser_error("unmatched '['", "", line_no);
Str key = detail::trim(line.substr(1, end - 1), stream.getloc());
if (local.find(key) != local.end())
throw ini_parser_error("duplicate section name", "", line_no);
section = &local.push_back(std::make_pair(key, Ptree()))->second;
}
else
{
if (!section)
throw ini_parser_error("section expected", "", line_no);
typename Str::size_type eqpos = line.find(Ch('='));
if (eqpos == Str::npos)
throw ini_parser_error("'=' character not found in line", "", line_no);
if (eqpos == 0)
throw ini_parser_error("key expected", "", line_no);
Str key = detail::trim(line.substr(0, eqpos), stream.getloc());
Str data = detail::trim(line.substr(eqpos + 1, Str::npos), stream.getloc());
if (section->find(key) != section->end())
throw ini_parser_error("duplicate key name", "", line_no);
section->push_back(std::make_pair(key, Ptree(data)));
}
}
}
// Swap local ptree with result ptree
pt.swap(local);
}
//! Read ini from file
template<class Ptree>
void read_ini(const std::string &filename,
Ptree &pt,
const std::locale &loc = std::locale())
{
std::basic_ifstream<typename Ptree::char_type> stream(filename.c_str());
if (!stream)
throw ini_parser_error("cannot open file", filename, 0);
stream.imbue(loc);
try {
read_ini(stream, pt);
}
catch (ini_parser_error &e) {
throw ini_parser_error(e.message(), filename, e.line());
}
}
//! Write ini to stream
template<class Ptree>
void write_ini(std::basic_ostream<typename Ptree::char_type> &stream,
const Ptree &pt,
int flags = 0)
{
typedef typename Ptree::char_type Ch;
typedef std::basic_string<Ch> Str;
BOOST_ASSERT(validate_flags(flags));
// Verify if ptree is not too rich to be saved as ini
if (!(flags & skip_ini_validity_check))
for (typename Ptree::const_iterator it = pt.begin(), end = pt.end(); it != end; ++it)
{
if (!it->second.data().empty())
throw ini_parser_error("ptree has data on root level keys", "", 0);
if (pt.count(it->first) > 1)
throw ini_parser_error("duplicate section name", "", 0);
for (typename Ptree::const_iterator it2 = it->second.begin(), end2 = it->second.end(); it2 != end2; ++it2)
{
if (!it2->second.empty())
throw ini_parser_error("ptree is too deep", "", 0);
if (it->second.count(it2->first) > 1)
throw ini_parser_error("duplicate key name", "", 0);
}
}
// Write ini
for (typename Ptree::const_iterator it = pt.begin(), end = pt.end(); it != end; ++it)
{
stream << Ch('[') << it->first << Ch(']') << Ch('\n');
for (typename Ptree::const_iterator it2 = it->second.begin(), end2 = it->second.end(); it2 != end2; ++it2)
stream << it2->first << Ch('=') << it2->second.template get_own<std::basic_string<Ch> >() << Ch('\n');
}
}
// Write ini to file
template<class Ptree>
void write_ini(const std::string &filename,
const Ptree &pt,
int flags = 0,
const std::locale &loc = std::locale())
{
std::basic_ofstream<typename Ptree::char_type> stream(filename.c_str());
if (!stream)
throw ini_parser_error("cannot open file", filename, 0);
stream.imbue(loc);
try {
write_ini(stream, pt, flags);
}
catch (ini_parser_error &e) {
throw ini_parser_error(e.message(), filename, e.line());
}
}
} } }
namespace boost { namespace property_tree
{
using ini_parser::ini_parser_error;
using ini_parser::read_ini;
using ini_parser::write_ini;
} }
#endif