// 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_