// Copyright (C) 2003 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_CONFIG_READER_KERNEl_1_
#define DLIB_CONFIG_READER_KERNEl_1_
#include "config_reader_kernel_abstract.h"
#include <string>
#include <iostream>
#include <sstream>
#include "../algs.h"
#include "../interfaces/enumerable.h"
namespace dlib
{
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking = false
>
class config_reader_kernel_1 : public enumerable<config_reader_kernel_1<map_string_string,
map_string_void,
tokenizer,
checking> >
{
/*!
REQUIREMENTS ON map_string_string
is an implementation of map/map_kernel_abstract.h that maps std::string to std::string
REQUIREMENTS ON map_string_void
is an implementation of map/map_kernel_abstract.h that maps std::string to void*
REQUIREMENTS ON tokenizer
is an implementation of tokenizer/tokenizer_kernel_abstract.h
REQUIREMENTS ON checking
- if (checking == true) then
- The preconditions for this object will be checked.
- else
- The preconditions for this object will NOT be checked.
CONVENTION
key_table.is_in_domain(x) == is_key_defined(x)
block_table.is_in_domain(x) == is_block_defined(x)
key_table[x] == operator[](x)
block_table[x] == (void*)&block(x)
!*/
public:
config_reader_kernel_1();
class config_reader_error : public dlib::error
{
friend class config_reader_kernel_1;
config_reader_error(
unsigned long ln,
bool r = false
) :
dlib::error(ECONFIG_READER),
line_number(ln),
redefinition(r)
{
std::ostringstream sout;
sout << "Error in config_reader while parsing at line number " << line_number << ".";
if (redefinition)
sout << "\nThe identifier on this line has already been defined in this scope.";
const_cast<std::string&>(info) = sout.str();
}
public:
const unsigned long line_number;
const bool redefinition;
};
config_reader_kernel_1(
std::istream& in
);
virtual ~config_reader_kernel_1(
);
void clear (
);
void load_from (
std::istream& in
);
bool is_key_defined (
const std::string& key
) const;
bool is_block_defined (
const std::string& name
) const;
typedef config_reader_kernel_1 this_type;
const this_type& block (
const std::string& name
) const;
const std::string& operator[] (
const std::string& key
) const;
template <
typename queue_of_strings
>
void get_keys (
queue_of_strings& keys
) const;
inline bool at_start (
) const ;
inline void reset (
) const ;
inline bool current_element_valid (
) const ;
inline const this_type& element (
) const ;
inline this_type& element (
) ;
inline bool move_next (
) const ;
inline unsigned long size (
) const ;
inline const std::string& current_block_name (
) const;
private:
static void parse_config_file (
config_reader_kernel_1& cr,
tokenizer& tok,
unsigned long& line_number,
const bool top_of_recursion = true
);
/*!
requires
- line_number == 1
- cr == *this
- top_of_recursion == true
ensures
- parses the data coming from tok and puts it into cr.
throws
- config_reader_error
!*/
map_string_string key_table;
map_string_void block_table;
// restricted functions
config_reader_kernel_1(config_reader_kernel_1&);
config_reader_kernel_1& operator=(config_reader_kernel_1&);
};
// ----------------------------------------------------------------------------------------
/*
This is a bunch of crap so we can enable and disable the DLIB_CASSERT statements
without getting warnings about conditions always being true or false.
*/
namespace config_reader_kernel_1_helpers
{
template <typename cr_type, bool do_check>
struct helper;
template <typename cr_type>
struct helper<cr_type,false>
{
static void check_operator_bracket_precondition (const cr_type&, const std::string& ) {}
static void check_block_precondition (const cr_type&, const std::string& ) {}
static void check_current_block_name_precondition (const cr_type& cr) {}
static void check_element_precondition (const cr_type& cr) {}
};
template <typename cr_type>
struct helper<cr_type,true>
{
static void check_operator_bracket_precondition (const cr_type& cr, const std::string& key)
{
DLIB_CASSERT ( cr.is_key_defined(key) == true ,
"\tconst std::string& config_reader::operator[](key)"
<< "\n\tTo access a key's value in the config_reader the key must actually exist."
<< "\n\tkey == " << key
<< "\n\t&cr: " << &cr
);
}
static void check_block_precondition (const cr_type& cr, const std::string& name)
{
DLIB_CASSERT ( cr.is_block_defined(name) == true ,
"\tconst this_type& config_reader::block(name)"
<< "\n\tTo access a sub block in the config_reader the block must actually exist."
<< "\n\tname == " << name
<< "\n\t&cr: " << &cr
);
}
static void check_current_block_name_precondition (const cr_type& cr)
{
DLIB_CASSERT ( cr.current_element_valid() == true ,
"\tconst std::string& config_reader::current_block_name()"
<< "\n\tYou can't call current_block_name() if the current element isn't valid."
<< "\n\t&cr: " << &cr
);
}
static void check_element_precondition (const cr_type& cr)
{
DLIB_CASSERT ( cr.current_element_valid() == true ,
"\tthis_type& config_reader::element()"
<< "\n\tYou can't call element() if the current element isn't valid."
<< "\n\t&cr: " << &cr
);
}
};
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
config_reader_kernel_1(
)
{
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
void config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
clear(
)
{
// free all our blocks
block_table.reset();
while (block_table.move_next())
{
delete reinterpret_cast<config_reader_kernel_1*>(block_table.element().value());
}
block_table.clear();
key_table.clear();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
void config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
load_from(
std::istream& in
)
{
clear();
tokenizer tok;
tok.set_stream(in);
tok.set_identifier_token(
tok.lowercase_letters() + tok.uppercase_letters(),
tok.lowercase_letters() + tok.uppercase_letters() + tok.numbers() + "_-."
);
unsigned long line_number = 1;
try
{
parse_config_file(*this,tok,line_number);
}
catch (...)
{
clear();
throw;
}
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
config_reader_kernel_1(
std::istream& in
)
{
load_from(in);
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
void config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
parse_config_file(
config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>& cr,
tokenizer& tok,
unsigned long& line_number,
const bool top_of_recursion
)
{
int type;
std::string token;
bool in_comment = false;
bool seen_identifier = false;
std::string identifier;
while (true)
{
tok.get_token(type,token);
// ignore white space
if (type == tokenizer::WHITE_SPACE)
continue;
// basically ignore end of lines
if (type == tokenizer::END_OF_LINE)
{
++line_number;
in_comment = false;
continue;
}
// we are in a comment still so ignore this
if (in_comment)
continue;
// if this is the start of a comment
if (type == tokenizer::CHAR && token[0] == '#')
{
in_comment = true;
continue;
}
// if this is the case then we have just finished parsing a block so we should
// quit this function
if ( (type == tokenizer::CHAR && token[0] == '}' && !top_of_recursion) ||
(type == tokenizer::END_OF_FILE && top_of_recursion) )
{
break;
}
if (seen_identifier)
{
seen_identifier = false;
// the next character should be either a '=' or a '{'
if (type != tokenizer::CHAR || (token[0] != '=' && token[0] != '{'))
throw config_reader_error(line_number);
if (token[0] == '=')
{
// we should parse the value out now
// first discard any white space
if (tok.peek_type() == tokenizer::WHITE_SPACE)
tok.get_token(type,token);
std::string value;
type = tok.peek_type();
token = tok.peek_token();
while (true)
{
if (type == tokenizer::END_OF_FILE || type == tokenizer::END_OF_LINE)
break;
if (type == tokenizer::CHAR && token[0] == '\\')
{
tok.get_token(type,token);
if (tok.peek_type() == tokenizer::CHAR &&
tok.peek_token()[0] == '#')
{
tok.get_token(type,token);
value += '#';
}
else if (tok.peek_type() == tokenizer::CHAR &&
tok.peek_token()[0] == '}')
{
tok.get_token(type,token);
value += '}';
}
else
{
value += '\\';
}
}
else if (type == tokenizer::CHAR &&
(token[0] == '#' || token[0] == '}'))
{
break;
}
else
{
value += token;
tok.get_token(type,token);
}
type = tok.peek_type();
token = tok.peek_token();
} // while(true)
// strip of any tailing white space from value
std::string::size_type pos = value.find_last_not_of(" \t\r\n");
if (pos == std::string::npos)
value.clear();
else
value.erase(pos+1);
// make sure this key isn't already in the key_table
if (cr.key_table.is_in_domain(identifier))
throw config_reader_error(line_number,true);
// add this key/value pair to the key_table
cr.key_table.add(identifier,value);
}
else // when token[0] == '{'
{
// make sure this identifier isn't already in the block_table
if (cr.block_table.is_in_domain(identifier))
throw config_reader_error(line_number,true);
config_reader_kernel_1* new_cr = new config_reader_kernel_1;
void* vtemp = new_cr;
try { cr.block_table.add(identifier,vtemp); }
catch (...) { delete new_cr; throw; }
// now parse this block
parse_config_file(*new_cr,tok,line_number,false);
}
}
else
{
// the next thing should be an identifier but if it isn't this is an error
if (type != tokenizer::IDENTIFIER)
throw config_reader_error(line_number);
seen_identifier = true;
identifier = token;
}
} // while (true)
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
~config_reader_kernel_1(
)
{
clear();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
bool config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
is_key_defined (
const std::string& key
) const
{
return key_table.is_in_domain(key);
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
bool config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
is_block_defined (
const std::string& name
) const
{
return block_table.is_in_domain(name);
}
// ----------------------------------------------------------------------------------------
template <
typename mss,
typename msv,
typename tokenizer,
bool checking
>
const config_reader_kernel_1<mss,msv,tokenizer,checking>& config_reader_kernel_1<mss,msv,tokenizer,checking>::
block (
const std::string& name
) const
{
config_reader_kernel_1_helpers::helper<config_reader_kernel_1,checking>::
check_block_precondition(*this,name);
return *reinterpret_cast<config_reader_kernel_1*>(block_table[name]);
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
const std::string& config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
operator[] (
const std::string& key
) const
{
config_reader_kernel_1_helpers::helper<config_reader_kernel_1,checking>::
check_operator_bracket_precondition(*this,key);
return key_table[key];
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
template <
typename queue_of_strings
>
void config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
get_keys (
queue_of_strings& keys
) const
{
keys.clear();
key_table.reset();
std::string temp;
while (key_table.move_next())
{
temp = key_table.element().key();
keys.enqueue(temp);
}
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
bool config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
at_start (
) const
{
return block_table.at_start();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
void config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
reset (
) const
{
block_table.reset();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
bool config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
current_element_valid (
) const
{
return block_table.current_element_valid();
}
// ----------------------------------------------------------------------------------------
template <
typename mss,
typename msv,
typename tokenizer,
bool checking
>
const config_reader_kernel_1<mss,msv,tokenizer,checking>& config_reader_kernel_1<mss,msv,tokenizer,checking>::
element (
) const
{
config_reader_kernel_1_helpers::helper<config_reader_kernel_1,checking>::
check_element_precondition(*this);
return *reinterpret_cast<config_reader_kernel_1*>(block_table.element().value());
}
// ----------------------------------------------------------------------------------------
template <
typename mss,
typename msv,
typename tokenizer,
bool checking
>
config_reader_kernel_1<mss,msv,tokenizer,checking>& config_reader_kernel_1<mss,msv,tokenizer,checking>::
element (
)
{
config_reader_kernel_1_helpers::helper<config_reader_kernel_1,checking>::
check_element_precondition(*this);
return *reinterpret_cast<config_reader_kernel_1*>(block_table.element().value());
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
bool config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
move_next (
) const
{
return block_table.move_next();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
unsigned long config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
size (
) const
{
return block_table.size();
}
// ----------------------------------------------------------------------------------------
template <
typename map_string_string,
typename map_string_void,
typename tokenizer,
bool checking
>
const std::string& config_reader_kernel_1<map_string_string,map_string_void,tokenizer,checking>::
current_block_name (
) const
{
config_reader_kernel_1_helpers::helper<config_reader_kernel_1,checking>::
check_current_block_name_precondition(*this);
return block_table.element().key();
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_CONFIG_READER_KERNEl_1_