// // C++ Implementation: user_info.cpp // // Description: UI (user info) class for loading/saving objects from/to configuration files. // // // Author: smidl , (C) 2009 // // Copyright: See COPYING file that comes with this distribution // // #include "user_info.h" namespace bdm { string UIException::format_message ( const string &reason, const string &path ) { stringstream ss; ss << reason; ss << " Check path \"" << path << "\"."; return ss.str(); } ///////////////////////////// Class UIFile ///////////////////////////////////////////// UIFile::UIFile() { } UIFile::UIFile ( const string &file_name ) { try { readFile ( file_name.c_str() ); // this flag has to be set AFTER each file load, that is why it is right here setAutoConvert ( true ); } catch ( FileIOException f ) { string msg = "UI error: file "; msg += file_name; msg += " not found."; bdm_error ( msg ); } catch ( ParseException& P ) { stringstream msg; msg << "UI error: parsing error """ << P.getError() << """ in file " << file_name << " on line " << P.getLine() << "."; bdm_error ( msg.str() ); } } void UIFile::save ( const string &file_name ) { try { writeFile ( file_name.c_str() ); } catch ( FileIOException f ) { string msg = "UI error: file "; msg += file_name; msg += " is inacessible."; bdm_error ( msg ); } } UIFile::operator Setting&() { return getRoot(); } ///////////////////////////// Class UI::MappedUI ///////////////////////////////////////////// UI::MappedUI::StringToUIMap& UI::MappedUI::mapped_strings() { // this way it is ensured that there is only one instance of StringTpUIMap, and // what is more, this declaration leaves its allocation/deallocation on the compiler static StringToUIMap var; return var; } UI::MappedUI::TypeInfoToStringMap& UI::MappedUI::mapped_type_infos() { // this way it is ensured that there is only one instance of TypeInfoToStringMap, and // what is more, this declaration leaves its allocation/deallocation on the compiler static TypeInfoToStringMap var; return var; } void UI::MappedUI::add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui ) { pair< StringToUIMap::iterator, bool> inres = mapped_strings().insert ( StringToUIMap::value_type ( class_name, ui ) ); if ( inres.second ) { mapped_type_infos().insert ( TypeInfoToStringMap::value_type ( class_type_info, class_name ) ); } } void UI::MappedUI::unregistered_class_error ( const string &unregistered_class_name ) { stringstream msg; msg << "UI error: class " + unregistered_class_name + " was not properly registered. Use the macro ""UIREGISTER([class name]);"" within your code." << endl; if ( mapped_strings().size() ) { StringToUIMap::const_iterator iter = mapped_strings().begin(); msg << "These classes are already registered: " << iter->first; for ( iter++; iter != mapped_strings().end(); iter++ ) msg << ", " << iter->first; msg << "." << endl; } else msg << "There is not any registered class yet!" << endl; bdm_error ( msg.str() ); } const UI& UI::MappedUI::retrieve_ui ( const string &class_name ) { StringToUIMap::const_iterator iter = mapped_strings().find ( class_name ); if ( iter == mapped_strings().end() ) unregistered_class_error ( class_name ); return *iter->second; } const string& UI::MappedUI::retrieve_class_name ( const type_info * const class_type_info ) { TypeInfoToStringMap::const_iterator iter = mapped_type_infos().find ( class_type_info ); if ( iter == mapped_type_infos().end() ) unregistered_class_error ( "with RTTI name " + string ( class_type_info->name() ) ); return iter->second; } ///////////////////////////// Class SettingResolver ///////////////////////////////////////////// SettingResolver::SettingResolver ( const Setting &potential_link ) : result ( initialize_reference ( file, potential_link ) ) { } const Setting& SettingResolver::initialize_reference ( UIFile *&file, const Setting &potential_link ) { file = NULL; if ( potential_link.getType() != Setting::TypeString ) return potential_link; string link = ( const char* ) potential_link; size_t aerobase = link.find ( '@' ); const Setting *result; if ( aerobase != string::npos ) { string file_name = link.substr ( aerobase + 1, link.length() ); file = new UIFile ( file_name ); result = & ( Setting& ) ( *file ); link = link.substr ( 0, aerobase ); } else { result = &potential_link; while ( !result->isRoot() ) result = &result->getParent(); } if ( !result->exists ( link ) ) throw UISettingException ( "UIException: linked setting was not found.", string ( ( const char* ) potential_link ) ); return ( *result ) [link]; } SettingResolver::~SettingResolver() { if ( file ) delete file; } ///////////////////////////// Class UI ///////////////////////////////////////////// void UI::assert_type ( const Setting &element, Setting::Type type ) { if ( element.getType() != type ) throw UISettingException ( "UIException: wrong setting type.", element ); } const Setting& UI::to_child_setting ( const Setting &element, const int index ) { if ( !element.isList() ) throw UISettingException ( "UIException: only TypeList elements could be indexed by integers.", element ); return element[index]; } const Setting& UI::to_child_setting ( const Setting &element, const string &name ) { if ( !element.isGroup() ) throw UISettingException ( "UIException: only TypeGroup elements could be indexed by strings.", element ); return element[name]; } void UI::call_to_setting( const root &instance, Setting &set, string class_name ) { try { instance.to_setting ( set ); } catch ( SettingException &sttng_xcptn ) { string msg = "UIException: method "; msg += class_name; msg += ".to_setting(Setting&) has thrown a SettingException."; throw UISettingException ( msg, sttng_xcptn.getPath() ); } } void UI::save ( const root &instance, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element : element.add ( name, Setting::TypeGroup ); call_to_setting( instance, set ); } void UI::save ( const log_level_base &log_level, Setting &element ) { assert_type ( element, Setting::TypeGroup ); string name = "log_level"; if( element.exists( name ) ) assert_type ( element[name], Setting::TypeString ); else element.add ( name, Setting::TypeString ); call_to_setting( log_level, element[name] ); } void UI::save ( const root * const instance, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element : element.add ( name, Setting::TypeGroup ); // add attribute "class" const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) ); Setting &type = set.add ( "class", Setting::TypeString ); type = class_name; call_to_setting( *instance, set, class_name ); } void UI::save ( const int &integer, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeInt ) : element.add ( name, Setting::TypeInt ); set = integer; } void UI::save ( const double &real, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeFloat ) : element.add ( name, Setting::TypeFloat ); set = real; } void UI::save ( const string &str, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeString ) : element.add ( name, Setting::TypeString ); set = str; } void UI::save ( const mat &matrix, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeList ) : element.add ( name, Setting::TypeList ); Setting &tag = set.add ( Setting::TypeString ); tag = "matrix"; Setting &rows = set.add ( Setting::TypeInt ); rows = matrix.rows(); Setting &cols = set.add ( Setting::TypeInt ); cols = matrix.cols(); Setting &elements = set.add ( Setting::TypeArray ); // build matrix row-wise for ( int i = 0; i < matrix.rows(); i++ ) for ( int j = 0; j < matrix.cols(); j++ ) { Setting &new_field = elements.add ( Setting::TypeFloat ); new_field = matrix ( i, j ); } } void UI::save ( const ivec &vector, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray ) : element.add ( name, Setting::TypeArray ); for ( int i = 0; i < vector.length(); i++ ) { Setting &new_field = set.add ( Setting::TypeInt ); new_field = vector ( i ); } } void UI::save ( const vec &vector, Setting &element, const string &name ) { Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray ) : element.add ( name, Setting::TypeArray ); for ( int i = 0; i < vector.length(); i++ ) { Setting &new_field = set.add ( Setting::TypeFloat ); new_field = vector ( i ); } } void UI::call_from_setting( root &instance, const Setting &set, string class_name) { try { instance.from_setting ( set ); } catch ( SettingException &sttng_xcptn ) { string msg = "UIException: method "; msg += class_name; msg += ".from_setting(Setting&) has thrown a SettingException."; throw UISettingException ( msg, sttng_xcptn.getPath() ); } catch ( std::runtime_error &e ) { string msg = "UIException: method "; msg += class_name; msg += " says: "; msg += e.what(); throw UISettingException ( msg, set ); } // validate the new instance instance.validate(); } void UI::from_setting ( log_level_base &log_level, const Setting &element ) { assert_type( element, Setting::TypeString ); call_from_setting( log_level, element ); } void UI::from_setting ( root &instance, const Setting &element ) { const SettingResolver link ( element ); assert_type( link.result, Setting::TypeGroup ); call_from_setting( instance, link.result); } void UI::from_setting ( mat& matrix, const Setting &element ) { const SettingResolver link ( element ); if ( link.result.isNumber() ) { matrix.set_size ( 1, 1 ); matrix ( 0, 0 ) = link.result; return; } if ( link.result.isList() ) { int data_offset; if ( link.result.getLength() == 3 ) data_offset = 0; else if ( link.result.getLength() == 4 ) { assert_type ( link.result[0], Setting::TypeString ); const char* elem1 = ( const char* ) link.result[0]; if ( ( strcmp ( elem1, "matrix" ) ) ) throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result ); data_offset = 1; } else throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result ); Setting &rows_setting = link.result[0 + data_offset]; Setting &cols_setting = link.result[1 + data_offset]; Setting &elements = link.result[2 + data_offset]; // vvv ----- not working in matlab!! //assert_type ( cols_setting, Setting::TypeInt ); //assert_type ( rows_setting, Setting::TypeInt ); //assert_type ( elements, Setting::TypeArray ); int cols = cols_setting; int rows = rows_setting; int elems = elements.getLength(); if ( cols < 0 || rows < 0 ) throw UISettingException ( "UIException: the dimensions of a matrix has to be non-negative.", link.result ); if ( elems != cols * rows ) throw UISettingException ( "UIException: the count of the matrix elements is incompatible with matrix dimension.", elements ); matrix.set_size ( rows, cols ); if ( cols == 0 || rows == 0 ) return; if ( !elements[0].isNumber() ) throw UISettingException ( "UIException: matrix elements have to be numbers.", elements[0] ); // build matrix row-wise int k = 0; for ( int i = 0; i < rows; i++ ) for ( int j = 0; j < cols; j++ ) matrix ( i, j ) = elements[k++]; return; } throw UISettingException ( "UIException: only numeric types or TypeList are supported as matrix values.", link.result ); } void UI::from_setting ( vec &vector, const Setting &element ) { const SettingResolver link ( element ); if ( link.result.isNumber() ) { vector.set_length ( 1 ); vector ( 0 ) = link.result; return; } if ( link.result.isList() ) { mat matrix; from_setting ( matrix, link.result ); if ( matrix.cols() != 1 && matrix.rows() != 1 && matrix.cols() != 0 ) throw UISettingException ( "UIException: the vector length is invalid, it seems to be rather a matrix.", link.result ); int len = matrix.rows() * matrix.cols(); vector.set_length ( len ); if ( len == 0 ) return; if ( matrix.cols() == 1 ) for ( int i = 0; i < len; i++ ) vector ( i ) = matrix ( i, 0 ); else for ( int i = 0; i < len; i++ ) vector ( i ) = matrix ( 0, i ); return; } if ( link.result.isArray() ) { int len = link.result.getLength(); vector.set_length ( len ); if ( len == 0 ) return; if ( !link.result[0].isNumber() ) throw UISettingException ( "UIException: a vector element has to be a number.", link.result[0] ); for ( int i = 0; i < len; i++ ) vector ( i ) = link.result[i]; return; } throw UISettingException ( "UIException: only numeric types, TypeArray or TypeList are supported as vector values.", link.result ); } void UI::from_setting ( ivec &vector, const Setting &element ) { vec double_vector; from_setting ( double_vector, element ); int len = double_vector.length(); vector.set_length ( len ); for ( int i = 0; i < len; i++ ) vector ( i ) = ( int ) double_vector ( i ); } void UI::from_setting ( string &str, const Setting &element ) { assert_type ( element, Setting::TypeString ); str = ( const char* ) element; } void UI::from_setting ( int &integer, const Setting &element ) { // assert_type ( element, Setting::TypeInt ); integer = element; } void UI::from_setting ( double &real, const Setting &element ) { // assert_type ( element, Setting::TypeFloat ); real = element; } }//namespace