Orocos Real-Time Toolkit  2.5.0
TinyDemarshaller.cpp
00001 /***************************************************************************
00002   tag: FMTC  Tue Mar 11 21:49:20 CET 2008  TinyDemarshaller.cpp
00003 
00004                         TinyDemarshaller.cpp -  description
00005                            -------------------
00006     begin                : Tue March 11 2008
00007     copyright            : (C) 2008 FMTC
00008     email                : peter.soetens@fmtc.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU General Public                   *
00013  *   License as published by the Free Software Foundation;                 *
00014  *   version 2 of the License.                                             *
00015  *                                                                         *
00016  *   As a special exception, you may use this file as part of a free       *
00017  *   software library without restriction.  Specifically, if other files   *
00018  *   instantiate templates or use macros or inline functions from this     *
00019  *   file, or you compile this file and link it with other files to        *
00020  *   produce an executable, this file does not by itself cause the         *
00021  *   resulting executable to be covered by the GNU General Public          *
00022  *   License.  This exception does not however invalidate any other        *
00023  *   reasons why the executable file might be covered by the GNU General   *
00024  *   Public License.                                                       *
00025  *                                                                         *
00026  *   This library is distributed in the hope that it will be useful,       *
00027  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00028  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00029  *   Lesser General Public License for more details.                       *
00030  *                                                                         *
00031  *   You should have received a copy of the GNU General Public             *
00032  *   License along with this library; if not, write to the Free Software   *
00033  *   Foundation, Inc., 59 Temple Place,                                    *
00034  *   Suite 330, Boston, MA  02111-1307  USA                                *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 
00039 #include "TinyDemarshaller.hpp"
00040 
00041 
00042 // Modified tinyxml* to include it in the RTT namespace to avoid clashes
00043 // with possible other libraries.
00044 #include "tinyxml.h"
00045 
00046 // This is currently not defined:
00047 #ifdef TIXML_USE_STL
00048 #include <iostream>
00049 #include <sstream>
00050 using namespace std;
00051 #else
00052 #include <cstdio>
00053 #endif
00054 
00055 #include <stack>
00056 #include <Property.hpp>
00057 #include <PropertyBag.hpp>
00058 #include <Logger.hpp>
00059 
00060 namespace RTT
00061 {
00062     namespace marsh
00063     {
00064         class Tiny2CPFHandler
00065         {
00069             PropertyBag &bag;
00070             std::stack< std::pair<PropertyBag*, Property<PropertyBag>*> > bag_stack;
00071 
00072             enum Tag { TAG_STRUCT, TAG_SIMPLE, TAG_SEQUENCE, TAG_PROPERTIES, TAG_DESCRIPTION, TAG_VALUE, TAG_UNKNOWN};
00073             std::stack<Tag> tag_stack;
00074 
00078             std::string name;
00079             std::string description;
00080             std::string type;
00081             std::string value_string;
00082 
00083         public:
00084 
00085             Tiny2CPFHandler( PropertyBag &b ) : bag( b )
00086             {
00087                 Property<PropertyBag>* dummy = 0;
00088                 bag_stack.push(std::make_pair(&bag, dummy));
00089             }
00090 
00091             bool endElement()
00092             {
00093                 switch ( tag_stack.top() )
00094                     {
00095                     case TAG_SIMPLE:
00096                         if ( type == "boolean" )
00097                         {
00098                             if ( value_string == "1" || value_string == "true")
00099                                 bag_stack.top().first->add
00100                                 ( new Property<bool>( name, description, true ) );
00101                             else if ( value_string == "0" || value_string == "false")
00102                                 bag_stack.top().first->add
00103                                 ( new Property<bool>( name, description, false ) );
00104                             else {
00105                                 log(Error)<< "Wrong value for property '"+type+"'." \
00106                                     " Value should contain '0' or '1', got '"+ value_string +"'." << endlog();
00107                                 return false;
00108                             }
00109                         }
00110                         else if ( type == "char" ) {
00111                             if ( value_string.length() > 1 ) {
00112                                 log(Error) << "Wrong value for property '"+type+"'." \
00113                                     " Value should contain a single character, got '"+ value_string +"'." << endlog();
00114                                 return false;
00115                             }
00116                             else
00117                                 bag_stack.top().first->add
00118                                     ( new Property<char>( name, description, value_string.empty() ? '\0' : value_string[0] ) );
00119                         }
00120                         else if ( type == "uchar" || type == "octet" ) {
00121                             if ( value_string.length() > 1 ) {
00122                                 log(Error) << "Wrong value for property '"+type+"'." \
00123                                     " Value should contain a single unsigned character, got '"+ value_string +"'." << endlog();
00124                                 return false;
00125                             }
00126                             else
00127                                 bag_stack.top().first->add
00128                                     ( new Property<unsigned char>( name, description, value_string.empty() ? '\0' : value_string[0] ) );
00129                         }
00130                         else if ( type == "long" || type == "short")
00131                         {
00132                             if (type == "short") {
00133                                 log(Warning) << "Use type='long' instead of type='short' for Property '"<< name << "', since 16bit integers are not supported." <<endlog();
00134                                 log(Warning) << "Future versions of RTT will no longer map XML 'short' to C++ 'int' but to C++ 'short' Property objects." <<endlog();
00135                             }
00136                             int v;
00137                             if ( sscanf(value_string.c_str(), "%d", &v) == 1)
00138                                 bag_stack.top().first->add( new Property<int>( name, description, v ) );
00139                             else {
00140                                 log(Error) << "Wrong value for property '"+type+"'." \
00141                                     " Value should contain an integer value, got '"+ value_string +"'." << endlog();
00142                                 return false;
00143                             }
00144                         }
00145                         else if ( type == "ulong" || type == "ushort")
00146                         {
00147                             if (type == "ushort") {
00148                                 log(Warning) << "Use type='ulong' instead of type='ushort' for Property '"<< name << "', since 16bit integers are not supported." <<endlog();
00149                                 log(Warning) << "Future versions of RTT will no longer map XML 'ushort' to C++ 'unsigned int' but to C++ 'unsigned short' Property objects." <<endlog();
00150                             }
00151                             unsigned int v;
00152                             if ( sscanf(value_string.c_str(), "%u", &v) == 1)
00153                                 bag_stack.top().first->add( new Property<unsigned int>( name, description, v ) );
00154                             else {
00155                                 log(Error) << "Wrong value for property '"+type+"'." \
00156                                     " Value should contain an integer value, got '"+ value_string +"'." << endlog();
00157                                 return false;
00158                             }
00159                         }
00160                         else if ( type == "double")
00161                         {
00162                             double v;
00163                             if ( sscanf(value_string.c_str(), "%lf", &v) == 1 )
00164                                 bag_stack.top().first->add
00165                                     ( new Property<double>( name, description, v ) );
00166                             else {
00167                                 log(Error) << "Wrong value for property '"+type+"'." \
00168                                     " Value should contain a double value, got '"+ value_string +"'." << endlog();
00169                                 return false;
00170                             }
00171                         }
00172                         else if ( type == "float")
00173                         {
00174                             float v;
00175                             if ( sscanf(value_string.c_str(), "%f", &v) == 1 )
00176                                 bag_stack.top().first->add
00177                                     ( new Property<float>( name, description, v ) );
00178                             else {
00179                                 log(Error) << "Wrong value for property '"+type+"'." \
00180                                     " Value should contain a float value, got '"+ value_string +"'." << endlog();
00181                                 return false;
00182                             }
00183                         }
00184                         else if ( type == "string")
00185                             bag_stack.top().first->add
00186                             ( new Property<std::string>( name, description, value_string ) );
00187                         tag_stack.pop();
00188                         value_string.clear(); // cleanup
00189                         description.clear();
00190                         name.clear();
00191                         break;
00192 
00193                     case TAG_SEQUENCE:
00194                     case TAG_STRUCT:
00195                         {
00196                             Property<PropertyBag>* prop = bag_stack.top().second;
00197                             bag_stack.pop();
00198                             bag_stack.top().first->add( prop );
00199                             //( new Property<PropertyBag>( pn, description, *pb ) );
00200                             //delete pb;
00201                             tag_stack.pop();
00202                             description.clear();
00203                             name.clear();
00204                             type.clear();
00205                         }
00206                         break;
00207 
00208                     case TAG_DESCRIPTION:
00209                         tag_stack.pop();
00210                         if ( tag_stack.top() == TAG_STRUCT ) {
00211                             // it is a description of a struct that ended
00212                             bag_stack.top().second->setDescription(description);
00213                             description.clear();
00214                         }
00215                         break;
00216                     case TAG_VALUE:
00217                     case TAG_PROPERTIES:
00218                     case TAG_UNKNOWN:
00219                         tag_stack.pop();
00220                         break;
00221 
00222                     }
00223                 return true;
00224             }
00225 
00226 
00227             void startElement(const char* localname,
00228                               const TiXmlAttribute* attributes )
00229             {
00230                 std::string ln = localname;
00231 
00232                 if ( ln == "properties" )
00233                     tag_stack.push( TAG_PROPERTIES );
00234                 else
00235                     if ( ln == "simple" )
00236                     {
00237                         tag_stack.push( TAG_SIMPLE );
00238                         name.clear();
00239                         type.clear();
00240                         while (attributes)
00241                         {
00242                             std::string an = attributes->Name();
00243                             if ( an == "name")
00244                             {
00245                                 name = attributes->Value();
00246                             }
00247                             else if ( an == "type")
00248                             {
00249                                 type = attributes->Value();
00250                             }
00251                             attributes = attributes->Next();
00252                         }
00253                     }
00254                     else
00255                         if ( ln == "struct" || ln == "sequence")
00256                         {
00257                             name.clear();
00258                             type.clear();
00259                             while (attributes)
00260                                 {
00261                                     std::string an = attributes->Name();
00262                                     if ( an == "name")
00263                                         {
00264                                             name = attributes->Value();
00265                                         }
00266                                     else if ( an == "type")
00267                                         {
00268                                             type = attributes->Value();
00269                                         }
00270                                     attributes = attributes->Next();
00271                                 }
00272                             if ( ln == "struct" )
00273                                 tag_stack.push( TAG_STRUCT );
00274                             else {
00275                                 tag_stack.push( TAG_SEQUENCE );
00276                                 type = "Sequence"; // override
00277                             }
00278 
00279                             Property<PropertyBag> *prop;
00280                             prop = new Property<PropertyBag>(name,"",PropertyBag(type));
00281 
00282                             // take reference to bag itself !
00283                             bag_stack.push(std::make_pair( &(prop->value()), prop));
00284                         }
00285                         else
00286                                 if ( ln == "description")
00287                                     tag_stack.push( TAG_DESCRIPTION );
00288                                 else
00289                                     if ( ln == "value"  )
00290                                         tag_stack.push( TAG_VALUE );
00291                                     else {
00292                                         log(Warning) << "Unrecognised XML tag :"<< ln <<": ignoring." << endlog();
00293                                         tag_stack.push( TAG_UNKNOWN );
00294                                     }
00295             }
00296 
00297             void characters( const char* chars )
00298             {
00299                 switch ( tag_stack.top() )
00300                 {
00301                     case TAG_DESCRIPTION:
00302                         description = chars;
00303                         break;
00304 
00305                     case TAG_VALUE:
00306                         value_string = chars;;
00307                         break;
00308                     case TAG_STRUCT:
00309                     case TAG_SIMPLE:
00310                     case TAG_SEQUENCE:
00311                     case TAG_PROPERTIES:
00312                     case TAG_UNKNOWN:
00313                         break;
00314                 }
00315             }
00316 
00317             bool populateBag(TiXmlNode* pParent)
00318             {
00319                 if ( !pParent )
00320                     return false;
00321 
00322                 TiXmlNode* pChild;
00323                 TiXmlText* pText;
00324                 int t = pParent->Type();
00325 
00326                 switch ( t )
00327                     {
00328                     case TiXmlNode::ELEMENT:
00329                         // notify start of new element
00330                         this->startElement( pParent->Value(), pParent->ToElement()->FirstAttribute() );
00331 
00332                         // recurse in children, if any
00333                         for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling())
00334                             {
00335                                 if ( this->populateBag( pChild ) == false)
00336                                     return false;
00337                             }
00338 
00339                         // notify end of element
00340                         if ( this->endElement() == false )
00341                             return false;
00342                         break;
00343 
00344                     case TiXmlNode::TEXT:
00345                         pText = pParent->ToText();
00346                         this->characters( pText->Value() );
00347                         break;
00348 
00349                         // not interested in these...
00350                     case TiXmlNode::DECLARATION:
00351                     case TiXmlNode::COMMENT:
00352                     case TiXmlNode::UNKNOWN:
00353                     case TiXmlNode::DOCUMENT:
00354                     default:
00355                         break;
00356                     }
00357             return true;
00358             }
00359         };
00360     }
00361 
00362     using namespace detail;
00363 
00364     class TinyDemarshaller::D {
00365     public:
00366         D(const std::string& f) : doc( f.c_str() ), loadOkay(false) {}
00367         TiXmlDocument doc;
00368         bool loadOkay;
00369     };
00370 
00371     TinyDemarshaller::TinyDemarshaller( const std::string& filename )
00372         : d( new TinyDemarshaller::D(filename) )
00373     {
00374         Logger::In in("TinyDemarshaller");
00375         d->loadOkay = d->doc.LoadFile();
00376 
00377         if ( !d->loadOkay ) {
00378             log(Error) << "Could not load " << filename << " Error: "<< d->doc.ErrorDesc() << endlog();
00379             return;
00380         }
00381 
00382     }
00383 
00384     TinyDemarshaller::~TinyDemarshaller()
00385     {
00386         delete d;
00387     }
00388 
00389     bool TinyDemarshaller::deserialize( PropertyBag &v )
00390     {
00391         Logger::In in("TinyDemarshaller");
00392 
00393         if ( !d->loadOkay )
00394             return false;
00395 
00396         TiXmlHandle docHandle( &d->doc );
00397         TiXmlHandle propHandle = docHandle.FirstChildElement( "properties" );
00398 
00399         if ( ! propHandle.Node() ) {
00400             log(Error) << "No <properties> element found in document!"<< endlog();
00401             return false;
00402         }
00403 
00404         detail::Tiny2CPFHandler proc( v );
00405 
00406         if ( proc.populateBag( propHandle.Node() ) == false) {
00407             deleteProperties( v );
00408             return false;
00409         }
00410         return true;
00411     }
00412 
00413 }
00414