root/bdm/user_info.h @ 345

Revision 345, 17.1 kB (checked in by mido, 15 years ago)

dodelano user_info, castecne predelano podle stabni kultury, zbyva otestovat a udelat dokumentaci

  • Property svn:eol-style set to native
Line 
1#ifndef UIBUILD
2#define UIBUILD
3
4#include <itpp/itbase.h>
5#include "stat/libBM.h"
6#include "libconfig/libconfig.h++"
7
8#include <sstream>
9#include <iostream>
10#include <stdio.h>
11#include <string>
12#include <typeinfo>
13#include <map>
14#include <utility>
15#include <vector>
16#include <iostream>
17
18using std::string;
19using namespace std;
20using namespace libconfig;
21
22#define UIREGISTER(class_name) template<> Particular_UI<class_name>& Particular_UI<class_name>::ui = Particular_UI<class_name>(#class_name)
23
24#define ASSERT_UITYPE(S,Type) it_assert_debug(S.getType()==Setting::Type, string("Wrong setting type, see input path \"")+string(S.getPath())+string("\""))
25
26namespace bdm
27{
28
29/*!
30@brief This class serves to load and/or save DOMElements into/from files
31stored on a hard-disk.
32
33Firstly, you associate new RootElement instance with some filename during a time of its
34construtcion. Then, you save some object into the new RootElement instance,
35and save it into the file this way:
36\code
37        CAudi audi;
38        RootElement root("cars.xml");
39        UserInfo::save( audi, root, "TT");
40        root.save();
41\endcode
42
43In the other way round, when loading object from a XML file, the appropriate code looks like this:
44\code
45        RootElement root("cars.xml");
46        root.load();
47        UserInfo::build<T>(root,"TT");
48\endcode
49*/
50
51class UI_File : public Config
52{
53private:
54        const string file_name;
55
56public:
57        //! attach new RootElement instance to a file (typically with an XML extension)
58        UI_File( const string &file_name );
59
60        //! loads root element from a file
61        void load();
62
63        //! save UserInfo to the file (typically with an XML extension)
64        void save();
65
66        operator Setting&();
67};
68
69
70/*!
71@brief UserInfo is an abstract is for internal purposes only. Use CompoundUserInfo<T> or Particular_UI<T> instead.
72The raison d'etre of this class is to allow pointers to its templated descendants.
73
74Also, the main functions of the whole UserInfo library are included within this class, see
75static methods 'build' and 'save'.
76
77
78/*!\brief Builds computational object from a UserInfo structure
79
80Return value is a pointer to the created object (memory management issue?)
81/
82
83*/
84class UI
85{
86private:
87        //! static class encalupsating map of names to related UserInfos
88        //!
89        //! The key property of this class is that it initilaized the internal map immediately
90        //! when it is used for a first time.
91        class Class_To_UI
92        {
93        private:
94                //! Type definition of mapping which transforms type names to the related user infors
95                typedef map< const string, UI* > Class_To_UI_Map;
96
97                //! immediately initialized instance of type Class_To_UI_Map
98                static Class_To_UI_Map& private_map();
99
100        public:
101                //! add a pair key-userinfo into the internal map
102                static void add_class( const string &class_name, UI* ui );
103
104                //! search for an userinfo related to the passed key within the internal map
105                static UI* retrieve_ui( const string &class_name );
106        };
107                               
108        //! internal method assembling a typeless instance from components obtained by the 'AssemblyComponentsFromSetting()' method
109        virtual bdmroot* new_instance() = 0;
110       
111        //! type name defined by user
112        const string class_name;
113
114        //! This methods tries to save an instance of type T (or some of its descendant types)
115        //! and build DOM tree accordingly. Then, it creates a new DOMNode named according class_name
116        //! and connecti it to the passed Setting as a new child node.
117        template<class T> static void to_setting( const T &instance, Setting &root )
118        {
119                const string &class_name = Particular_UI<T>::ui.class_name;
120                       
121                // add attribute "class"
122                Setting &type = root.add( "class", Setting::TypeString );
123                type = class_name;
124       
125                try
126                {
127                        // instance disassembling
128                        instance.to_setting( root );
129                }
130                catch(SettingException xcptn)
131                {
132                        it_error ( "UI error: the method " + class_name + ".to_setting(Setting&) has thrown an exception when filling the setting " + xcptn.getPath() + ". Try to correct this method." );
133                }       
134        }
135
136        template<class T> static T* from_setting( const Setting &element )
137        {                       
138                const Link_Expander link_expander( element );
139                const Setting &root = link_expander.root();
140
141                ASSERT_UITYPE(root,TypeGroup);
142
143                // we get a velue stored in the "class" attribute
144                string class_name;
145                if( !root.lookupValue( "class", class_name ) )
146                        ui_error( "the obligatory ""class"" identifier is missing", root );
147       
148                // and finally we find a UserInfo related to this type
149                UI* related_UI = Class_To_UI::retrieve_ui( class_name );
150                if( !related_UI)
151                        it_error ( "UI error: class " + class_name + " was not properly registered. Use the macro ""UIREGISTER([class name]);"" within your code." );
152               
153                bdmroot* typeless_instance = related_UI->new_instance();
154
155                T* ui = NULL;
156                try
157                {
158                        ui = (T*) typeless_instance ;
159                }
160                catch(...)
161                {
162                        it_error ( "UI error: class " + class_name + " is not a descendant of the desired output class. Try to call the UI::build function with a different type parameter." );
163                }
164               
165                try
166                {
167                        // instance assembling
168                        ui->from_setting( root );
169                }
170                catch(SettingException xcptn)
171                {
172                        it_error ( "UI error: the method " + class_name + ".from_setting(Setting&) has thrown an exception when parsing the setting " + xcptn.getPath() + ". Try to correct this method." );
173                }
174                return ui;
175        }       
176
177
178        // vraci true, kdyz to byl platny link, jinak false.. v pripade chyby konci it_errorem..
179        // do elementu vrati setting prislusny po rozbaleni linku, jinak ponecha beze zmeny
180        class Link_Expander     
181        {
182        private:
183                UI_File *file;
184                const Setting *result;
185
186        public:
187
188                Link_Expander( const Setting &potential_link )
189                {
190                        file = NULL;
191                        result = &potential_link;
192
193                        if( potential_link.getType() !=  Setting::TypeString )
194                                return;
195
196                        string link = (string) potential_link;
197                        size_t aerobase = link.find('@');
198                        if( aerobase != string::npos )
199                        {
200                                string file_name = link.substr( aerobase + 1, link.length() );
201                                file = new UI_File( file_name );
202                                file->load();
203                                result = &(Setting&)(*file);
204                                link = link.substr( 0, aerobase );
205                        }
206                        else
207                                while ( !result->isRoot() ) 
208                                        result = &result->getParent();
209
210                        if( !result->exists( link ) )
211                                ui_error( "linked Setting was not found", potential_link );
212
213                        result = &(*result)[link];
214                }
215
216                ~Link_Expander()
217                {
218                        if( file ) delete file;
219                }
220               
221                const Setting& root() const
222                {
223                        return *result;
224                }
225        };
226
227                static const Setting* to_child_setting( const Setting &element, const int index )
228                {
229                        if( !element.isAggregate())
230                                return NULL;
231
232                        if( element.getLength() <= index )
233                                return NULL;
234
235                        return &element[index];
236                }
237
238                static const Setting* to_child_setting( const Setting &element, const string &name )
239                {
240                        if( !element.isGroup() )
241                                return NULL;
242
243                        if( !element.exists( name ) )
244                                return NULL;
245
246                        return &element[name];
247                }
248
249                static Setting& to_child_setting( Setting &element, const int index )
250                {
251                        if( !element.isAggregate())
252                                ui_error( "it is not possible to index non-agregate element by integers", element );
253
254                        if( element.getLength() <= index )
255                                ui_error( "there is not any child with index " + index, element );
256
257                        return element[index];
258                }
259
260                static Setting& to_child_setting( Setting &element, const string &name )
261                {
262                        ASSERT_UITYPE(element,TypeGroup);
263                        if( !element.exists( name ) )
264                                ui_error( "there is not any child named """ + name, element );
265                        return element[name];
266                }
267
268        //! This methods tries to build a new double matrix
269        static void from_setting( mat& matrix, const Setting &element )
270        {
271                const Link_Expander link_expander( element );
272                const Setting &root = link_expander.root();
273
274                if( root.isNumber() )
275                {
276                        matrix.set_size( 1, 1 );
277                        matrix(0,0) = root;
278                        return;
279                }
280
281                if( root.isList() )
282                {
283                        if( root.getLength() != 3 )
284                                ui_error( "the setting supposed to represent a matrix element has wrong syntax", root );
285
286                        Setting &cols_setting = root[0];
287                        Setting &rows_setting = root[1];
288                        Setting &elements = root[2];
289
290                        ASSERT_UITYPE(cols_setting,TypeInt);
291                        ASSERT_UITYPE(rows_setting,TypeInt);
292                        ASSERT_UITYPE(elements,TypeArray);
293
294                        int cols = cols_setting;
295                        int rows = rows_setting;
296                                               
297                        if( cols < 0 | rows < 0 )
298                                ui_error( "the dimensions of a matrix has to be non-negative", root );
299
300                        if( elements.getLength() != cols * rows )
301                                ui_error( "the count of the matrix elements is incompatible with matrix dimension", elements );
302
303                        matrix.set_size( rows, cols );
304
305                        if( cols == 0 || rows == 0 )
306                                return;
307
308                        if( !elements[0].isNumber() )
309                                ui_error( "matrix elements have to be numbers", elements[0] );
310
311                        // build matrix row-wise
312                        int k = 0;
313                        for( int i=0;i<rows;i++ ) 
314                                for( int j=0; j<cols; j++)
315                                        matrix(i,j) = elements[k++];
316                        return;
317                }
318
319                ui_error( "only numeric types or TypeList are supported as matrix values", root );
320        }
321
322        //! This methods tries to save a double matrix
323        static void to_setting( const mat &matrix, Setting &root )
324        {
325                Setting &cols = root.add( Setting::TypeInt );
326                cols = matrix.cols();
327
328                Setting &rows = root.add( Setting::TypeInt );
329                rows = matrix.rows();
330
331                Setting &elements = root.add( Setting::TypeArray );
332
333                // build matrix row-wise
334                for( int i=0; i<matrix.rows(); i++ ) 
335                        for( int j=0; j<matrix.cols(); j++)
336                        {
337                                Setting &newField = elements.add(Setting::TypeFloat);
338                                newField = matrix(i,j);
339                        }
340        }
341
342        //! This methods tries to build a new integer vector
343        static void from_setting( ivec &vec, const Setting &element )
344        {
345                const Link_Expander link_expander( element );
346                const Setting &root = link_expander.root();
347
348                if( root.isNumber() )
349                {
350                        ASSERT_UITYPE(root,TypeInt);
351                        vec.set_length( 1 );
352                        vec(0) = root;
353                        return;
354                }
355
356                if( root.isList() )
357                {
358                        if( root.getLength() != 3 )
359                                ui_error( "the setting supposed to represent a matrix element has wrong syntax", root );
360
361                        Setting &cols_setting = root[0];
362                        Setting &rows_setting = root[1];
363                        Setting &elements = root[2];
364
365                        ASSERT_UITYPE(cols_setting,TypeInt);
366                        ASSERT_UITYPE(rows_setting,TypeInt);
367                        ASSERT_UITYPE(elements,TypeArray);
368
369                        int cols = cols_setting;
370                        int rows = rows_setting;
371                       
372                        if( cols < 0 | rows < 0)
373                                ui_error( "the dimensions of a matrix has to be non-negative", root );
374
375                        if( elements.getLength() != cols * rows )
376                                ui_error( "the count of the matrix elements is incompatible with matrix dimension", elements );
377
378                        if( cols != 1 & rows !=1)
379                                ui_error( "the vector length is invalid, it seems to be rather a matrix", elements );
380                       
381                        int len = rows * cols;
382                        vec.set_length ( len );
383                        if( len == 0 ) return;
384
385                        ASSERT_UITYPE(elements[0],TypeInt);
386                        for( int i=0; i<len; i++ ) 
387                                vec(i) = elements[i];
388                        return;
389                }
390
391                if( root.isArray() )
392                {       
393                        int len = root.getLength();
394                        vec.set_length( len );
395                        if( len == 0 ) return;
396
397                        ASSERT_UITYPE(root[0],TypeInt);
398                        for( int i=0; i < len; i++ ) 
399                                vec(i) = root[i];
400                        return;
401                }
402
403                ui_error( "only numeric types, TypeArray or TypeList are supported as vector values", root );
404        }
405
406        //! This methods tries to save an integer vector
407        static void to_setting( const ivec &vec, Setting &root )
408        {
409                for( int i=0; i<vec.length(); i++ ) 
410                {
411                        Setting &newField = root.add(Setting::TypeInt);
412                        newField = vec(i);
413                }
414        }
415
416        //! This methods tries to build a new array of strings
417        static void from_setting( Array<string> &string_array, const Setting &element )
418        {
419                const Link_Expander link_expander( element );
420                const Setting &root = link_expander.root();
421
422                if( root.getType() == Setting::TypeString )
423                {
424                        string_array.set_length( 1 );
425                        string_array(0) = (string)root;
426                        return;
427                }
428
429                if( root.isArray() )
430                {
431                        int len = root.getLength();
432                        string_array.set_length( len );
433                        if( len == 0 ) return;
434
435                        ASSERT_UITYPE(root[0],TypeString);
436                        for( int i=0; i < len; i++ ) 
437                                string_array(i) = (string)root[i];
438                        return;
439                }
440
441                ui_error( "only TypeString or TypeArray are supported as vector of string values", root );
442        }
443
444        //! This methods tries to save an array of strings
445        static void to_setting( const Array<string> &string_array, Setting &root )
446        {
447                for( int i=0; i<string_array.length(); i++ ) 
448                {
449                        Setting &newField = root.add(Setting::TypeString);
450                        newField = string_array(i);
451                }
452        }
453
454protected:
455
456        //! default constructor
457        UI( const string& class_name ) : class_name ( class_name )
458        {       
459                Class_To_UI::add_class( class_name, this );
460        }
461
462        //! Virtual destructor for future use;
463        virtual ~UI(){};
464
465public: 
466
467        static void ui_error( string message, const Setting &element )
468        {
469                stringstream error_message;
470                error_message << "UI ui_error: " << message << "! Check path """ << element.getPath() << """, source line " << element.getSourceLine() << ".";
471                it_error ( error_message.str() );
472        }
473
474        //! This methods tries to build a new instance of type T (or some of its descendant types)
475        //! according to a data stored in a DOMNode named class_name within a child nodes of the passed element.
476        //! If an ui_error occurs, it returns a NULL pointer.
477
478        //! Prototype of a UI builder. Return value is by the second argument since it type checking via \c dynamic_cast.
479        template<class T> static T* build( Setting &element, const int index )
480        {
481                return from_setting<T>( to_child_setting( element, index ) );
482        }
483
484        template<class T> static T* build( Setting &element, const string &name )
485        {                       
486                return from_setting<T>( to_child_setting( element, name ) );
487        }
488
489        //! This methods tries to save an instance of type T (or some of its descendant types)
490        //! and build DOM tree accordingly. Then, it creates a new DOMNode named according class_name
491        //! and connecti it to the passed Setting as a new child node.
492        template<class T> static void save( T &instance, Setting &element, const string &name = "")
493        {
494                Setting &root = (name == "") ? element.add( Setting::TypeGroup )                                                       
495                                                                         : element.add( name, Setting::TypeGroup );             
496                to_setting( instance, root );
497        }
498
499        //! This methods tries to build a new double matrix
500        static bool get( mat& matrix, const Setting &element, const string &name )
501        {
502                const Setting *root = to_child_setting( element, name );
503                if( !root ) return false;                               
504                from_setting( matrix, *root );
505                return true;
506        }
507
508        //! This methods tries to build a new double matrix
509        static bool get( mat& matrix, const Setting &element, const int index )
510        {
511                const Setting *root = to_child_setting( element, index );
512                if( !root ) return false;                               
513                from_setting( matrix, *root );
514                return true;
515        }
516
517        //! This methods tries to save a double matrix
518        static void save( mat &matrix, Setting &element, const string &name = "" )
519        {
520                Setting &root = (name == "") ? element.add( Setting::TypeList )                                                 
521                                                                         : element.add( name, Setting::TypeList );             
522                to_setting( matrix, root );
523        }
524
525        //! This methods tries to build a new double vec
526        static bool get( ivec& vec, const Setting &element, const string &name )
527        {
528                const Setting *root = to_child_setting( element, name );
529                if( !root ) return false;                               
530                from_setting( vec, *root );
531                return true;
532        }
533
534        //! This methods tries to build a new double vec
535        static bool get( ivec& vec, const Setting &element, const int index )
536        {
537                const Setting *root = to_child_setting( element, index );
538                if( !root ) return false;                               
539                from_setting( vec, *root );
540                return true;
541        }
542
543        //! This methods tries to save a double vec
544        static void save( ivec &vec, Setting &element, const string &name = "" )
545        {
546                Setting &root = (name == "") ? element.add( Setting::TypeArray )                                                       
547                                                                         : element.add( name, Setting::TypeArray );             
548                to_setting( vec, root );
549        }
550
551        //! This methods tries to build a new double string_array
552        static bool get( Array<string> &string_array, const Setting &element, const string &name )
553        {
554                const Setting *root = to_child_setting( element, name );
555                if( !root ) return false;                               
556                from_setting( string_array, *root );
557                return true;
558        }
559
560        //! This methods tries to build a new double string_array
561        static bool get( Array<string> &string_array, const Setting &element, const int index )
562        {
563                const Setting *root = to_child_setting( element, index );
564                if( !root ) return false;                               
565                from_setting( string_array, *root );
566                return true;
567        }
568
569        //! This methods tries to save a double string_array
570        static void save( Array<string> &string_array, Setting &element, const string &name = "" )
571        {
572                Setting &root = (name == "") ? element.add( Setting::TypeArray )                                                       
573                                                                         : element.add( name, Setting::TypeArray );             
574                to_setting( string_array, root );
575        }
576};
577
578
579/*!
580@brief The main userinfo template class. You should derive this class whenever you need
581a new userinfo of a class which is compound from smaller elements (all having its
582own userinfo class prepared).
583*/
584template<typename T> class Particular_UI : private UI
585{
586        // to permit acces to the Particular_UI<T>::ui to the UI class
587        friend UI;
588
589        //! default constructor, which is intentionally declared as private
590        Particular_UI<T>( const string &class_name) : UI( class_name ) 
591        {       
592        };
593
594        //! the only instance of this class (each type T has its own instance)
595        //! which is used as a factory for processing related UI
596        static Particular_UI<T>& ui;   
597
598        bdmroot* new_instance()
599        {
600                return new T();
601        }
602};
603
604
605}
606
607/*! Recursive build of objects defined in the same file
608
609\code
610{type="internal";
611path="system.profile.[0]";    // Path from the root
612};
613\endcode
614 */
615
616
617
618/*! Recursive build of objects defined in external file
619
620\code
621{type="external";
622filename="my_file.cfg";       // name of file from which to read
623path="system.profile.[0]";    // Path in the external file
624};
625\endcode
626/
627
628*/
629
630#endif // #ifndef UIBUILD
Note: See TracBrowser for help on using the browser.