root/library/bdm/base/user_info.cpp @ 942

Revision 942, 13.3 kB (checked in by mido, 14 years ago)

the functionality of user info was improved, it supports an initialization of root descendant via UI::get directly, however it is save only for static attributes, for dynamically allocated attributes UI::build should be called to handle with intahrence issues

  • Property svn:eol-style set to native
Line 
1//
2// C++ Implementation: user_info.cpp
3//
4// Description: UI (user info) class for loading/saving objects from/to configuration files.
5//
6//
7// Author: smidl <smidl@utia.cas.cz>, (C) 2009
8//
9// Copyright: See COPYING file that comes with this distribution
10//
11//
12
13#include "user_info.h"
14
15namespace bdm {
16
17string UIException::format_message ( const string &reason, const string &path ) {
18        stringstream ss;
19        ss << reason;
20        ss << " Check path \"" << path << "\".";
21        return ss.str();
22}
23
24///////////////////////////// Class UIFile /////////////////////////////////////////////
25
26UIFile::UIFile() {
27}
28
29UIFile::UIFile ( const string &file_name ) {
30        try {
31                readFile ( file_name.c_str() );
32                // this flag has to be set AFTER each file load, that is why it is right here
33                setAutoConvert ( true );
34        } catch ( FileIOException f ) {
35                string msg = "UI error: file ";
36                msg += file_name;
37                msg += " not found.";
38                bdm_error ( msg );
39        } catch ( ParseException& P ) {
40                stringstream msg;
41                msg << "UI error: parsing error """ << P.getError() << """ in file " << file_name << " on line " <<  P.getLine() << ".";
42                bdm_error ( msg.str() );
43        }
44}
45
46
47void UIFile::save ( const string &file_name ) {
48        try {
49                writeFile ( file_name.c_str() );
50        } catch ( FileIOException f ) {
51                string msg = "UI error: file ";
52                msg += file_name;
53                msg += " is inacessible.";
54                bdm_error ( msg );
55        }
56}
57
58UIFile::operator Setting&() {
59        return getRoot();
60}
61
62///////////////////////////// Class UI::MappedUI /////////////////////////////////////////////
63
64UI::MappedUI::StringToUIMap& UI::MappedUI::mapped_strings() {
65        // this way it is ensured that there is only one instance of StringTpUIMap, and
66        // what is more, this declaration leaves its allocation/deallocation on the compiler
67        static StringToUIMap var;
68        return var;
69}
70
71UI::MappedUI::TypeInfoToStringMap& UI::MappedUI::mapped_type_infos() {
72        // this way it is ensured that there is only one instance of TypeInfoToStringMap, and
73        // what is more, this declaration leaves its allocation/deallocation on the compiler
74        static TypeInfoToStringMap var;
75        return var;
76}
77
78void UI::MappedUI::add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui ) {
79        pair< StringToUIMap::iterator, bool> inres =
80            mapped_strings().insert (
81                StringToUIMap::value_type ( class_name, ui ) );
82        if ( inres.second ) {
83                mapped_type_infos().insert (
84                    TypeInfoToStringMap::value_type (
85                        class_type_info, class_name ) );
86        }
87}
88
89void UI::MappedUI::unregistered_class_error ( const string &unregistered_class_name ) {
90        stringstream msg;
91        msg << "UI error: class " + unregistered_class_name + " was not properly registered. Use the macro ""UIREGISTER([class name]);"" within your code." << endl;
92
93        if ( mapped_strings().size() ) {
94                StringToUIMap::const_iterator iter = mapped_strings().begin();
95                msg << "These classes are already registered: " << iter->first;
96                for ( iter++; iter != mapped_strings().end(); iter++ )
97                        msg << ", " << iter->first;
98                msg << "." << endl;
99        } else
100                msg << "There is not any registered class yet!" << endl;
101
102        bdm_error ( msg.str() );
103}
104
105const UI& UI::MappedUI::retrieve_ui ( const string &class_name ) {
106        StringToUIMap::const_iterator iter = mapped_strings().find ( class_name );
107        if ( iter == mapped_strings().end() )
108                unregistered_class_error ( class_name );
109
110        return *iter->second;
111}
112
113const string& UI::MappedUI::retrieve_class_name ( const type_info * const class_type_info ) {
114        TypeInfoToStringMap::const_iterator iter = mapped_type_infos().find ( class_type_info );
115        if ( iter == mapped_type_infos().end() )
116                unregistered_class_error ( "with RTTI name " + string ( class_type_info->name() ) );
117        return iter->second;
118}
119
120///////////////////////////// Class SettingResolver /////////////////////////////////////////////
121
122SettingResolver::SettingResolver ( const Setting &potential_link )
123                : result ( initialize_reference ( file, potential_link ) ) {
124}
125
126const Setting& SettingResolver::initialize_reference ( UIFile *&file, const Setting &potential_link ) {
127        file = NULL;
128
129        if ( potential_link.getType() !=  Setting::TypeString )
130                return potential_link;
131
132        string link = ( const char* ) potential_link;
133        size_t aerobase = link.find ( '@' );
134
135        const Setting *result;
136        if ( aerobase != string::npos ) {
137                string file_name = link.substr ( aerobase + 1, link.length() );
138                file = new UIFile ( file_name );
139                result = & ( Setting& ) ( *file );
140                link = link.substr ( 0, aerobase );
141        } else {
142                result = &potential_link;
143                while ( !result->isRoot() )
144                        result = &result->getParent();
145        }
146
147        if ( !result->exists ( link ) )
148                throw UISettingException ( "UIException: linked setting was not found.", string ( ( const char* ) potential_link ) );
149
150        return ( *result ) [link];
151}
152
153SettingResolver::~SettingResolver() {
154        if ( file ) delete file;
155}
156
157///////////////////////////// Class UI /////////////////////////////////////////////
158
159void UI::assert_type ( const Setting &element, Setting::Type type ) {
160        if ( element.getType() != type )
161                throw UISettingException ( "UIException: wrong setting type.", element );
162}
163
164const Setting& UI::to_child_setting ( const Setting &element, const int index ) {
165        if ( !element.isList() )
166                throw UISettingException ( "UIException: only TypeList elements could be indexed by integers.", element );
167
168        return element[index];
169}
170
171const Setting& UI::to_child_setting ( const Setting &element, const string &name ) {
172        if ( !element.isGroup() )
173                throw UISettingException ( "UIException: only TypeGroup elements could be indexed by strings.", element );
174
175        return element[name];
176}
177
178void UI::call_to_setting( const root &instance, Setting &set, string class_name ) {
179        try {
180                instance.to_setting ( set );
181        } catch ( SettingException &sttng_xcptn ) {
182                string msg = "UIException: method ";
183                msg += class_name;
184                msg += ".to_setting(Setting&) has thrown a SettingException.";
185                throw UISettingException ( msg, sttng_xcptn.getPath() );
186        }
187}
188
189void UI::save ( const root &instance, Setting &element, const string &name ) {
190        Setting &set = ( name == "" ) ?  (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element
191                            : element.add ( name, Setting::TypeGroup );
192
193        call_to_setting( instance, set );
194}
195
196void UI::save ( const root * const instance, Setting &element, const string &name ) {
197        Setting &set = ( name == "" ) ?  (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element
198                            : element.add ( name, Setting::TypeGroup );
199
200        // add attribute "class"
201        const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) );
202        Setting &type = set.add ( "class", Setting::TypeString );
203        type = class_name;
204
205        call_to_setting( *instance, set, class_name );
206}
207
208void UI::save ( const int &integer, Setting &element, const string &name ) {
209        Setting &set = ( name == "" ) ? element.add ( Setting::TypeInt )
210                       : element.add ( name, Setting::TypeInt );
211        set = integer;
212}
213
214void UI::save ( const double &real, Setting &element, const string &name ) {
215        Setting &set = ( name == "" ) ? element.add ( Setting::TypeFloat )
216                       : element.add ( name, Setting::TypeFloat );
217        set = real;
218}
219
220void UI::save ( const string &str, Setting &element, const string &name ) {
221        Setting &set = ( name == "" ) ? element.add ( Setting::TypeString )
222                       : element.add ( name, Setting::TypeString );
223        set = str;
224}
225
226void UI::save ( const mat &matrix, Setting &element, const string &name ) {
227        Setting &set = ( name == "" ) ? element.add ( Setting::TypeList )
228                       : element.add ( name, Setting::TypeList );
229
230        Setting &tag = set.add ( Setting::TypeString );
231        tag = "matrix";
232
233        Setting &rows = set.add ( Setting::TypeInt );
234        rows = matrix.rows();
235
236        Setting &cols = set.add ( Setting::TypeInt );
237        cols = matrix.cols();
238       
239        Setting &elements = set.add ( Setting::TypeArray );
240
241        // build matrix row-wise
242        for ( int i = 0; i < matrix.rows(); i++ )
243                for ( int j = 0; j < matrix.cols(); j++ ) {
244                        Setting &new_field = elements.add ( Setting::TypeFloat );
245                        new_field = matrix ( i, j );
246                }
247}
248
249void UI::save ( const ivec &vector, Setting &element, const string &name ) {
250        Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray )
251                       : element.add ( name, Setting::TypeArray );
252        for ( int i = 0; i < vector.length(); i++ ) {
253                Setting &new_field = set.add ( Setting::TypeInt );
254                new_field = vector ( i );
255        }
256}
257
258void UI::save ( const vec &vector, Setting &element, const string &name ) {
259        Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray )
260                       : element.add ( name, Setting::TypeArray );
261        for ( int i = 0; i < vector.length(); i++ ) {
262                Setting &new_field = set.add ( Setting::TypeFloat );
263                new_field = vector ( i );
264        }
265}
266
267
268void UI::call_from_setting( root &instance, const Setting &set, string class_name) {
269        try {
270                instance.from_setting ( set );
271        } catch ( SettingException &sttng_xcptn ) {
272                string msg = "UIException: method ";
273                msg += class_name;
274                msg += ".from_setting(Setting&) has thrown a SettingException.";
275                throw UISettingException ( msg, sttng_xcptn.getPath() );
276        } catch ( std::runtime_error &e ) {
277                string msg = "UIException: method ";
278                msg += class_name;
279                msg += " says: ";
280                msg += e.what();
281                throw UISettingException ( msg, set );
282        } 
283
284        // validate the new instance
285        instance.validate();   
286}
287
288void UI::from_setting ( root &instance, const Setting &element ) {
289        const SettingResolver link ( element );
290        assert_type( link.result, Setting::TypeGroup );
291        call_from_setting( instance, link.result );
292}
293
294void UI::from_setting ( mat& matrix, const Setting &element ) {
295        const SettingResolver link ( element );
296
297        if ( link.result.isNumber() ) {
298                matrix.set_size ( 1, 1 );
299                matrix ( 0, 0 ) = link.result;
300                return;
301        }
302
303        if ( link.result.isList() ) {
304                int data_offset;
305
306                if ( link.result.getLength() == 3 )
307                        data_offset = 0;
308                else if ( link.result.getLength() == 4 ) {
309                        assert_type ( link.result[0], Setting::TypeString );
310                        const char* elem1 = ( const char* ) link.result[0];
311                        if ( ( strcmp ( elem1, "matrix" ) ) )
312                                throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result );
313
314                        data_offset = 1;
315                } else
316                        throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result );
317
318                Setting &rows_setting = link.result[0 + data_offset];
319                Setting &cols_setting = link.result[1 + data_offset];
320                Setting &elements = link.result[2 + data_offset];
321
322                // vvv ----- not working in matlab!!
323                //assert_type ( cols_setting, Setting::TypeInt );
324                //assert_type ( rows_setting, Setting::TypeInt );
325                //assert_type ( elements, Setting::TypeArray );
326
327                int cols = cols_setting;
328                int rows = rows_setting;
329                int elems = elements.getLength();
330
331                if ( cols < 0 || rows < 0 )
332                        throw UISettingException ( "UIException: the dimensions of a matrix has to be non-negative.", link.result );
333
334                if ( elems != cols * rows )
335                        throw UISettingException ( "UIException: the count of the matrix elements is incompatible with matrix dimension.", elements );
336
337                matrix.set_size ( rows, cols );
338
339                if ( cols == 0 || rows == 0 )
340                        return;
341
342                if ( !elements[0].isNumber() )
343                        throw UISettingException ( "UIException: matrix elements have to be numbers.", elements[0] );
344
345                // build matrix row-wise
346                int k = 0;
347                for ( int i = 0; i < rows; i++ )
348                        for ( int j = 0; j < cols; j++ )
349                                matrix ( i, j ) = elements[k++];
350                return;
351        }
352
353        throw UISettingException ( "UIException: only numeric types or TypeList are supported as matrix values.", link.result );
354}
355
356void UI::from_setting ( vec &vector, const Setting &element ) {
357        const SettingResolver link ( element );
358
359        if ( link.result.isNumber() ) {
360                vector.set_length ( 1 );
361                vector ( 0 ) = link.result;
362                return;
363        }
364
365        if ( link.result.isList() ) {
366                mat matrix;
367                from_setting ( matrix, link.result );
368
369                if ( matrix.cols() != 1 && matrix.rows() != 1 && matrix.cols() != 0 )
370                        throw UISettingException ( "UIException: the vector length is invalid, it seems to be rather a matrix.", link.result );
371
372                int len = matrix.rows() * matrix.cols();
373                vector.set_length ( len );
374                if ( len == 0 ) return;
375
376                if ( matrix.cols() == 1 )
377                        for ( int i = 0; i < len; i++ )
378                                vector ( i ) = matrix ( i, 0 );
379                else
380                        for ( int i = 0; i < len; i++ )
381                                vector ( i ) = matrix ( 0, i );
382                return;
383        }
384
385        if ( link.result.isArray() ) {
386                int len = link.result.getLength();
387                vector.set_length ( len );
388                if ( len == 0 ) return;
389
390                if ( !link.result[0].isNumber() )
391                        throw UISettingException ( "UIException: a vector element has to be a number.", link.result[0] );
392
393                for ( int i = 0; i < len; i++ )
394                        vector ( i ) = link.result[i];
395
396                return;
397        }
398
399        throw UISettingException ( "UIException: only numeric types, TypeArray or TypeList are supported as vector values.", link.result );
400}
401
402void UI::from_setting ( ivec &vector, const Setting &element ) {
403        vec double_vector;
404        from_setting ( double_vector, element );
405        int len = double_vector.length();
406        vector.set_length ( len );
407        for ( int i = 0; i < len; i++ )
408                vector ( i ) = ( int ) double_vector ( i );
409}
410
411void UI::from_setting ( string &str, const Setting &element ) {
412        assert_type ( element, Setting::TypeString );
413        str = ( const char* ) element;
414}
415
416void UI::from_setting ( int &integer, const Setting &element ) {
417//      assert_type ( element, Setting::TypeInt );
418        integer = element;
419}
420
421void UI::from_setting ( double &real, const Setting &element ) {
422//      assert_type ( element, Setting::TypeFloat );
423        real = element;
424}
425
426}//namespace
Note: See TracBrowser for help on using the browser.