root/bdm/userinfo.h @ 160

Revision 160, 20.0 kB (checked in by smidl, 16 years ago)

Compile userinfo on Linux without warnings

  • Property svn:eol-style set to native
Line 
1#ifndef UI_H
2#define UI_H
3
4#include <sstream>
5#include <iostream>
6#include <stdio.h>
7#include <string>
8#include <typeinfo>
9#include <map>
10#include <utility>
11#include <vector>
12#include <xercesc/dom/DOM.hpp>
13#include <xercesc/util/PlatformUtils.hpp>
14#include <xercesc/util/XMLString.hpp>
15#include <iostream>
16#include <xercesc/framework/LocalFileFormatTarget.hpp>
17#include <xercesc/framework/LocalFileInputSource.hpp>
18#include <xercesc/dom/DOMWriter.hpp>
19#include <xercesc/parsers/XercesDOMParser.hpp>
20
21#ifdef XERCES_CPP_NAMESPACE_USE
22XERCES_CPP_NAMESPACE_USE
23#endif
24
25using std::string;
26using namespace std;
27
28/*!
29@brief Class initializing Xerces library
30
31This class is used to initialize the Xerces library. Namely
32to call static method XMLPlatformUtils::Initialize(). It should
33be called just once, at previously to any other usage of the Xerces
34library. This behaviour is assured by a quite complicated design
35based on the use of an inner singleton class named 'XercesConnector'.
36The point is to be the first initialized class even within the set of all
37(global) static variables. For better understanding, find instances of
38this class and look at their context.
39*/
40class AssertXercesIsAlive
41{
42private:
43        //! Singleton class implementing the key property of the 'AssertXercesIsAlive' class
44        class XercesConnector
45        {
46        private:
47                //! Default constructor, which is intentionally declared as private and called only from static
48                //! method 'StayAlive()'. Therfore, it is called only once in the run of a application!
49                XercesConnector()
50                {
51                        // initialize the XML library
52                        XMLPlatformUtils::Initialize();
53                }
54
55                ~XercesConnector()
56                {
57                        // terminate the XML library
58                        XMLPlatformUtils::Terminate();
59                }
60
61        public:
62                //! This method just touches the only instance of the XercesConnector class
63                //! which is declared as a static local variable.
64                //!
65                //! This way we know that Xerces was initialized properly when returning from StayAlive().
66                //! And what is more, the local nature of the inner variable prevents us before multiple
67                //! initializations.
68                static void StayAlive()
69                {
70                        static XercesConnector xc;             
71                };
72        };
73
74
75public:
76        //!default constructor
77        AssertXercesIsAlive()
78        {
79                XercesConnector::StayAlive();
80        }
81};
82
83/*!
84@brief Abstract class declaring general properties of a frame for data binding
85*/
86class BindingFrame
87{
88private:
89        //! This private attribute has to be declared as the first attribute in the class.
90        //! Only this way we can be sure it's constructor is called as the first
91        //! and thus Xerces is initialized properly and right on time
92        AssertXercesIsAlive dummy;
93
94protected:
95        //!default constructor
96        BindingFrame();
97
98        //! function which transcodes Xerces' XMLCh-based strings into C++ strings
99        string XMLCh2str( const XMLCh* const  XMLCh_str );
100
101        string removeSpaces(const string &str) 
102        {
103                std::string temp;
104                for (unsigned int i = 0; i < str.length(); i++)
105                        if (str[i] == ' ') temp += '_';
106                        else temp += str[i];
107                return temp;
108        }
109
110public: 
111        //! This method parse DOMElement, finds proper DOMNode and fills binded data accordingly
112        virtual void AssemblyComponentsFromXML( DOMElement &element ) = 0;
113
114        //! A method for releasing memory allocated within the 'AssemblyComponentsFromXML()' method
115        virtual void ReleaseMemory() {}
116
117        //! This method reads binded data, fill them into a new DOMNode, which then
118        //! appends to the passed DOMElement
119        virtual bool DisassemblyComponentsToXML( DOMElement &element ) = 0;             
120};
121
122/*!
123@brief Abstract class declaring general properties of a frame for data binding
124*/
125class Attribute
126{               
127private:
128        //! This private attribute has to be declared as the first attribute in the class.
129        //! Only this way we can be sure it's constructor is called as the first
130        //! and thus Xerces is initialized properly and right on time
131        AssertXercesIsAlive dummy;
132
133        //! an attribute name
134        const XMLCh* const transcodedAttributeName;
135
136public:
137        //! Default constructor fixing a name of the related attribute
138        Attribute( string attributeName );
139
140        ~Attribute();
141
142        //! This method parse DOMElement, finds proper attribute and returns its value
143        string& Get( DOMElement &element ) const;
144
145        //! This method adds the passed string as an new attribute into the passed DOMElement
146        void Set( DOMElement &element, const string &str ) const;       
147
148        //! Static member, an instance related to an attribute named 'help'
149        static const Attribute help;
150
151        //! Static member, a constant instance related to an attribute named 'type'
152        static const Attribute type;
153
154        //! Static member, a constant instance related to the attribute named 'value'
155        static const Attribute value;
156};
157
158
159
160/*!
161@brief UserInfo is an abstract is for internal purposes only. Use CompoundUserInfo<T> or ValuedUserInfo<T> instead.
162The raison d'etre of this class is to allow pointers to its templated descendants.
163
164Also, the main functions of the whole UserInfo library are included within this class, see
165static methods 'Assembly' and 'Disassembly'.
166*/
167class UserInfo : protected BindingFrame
168{
169private:
170        //! just a typedef shortuct for a constant pointer to UserInfo
171        typedef UserInfo* const pUserInfo;
172
173        //! static class encalupsating map of names to related UserInfos
174        //!
175        //! The key property of this class is that it initilaized the internal map immediately
176        //! when it is used for a first time.
177        class StringToUIMap
178        {
179        private:
180                //! Type definition of mapping which transforms type names to the related user infors
181                typedef map< const string, pUserInfo > MappedString2UI;
182
183                //! immediately initialized instance of type MappedString2UI
184                static MappedString2UI& privateMap();
185
186        public:
187                //! add a pair key-userinfo into the internal map
188                static void Add( string key, pUserInfo pInstance );
189
190                //! search for an userinfo related to the passed key within the internal map
191                static pUserInfo Retrieve( string key );
192        };
193                               
194        //! internal method assembling a typeless instance from components obtained by the 'AssemblyComponentsFromXML()' method
195        virtual void* AssemblyTypelessInstance() = 0;
196       
197        //! internal method disassembling a typeless instance to components which are processed by the 'DisassemblyComponentsToXML()' method
198        virtual bool DisassemblyTypelessInstance(void* pInstance) = 0;
199
200        //! an user-friendly type name
201        const string userFriendlyTypeName;
202
203        //! a type name obtained by RTTI
204        const string typeNameByRTTI;
205
206protected:
207
208        //! default constructor
209        UserInfo( const string& userFriendlyTypeName, const string& typeNameByRTTI )
210                : userFriendlyTypeName ( removeSpaces( userFriendlyTypeName ) ), 
211                  typeNameByRTTI( typeNameByRTTI )
212        {       
213                StringToUIMap::Add( userFriendlyTypeName, this );
214
215                if( userFriendlyTypeName != typeNameByRTTI )
216                        // we have a common map for both groups of names,
217                        // therefore, it is no use to add the same pair again
218                        StringToUIMap::Add( typeNameByRTTI, this );
219        }
220
221public: 
222        //! This methods tries to assembly a new instance of type T (or some of its descendant types)
223        //! according to a data stored in a DOMNode named tagName within a child nodes of the passed element.
224        //! If an error occurs, it returns a NULL pointer.
225        template<class T>
226        static T* Assembly( DOMElement &element, const string tagName )
227        {       
228                XMLCh* transcodedTagName = XMLString::transcode( tagName.c_str() );             
229                XMLString::upperCase( transcodedTagName );
230
231                DOMNodeList* const nodeList = element.getElementsByTagName( transcodedTagName );
232                XMLString::release( (XMLCh**)&transcodedTagName );
233                if( !nodeList || nodeList->getLength() == 0 )
234                {
235                        cerr << "Warning: there is not any tag named """ << tagName << """ in the passed DOM element of a XML docmument!";
236                        return NULL;
237                }
238
239                if( nodeList->getLength() > 1 )
240                {
241                        cerr << "Warning: there is to many elements named """ << tagName << """ in the passed DOM element of a XML docmument. But the tag name has to be unique!";
242                        return NULL;
243                }
244
245                // this time we hold an element with the same name as the tagName is
246                DOMElement* pTheOnlyElement = (DOMElement*) nodeList->item(0);
247
248                // we get a velue stored in the "type" attribute
249                string userFriendlyTypeName = Attribute::type.Get( *pTheOnlyElement );
250       
251                // and finally we find a UserInfo related to this type
252                pUserInfo pRelatedUI = StringToUIMap::Retrieve( userFriendlyTypeName );
253                if( !pRelatedUI )
254                {
255                        cerr << "Warning: there is not any UserInfo related to type named """ << userFriendlyTypeName << """, instance assembling terminated!";
256                        return NULL;
257                }
258
259                // prepare all components necessary for an instance assembling
260                pRelatedUI->AssemblyComponentsFromXML( *pTheOnlyElement );             
261
262                // instance assembling
263                void* pTypelessInstance = pRelatedUI->AssemblyTypelessInstance();
264
265                // cleaning up
266                pRelatedUI->ReleaseMemory();
267
268                if( pTypelessInstance == NULL )
269                {
270                        cerr << "Warning: there was some error when parsing a XML document, instance assembling terminated!";
271                        return NULL;
272                }
273
274                T* pInstance = NULL;
275                try
276                {
277                        // a "do it yourself" type check:)
278                        pInstance = (T*) pTypelessInstance;
279                        string resultingTypeNameByRTTI = typeid( *pInstance ).name();
280                        if( resultingTypeNameByRTTI != pRelatedUI->typeNameByRTTI )
281                                pInstance = NULL;
282                }
283                catch(...)
284                {
285                        pInstance = NULL;
286                }               
287                if( pInstance == NULL )
288                        cerr << "Warning: UserInfo related to type """ << userFriendlyTypeName << """ have just returned instance of a different type! Instance assembling terminated!";
289
290                return pInstance;
291        }       
292
293        //! This methods tries to disassembly an instance of type T (or some of its descendant types)
294        //! and build DOM tree accordingly. Then, it creates a new DOMNode named according tagName
295        //! and connecti it to the passed DOMElement as a new child node (with a help attribute filled).
296        template<class T>
297        static bool Disassembly( T& instance, DOMElement &element, const string tagName, const string help)
298        {       
299                pUserInfo pRelatedUI = StringToUIMap::Retrieve( typeid(instance).name() );
300                if( !pRelatedUI )
301                        return false;
302
303                // add a new element named according the passed tagName
304                XMLCh* transcodedTagName = XMLString::transcode( tagName.c_str() );             
305                XMLString::upperCase( transcodedTagName );
306                DOMDocument* pDoc = element.getOwnerDocument();
307                DOMElement* pCreatedElement = pDoc->createElement( transcodedTagName );         
308                element.appendChild( pCreatedElement );
309                XMLString::release( (XMLCh**)&transcodedTagName );
310                       
311                // add attributes "type" and "help"
312                Attribute::type.Set( *pCreatedElement, pRelatedUI->userFriendlyTypeName );             
313                Attribute::help.Set( *pCreatedElement, help );
314               
315                // disassembly instance itself into its components
316                bool result =  pRelatedUI->DisassemblyTypelessInstance( (void*) &instance );
317                if( result )
318                        // disassembly all components of an instance   
319                        result = pRelatedUI->DisassemblyComponentsToXML( *pCreatedElement );           
320                return result;
321        }       
322
323        //! This methods tries to disassembly an instance of type T (or some of its descendant types)
324        //! and build DOM tree accordingly. Then, it creates a new DOMNode named according tagName
325        //! and connecti it to the passed DOMElement as a new child node.
326        template<class T>
327        static bool Disassembly( T &instance, DOMElement &element, const string tagName )
328        {
329                return Disassembly( instance, element, tagName, "" );
330        }
331};
332
333/*!
334@brief TypeUserInfo is still an abstract class, but contrary to the UserInfo class it is already
335templated. It serves as a bridge to non-abstract classes CompoundUserInfo<T> or ValuedUserInfo<T>.
336
337There are two important features of this class. The first is a primitive mechanism bounding
338typeless methods DisassemblyTypelessInstance, resp. AssemblyTypelessInstance, to their typed
339virtual versions DisassemblyInstance, resp. AssemblyInstance. And the other is the only public
340attribute of this class, called 'instance', which is to be filled by the only instance of this
341class. Indeed, it is not  possible to create any other instance outside this class (or its
342descendant classes), as the constructor is intentionally protected.
343*/
344template<typename T> class TypedUserInfo : public UserInfo
345{
346private:
347
348        bool DisassemblyTypelessInstance(void* pInstance)
349        {
350                try
351                {
352                        return DisassemblyInstance( *(T*) pInstance );
353                }
354                catch (...)
355                {
356                        return false;
357                }
358        }
359
360        void* AssemblyTypelessInstance()
361        {
362                return (void*) AssemblyInstance( );
363        }
364
365        //! abstract method assembling a typed instance from components obtained by the 'AssemblyComponentsFromXML()' method
366        virtual T* AssemblyInstance() = 0;
367
368        //! abstract method disassembling a typed instance to components which are processed by the 'DisassemblyComponentsToXML()' method
369        virtual bool DisassemblyInstance(T& instance) = 0;
370
371protected:
372
373        //! default constructor, which is intentionally declared as protected
374        TypedUserInfo<T>( const string &userFriendlyTypeName) 
375                : UserInfo( userFriendlyTypeName, typeid(T).name() ) 
376        {       
377
378        };
379
380        //! destructor
381        ~TypedUserInfo<T>()
382        {
383        }
384
385        //! the only instance of this class (each type T has its own instance)
386        //! which is used as a factory for processing related userinfos
387        static const TypedUserInfo<T>& instance;
388};
389
390
391/*!
392@brief The main userinfo template class. You should derive this class whenever you need
393a new userinfo of a class which is compound from smaller elements (all having its
394own userinfo class prepared).
395
396To bind some inner element to its own userinfo class, and also to automate its assembling and
397disassembling, it is necessary to create a instance of an inner templated class BindedElement<T>.
398Those attributes have to be initialized in constructor of a new compound userinfo this way:
399
400\code
401class BikeUI: public CompoundUserInfo<Bike>
402{
403private:
404        BindedElement<int> year;
405        BindedElement<bool> lights;
406        BindedElement<string> manufacturer;
407public:
408        BikeUI()
409                :CompoundUserInfo<Bike>("bike"),
410                year( this, "year", 0 ),
411                lights( this, "electric lights", false ),
412                manufacturer( this, "manufacturer", "unknown")
413        {
414        }
415
416        ...
417}
418\endcode
419*/
420template<typename T> class CompoundUserInfo : public TypedUserInfo<T>
421{
422private:
423        //! Elements binding inner XML tags to related userinfos
424        vector<BindingFrame*> bindedElements;
425
426protected:
427
428        /*!
429        @brief Templated class binding inner element with its XML tag and automating data transfers
430        in both directions.
431        */
432        template<typename U> class BindedElement: public BindingFrame
433        {
434        private:
435                string name;
436                string help;                           
437                bool release;
438
439                U* pValue;
440
441                const U defaultValue;
442               
443 
444        public:
445                U value;
446
447                BindedElement<U>( CompoundUserInfo<T> *parent, string name, U defaultValue, string help ) 
448                        : name( removeSpaces( name )), help(help), defaultValue( defaultValue )
449                {
450                        parent->bindedElements.push_back( this );
451                        pValue = NULL;
452                        value = defaultValue;
453                }
454
455                BindedElement<U>( CompoundUserInfo<T> *parent, string name, U defaultValue ) 
456                        : name( removeSpaces( name )), help(""), defaultValue( defaultValue ), value( defaultValue)
457                {
458                        parent->bindedElements.push_back( this );
459                        pValue = NULL;
460                        value = defaultValue;
461                }
462
463                ~BindedElement<U>()
464                {
465                }
466
467                void AssemblyComponentsFromXML( DOMElement &element )
468                {
469                        pValue = UserInfo::Assembly<U>( element, name );
470                        if( pValue ) value = *pValue;                   
471                }
472
473                void ReleaseMemory()
474                {
475                        if( pValue != NULL )
476                                delete pValue;
477                }
478
479                bool DisassemblyComponentsToXML( DOMElement &element )
480                {
481                        return UserInfo::Disassembly( value, element, name, help );
482                }
483        };
484
485private:
486
487        void AssemblyComponentsFromXML( DOMElement &element )
488        {
489                for( unsigned int ind = 0; ind < bindedElements.size(); ind++ )
490                        bindedElements[ind]->AssemblyComponentsFromXML( element );
491        }
492
493        void ReleaseMemory()
494        {                       
495                for( unsigned int ind = 0; ind < bindedElements.size(); ind++ )
496                        bindedElements[ind]->ReleaseMemory();
497        }
498
499        bool DisassemblyComponentsToXML( DOMElement &element )
500        {
501                for( unsigned int ind = 0; ind < bindedElements.size(); ind++ )
502                        if( !bindedElements[ind]->DisassemblyComponentsToXML( element ) )
503                                return false;
504                return true;
505        }
506
507protected:
508
509        CompoundUserInfo<T>( string userFriendlyTypeName )
510                : TypedUserInfo<T>( userFriendlyTypeName )
511        {
512        }
513
514};
515
516
517/*!
518@brief The main userinfo template class. It should be derived whenever you need
519a new userinfo of a class which does not contain any subelements. It is the case of
520basic classes(or types) like int, string, double, etc.
521
522The only thing left is to translate its public string attribute 'value' into a value
523of type T and also implement conversion in the other way round. For that, an overloading
524of methods T* AssemblyInstance() /  bool DisassemblyInstance(T  &instance) is fruitful.
525See some valued userinfo below as an example.
526*/
527template<typename T> class ValuedUserInfo : public TypedUserInfo<T>
528{
529private:
530        void AssemblyComponentsFromXML( DOMElement &element )
531        {               
532                value = Attribute::value.Get( element );
533        }
534
535        bool DisassemblyComponentsToXML( DOMElement &element )
536        {
537                Attribute::value.Set( element, value );
538                return true;
539        }
540
541protected:
542        ValuedUserInfo<T>( string userFriendlyTypeName )
543                : TypedUserInfo<T>( userFriendlyTypeName )
544        {
545        }
546
547        ~ValuedUserInfo<T>()
548        {
549        }
550
551        //! string variable which is automatically binded to a proper DOMElement attribute
552        string value;
553};
554
555/*!
556@brief This class serves to load and/or save DOMElements into/from files
557stored on a hard-disk.
558
559Firstly, you associate new RootElement instance with some filename during a time of its
560construtcion. Then, you disassembly some object into the new RootElement instance,
561and save it into the file this way:
562\code
563        CAudi audi;
564        RootElement root("cars.xml");
565        UserInfo::Disassembly( audi, root, "TT");
566        root.Save();
567\endcode
568
569In the other way round, when loading object from a XML file, the appropriate code looks like this:
570\code
571        RootElement root("cars.xml");
572        root.Load();
573        UserInfo::Assembly<T>(root,"TT");
574\endcode
575*/
576class RootElement
577{
578private:
579        //! This private attribute has to be declared as the first attribute in the class.
580        //! Only this way we can be sure it's constructor is called as the first
581        //! and thus Xerces is initialized properly and right on time
582        const AssertXercesIsAlive dummy;
583
584        //! DOMDocument containing the root element this instance is associated to
585        DOMDocument* pDoc;
586
587        const XMLCh* const transcodedFileName;
588
589        //! DOMImplementation is a base class for the all DOM oparations
590        DOMImplementation *pImplementation;
591
592        //! This DOMWriter is used to export internal data into xml file
593        DOMWriter *pSerializer;
594
595        void Clean();
596
597public:
598        //! attach new RootElement instance to a file (typically with an XML extension)
599        RootElement(const char* fileName );
600
601        ~RootElement();
602
603        //! this method loads root element and all its subnodes from the attached file
604        bool Load( void ) ;
605
606        //! this method saves all the previsoulsy attached DOMElements into the file
607        void Save ( void );
608
609        //! this operator allows to use a RootElement instance whenever a DOMElement variable is accepted
610        operator DOMElement&();
611};
612
613
614//////////////////////////////////////////////////////////////////////////////////////////////
615////////////////////////////////////////// BASIC VALUED USER INFOS ///////////////////////////
616//////////////////////////////////////////////////////////////////////////////////////////////
617
618class BoolUI: public ValuedUserInfo<bool>
619{
620private:
621
622        bool* AssemblyInstance()
623        {               
624                if( value == "true" )
625                        return new bool( true );
626                else if( value == "false" )
627                        return new bool( false );
628                else return NULL;
629        }
630
631        bool DisassemblyInstance(bool &instance)
632        {
633                if( instance )
634                        value = "true";
635                else
636                        value = "false";
637                return true; 
638        }
639
640public:
641
642        BoolUI()
643                : ValuedUserInfo<bool>("bool")
644        {
645        }
646};
647
648
649class IntUI: public ValuedUserInfo<int>
650{
651private:
652
653        int* AssemblyInstance()
654        {
655                return new int( atoi( value.c_str()) );
656        }
657
658        bool DisassemblyInstance(int &instance)
659        {
660                char buff[30];
661                sprintf(buff, "%d", instance );
662                value = buff;
663                return true; 
664        }
665
666public:
667        IntUI():ValuedUserInfo<int>("int")
668        {
669        }
670};
671
672
673
674class DoubleUI: public ValuedUserInfo<double>
675{
676private:
677
678        double* AssemblyInstance()
679        {
680                return new double( atof( value.c_str()) );
681        }
682
683        bool DisassemblyInstance(double &instance)
684        {
685                char buff[30];
686                sprintf(buff, "%f", instance );
687                value = buff;
688                return true; 
689        }
690
691public:
692        DoubleUI():ValuedUserInfo<double>("double")
693        {
694        }
695};
696
697
698class StringUI: public ValuedUserInfo<string>
699{
700private:
701        string* AssemblyInstance()
702        {
703                return new string( value );
704        }
705
706        bool DisassemblyInstance(string &instance)
707        {
708                value = instance;
709                return true;
710        }
711
712public:
713        StringUI():ValuedUserInfo<string>("string")
714        {
715        }
716};
717
718
719#endif // #ifndef UI_H
Note: See TracBrowser for help on using the browser.