Orocos Real-Time Toolkit  2.5.0
CPFDemarshaller.cpp
00001 /***************************************************************************
00002   tag: Peter Soetens  Tue Apr 5 16:53:25 CEST 2005  CPFDemarshaller.cxx
00003 
00004                         CPFDemarshaller.cxx -  description
00005                            -------------------
00006     begin                : Tue April 05 2005
00007     copyright            : (C) 2005 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.ac.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 
00040 #include "CPFDemarshaller.hpp"
00041 #include "CPFDTD.hpp"
00042 
00043 #ifdef OROPKG_SUPPORT_XERCES_C
00044 #include <xercesc/util/PlatformUtils.hpp>
00045 #include <xercesc/util/TransService.hpp>
00046 #include <xercesc/sax2/SAX2XMLReader.hpp>
00047 #include <xercesc/sax2/XMLReaderFactory.hpp>
00048 #include <xercesc/sax2/DefaultHandler.hpp>
00049 #include <xercesc/sax2/Attributes.hpp>
00050 #include <xercesc/util/XMLUniDefs.hpp>
00051 #include <xercesc/util/BinMemInputStream.hpp>
00052 #include <xercesc/framework/LocalFileInputSource.hpp>
00053 #include <xercesc/framework/MemBufInputSource.hpp>
00054 #include <xercesc/validators/common/Grammar.hpp>
00055 
00056 #include <vector>
00057 #include <stack>
00058 #include <map>
00059 #include <string>
00060 #include <iostream>
00061 #include <cstdio>
00062 
00063 #include <Property.hpp>
00064 #include "../base/PropertyIntrospection.hpp"
00065 #include <Logger.hpp>
00066 
00067 namespace RTT
00068 {
00069 #ifdef XERCES_CPP_NAMESPACE
00070     using namespace XERCES_CPP_NAMESPACE;
00071 #endif
00072     using namespace marsh;
00073     using namespace base;
00074 
00075     inline bool XMLChToStdString(const XMLCh* const c, std::string& res)
00076     {
00077         char* chholder;
00078         chholder = XMLString::transcode( c );
00079         if ( chholder ) {
00080             res = chholder;
00081             XMLString::release( &chholder );
00082             return true;
00083         }
00084         log(Error) << "Could not transcode XMLCh* !" <<endlog();
00085         return false;
00086     }
00087 
00088     inline std::string  XMLgetString(const XMLCh* const c)
00089     {
00090         std::string res;
00091         char* chholder;
00092         chholder = XMLString::transcode( c );
00093         if ( chholder ) {
00094             res = chholder;
00095             XMLString::release( &chholder );
00096             return res;
00097         }
00098         log(Error) << "Could not transcode XMLCh* !" <<endlog();
00099         return res;
00100     }
00101 
00102     class SAX2CPFHandler : public DefaultHandler
00103     {
00104 
00108             PropertyBag &bag;
00109             std::stack< std::pair<PropertyBag*, Property<PropertyBag>*> > bag_stack;
00110 
00111             enum Tag { TAG_STRUCT, TAG_SIMPLE, TAG_SEQUENCE, TAG_PROPERTIES, TAG_DESCRIPTION, TAG_VALUE, TAG_UNKNOWN};
00112             std::stack<Tag> tag_stack;
00113 
00117             std::string name;
00118             std::string description;
00119             std::string type;
00120             std::string value_string;
00121 
00122         public:
00123 
00124             SAX2CPFHandler( PropertyBag &b ) : bag( b )
00125             {
00126                 Property<PropertyBag>* dummy = 0;
00127                 bag_stack.push(std::make_pair(&bag, dummy));
00128             }
00129 
00130             void endElement( const XMLCh* const uri,
00131                              const XMLCh* const localname,
00132                              const XMLCh* const qname )
00133             {
00134                 //char *ln = XMLString::transcode( localname );
00135 
00136 //                 if ( value_string.empty() ) {
00137 //                     Logger::log()<<Logger::Debug << "SAX2CPFHandler : Empty value for property."
00138 //                                  <<Logger::endl;
00139 //                 }
00140                 switch ( tag_stack.top() )
00141                 {
00142                     case TAG_SIMPLE:
00143                         if ( type == "boolean" )
00144                         {
00145                             if ( value_string == "1" || value_string == "true")
00146                                 bag_stack.top().first->add
00147                                 ( new Property<bool>( name, description, true ) );
00148                             else if ( value_string == "0" || value_string == "false" )
00149                                 bag_stack.top().first->add
00150                                 ( new Property<bool>( name, description, false ) );
00151                             else
00152                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00153                                                                " Value should contain '0' or '1', got '"+ value_string +"'.").c_str());
00154                         }
00155                         else if ( type == "char" ) {
00156                             if ( value_string.empty() )
00157                                 bag_stack.top().first->add( new Property<char>( name, description, '\0' ) );
00158                             else
00159                                 if ( value_string.length() != 1 )
00160                                     throw SAXException(std::string("Wrong value for property '"+type+"'." \
00161                                                                " Value should contain a single character, got '"+ value_string +"'.").c_str());
00162                                 else
00163                                     bag_stack.top().first->add( new Property<char>( name, description, value_string[0] ) );
00164                         }
00165                         else if ( type == "uchar" || type == "octet" ) {
00166                             if ( value_string.length() != 1 )
00167                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00168                                                                " Value should contain a single unsigned character, got '"+ value_string +"'.").c_str());
00169                             else
00170                                 bag_stack.top().first->add
00171                                     ( new Property<unsigned char>( name, description, value_string[0] ) );
00172                         }
00173                         else if ( type == "long" || type == "short")
00174                         {
00175                             int v;
00176                             if ( sscanf(value_string.c_str(), "%d", &v) == 1)
00177                                 bag_stack.top().first->add( new Property<int>( name, description, v ) );
00178                             else
00179                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00180                                                                " Value should contain an integer value, got '"+ value_string +"'.").c_str());
00181                         }
00182                         else if ( type == "ulong" || type == "ushort")
00183                         {
00184                             unsigned int v;
00185                             if ( sscanf(value_string.c_str(), "%u", &v) == 1)
00186                                 bag_stack.top().first->add( new Property<unsigned int>( name, description, v ) );
00187                             else
00188                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00189                                                                " Value should contain an integer value, got '"+ value_string +"'.").c_str());
00190                         }
00191                         else if ( type == "double")
00192                         {
00193                             double v;
00194                             if ( sscanf(value_string.c_str(), "%lf", &v) == 1 )
00195                                 bag_stack.top().first->add
00196                                     ( new Property<double>( name, description, v ) );
00197                             else
00198                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00199                                                                " Value should contain a double value, got '"+ value_string +"'.").c_str());
00200                         }
00201                         else if ( type == "float")
00202                         {
00203                             float v;
00204                             if ( sscanf(value_string.c_str(), "%f", &v) == 1 )
00205                                 bag_stack.top().first->add
00206                                     ( new Property<float>( name, description, v ) );
00207                             else
00208                                 throw SAXException(std::string("Wrong value for property '"+type+"'." \
00209                                                                " Value should contain a float value, got '"+ value_string +"'.").c_str());
00210                         }
00211                         else if ( type == "string")
00212                             bag_stack.top().first->add
00213                             ( new Property<std::string>( name, description, value_string ) );
00214                         tag_stack.pop();
00215                         value_string.clear(); // cleanup
00216                         description.clear();
00217                         name.clear();
00218                         break;
00219 
00220                     case TAG_SEQUENCE:
00221                     case TAG_STRUCT:
00222                         {
00223                             Property<PropertyBag>* prop = bag_stack.top().second;
00224                             bag_stack.pop();
00225                             bag_stack.top().first->add( prop );
00226                             //( new Property<PropertyBag>( pn, description, *pb ) );
00227                             //delete pb;
00228                             tag_stack.pop();
00229                             description.clear();
00230                             name.clear();
00231                             type.clear();
00232                         }
00233                         break;
00234 
00235                     case TAG_DESCRIPTION:
00236                         tag_stack.pop();
00237                         if ( tag_stack.top() == TAG_STRUCT ) {
00238                             // it is a description of a struct that ended
00239                             bag_stack.top().second->setDescription(description);
00240                             description.clear();
00241                         }
00242                         break;
00243                     case TAG_VALUE:
00244                     case TAG_PROPERTIES:
00245                     case TAG_UNKNOWN:
00246                         tag_stack.pop();
00247                         break;
00248 
00249                 }
00250             }
00251 
00252 
00253             void startElement( const XMLCh* const uri,
00254                                const XMLCh* const localname,
00255                                const XMLCh* const qname,
00256                                const Attributes& attributes )
00257             {
00258                 std::string ln;
00259                 XMLChToStdString( localname, ln );
00260 
00261                 if ( ln == "properties" )
00262                     tag_stack.push( TAG_PROPERTIES );
00263                 else
00264                     if ( ln == "simple" )
00265                     {
00266                         name.clear();
00267                         type.clear();
00268                         tag_stack.push( TAG_SIMPLE );
00269                         for (unsigned int ac = 0; ac < attributes.getLength(); ++ac)
00270                         {
00271                             std::string an;
00272                             XMLChToStdString( attributes.getLocalName(ac), an );
00273                             if ( an == "name")
00274                             {
00275                                 XMLChToStdString( attributes.getValue(ac), name);
00276                             }
00277                             else if ( an == "type")
00278                             {
00279                                 XMLChToStdString( attributes.getValue(ac), type);
00280                             }
00281                         }
00282                     }
00283                     else
00284                         if ( ln == "struct" || ln == "sequence")
00285                         {
00286                             name.clear();
00287                             type.clear();
00288 
00289                             for (unsigned int ac = 0; ac < attributes.getLength(); ++ac)
00290                             {
00291                                 std::string an;
00292                                 XMLChToStdString( attributes.getLocalName(ac), an );
00293                                 if ( an == "name")
00294                                 {
00295                                     XMLChToStdString( attributes.getValue(ac), name);
00296                                 }
00297                                 else if ( an == "type")
00298                                 {
00299                                     XMLChToStdString( attributes.getValue(ac), type);
00300                                 }
00301                             }
00302 
00303                             if ( ln == "struct" )
00304                                 tag_stack.push( TAG_STRUCT );
00305                             else {
00306                                 tag_stack.push( TAG_SEQUENCE );
00307                                 type = "Sequence"; // override
00308                             }
00309 
00310                             Property<PropertyBag> *prop;
00311                             prop = new Property<PropertyBag>(name,"",PropertyBag(type));
00312 
00313                             // take reference to bag itself !
00314                             bag_stack.push(std::make_pair( &(prop->value()), prop));
00315                         }
00316                         else
00317                                 if ( ln == "description")
00318                                     tag_stack.push( TAG_DESCRIPTION );
00319                                 else
00320                                     if ( ln == "value"  )
00321                                         tag_stack.push( TAG_VALUE );
00322                                     else {
00323                                         log(Warning) << "Unrecognised XML tag :"<< ln <<": ignoring." << endlog();
00324                                         tag_stack.push( TAG_UNKNOWN );
00325                                     }
00326             }
00327 
00328             void warning( const SAXParseException& exception )
00329             {
00330                 std::string warn;
00331                 XMLChToStdString( exception.getMessage(), warn);
00332                 Logger::log() << Logger::Warning << "SAX2CPFHandler Parsing: " << warn <<Logger::nl;
00333                 if ( exception.getPublicId() )
00334                     {
00335                         XMLChToStdString( exception.getPublicId(), warn);
00336                         Logger::log() << " At entity "<< warn <<Logger::nl;
00337                     }
00338                 Logger::log() << " Column "<< exception.getColumnNumber()<< " Line " <<exception.getLineNumber()<<Logger::endl;
00339                 // to not throw.
00340             }
00341 
00342             void error( const SAXParseException& exception )
00343             {
00344                 throw exception;
00345             }
00346             void fatalError( const SAXParseException& exception )
00347             {
00348                 throw exception;
00349             }
00350 #if XERCES_VERSION_MAJOR < 3
00351             void characters( const XMLCh* const chars, const unsigned int length )
00352 #else
00353             void characters( const XMLCh* const chars, const XMLSize_t length )
00354 #endif
00355             {
00356                 //char *ln = XMLString::transcode( chars );
00357                 switch ( tag_stack.top() )
00358                 {
00359                     case TAG_DESCRIPTION:
00360                         XMLChToStdString( chars, description);
00361                         break;
00362 
00363                     case TAG_VALUE:
00364                         XMLChToStdString( chars, value_string);
00365                         break;
00366                     case TAG_STRUCT:
00367                     case TAG_SIMPLE:
00368                     case TAG_SEQUENCE:
00369                     case TAG_PROPERTIES:
00370                     case TAG_UNKNOWN:
00371                         break;
00372                 }
00373             }
00374 
00375 
00376     };
00377 
00378     CPFDemarshaller::CPFDemarshaller( const std::string& filename )
00379         : name(0), fis(0)
00380     {
00381         Logger::In in("CPFDemarshaller");
00382         try
00383             {
00384                 XMLPlatformUtils::Initialize();
00385             }
00386         catch ( const XMLException & toCatch )
00387             {
00388                 std::string error;
00389                 XMLChToStdString(toCatch.getMessage(), error);
00390                 Logger::log() << Logger::Error << "XML Initialization : "
00391                               << error << Logger::endl;
00392             }
00393         catch ( ... )
00394             {
00395                 Logger::log() << Logger::Error << "XML Init: General System Exception !" << Logger::endl;
00396             }
00397 
00398         try {
00399             name =  XMLString::transcode( filename.c_str() );
00400             fis  = new LocalFileInputSource( name );
00401         }
00402         catch ( XMLException& xe )
00403             {
00404                 Logger::log() << Logger::Error << "Failed to open file " <<filename << Logger::endl;
00405                 Logger::log() << Logger::Error << xe.getMessage() << Logger::endl;
00406 
00407                 fis = 0;
00408             }
00409         catch ( ... )
00410             {
00411                 Logger::log() << Logger::Error << "Opening file: General System Exception !" << Logger::endl;
00412             }
00413         XMLString::release( &name );
00414     }
00415 
00416     CPFDemarshaller::~CPFDemarshaller()
00417     {
00418         delete fis;
00419         XMLPlatformUtils::Terminate();
00420     }
00421 
00422     bool CPFDemarshaller::deserialize( PropertyBag &v )
00423     {
00424         if ( fis == 0 )
00425             return false;
00426 
00427         SAX2XMLReader* parser = 0;
00428         try {
00429             XMLPlatformUtils::Initialize();
00430             parser = XMLReaderFactory::createXMLReader();
00431         }
00432         catch ( ... )
00433             {
00434                 Logger::log() << Logger::Error << "SAX2XMLReader System Exception !" << Logger::endl;
00435                 XMLPlatformUtils::Terminate();
00436                 return false;
00437             }
00438 
00439 
00440         Logger::In in("CPFDemarshaller");
00441 
00442         try
00443             {
00444                 SAX2CPFHandler handler( v );
00445                 parser->setContentHandler( &handler );
00446                 parser->setErrorHandler( &handler );
00447                 parser->setFeature( XMLUni::fgSAX2CoreValidation, false );
00448                 parser->setFeature( XMLUni::fgXercesValidationErrorAsFatal, false );
00449                 parser->setFeature( XMLUni::fgXercesContinueAfterFatalError, false );
00450 #if 0
00451                 parser->setFeature( XMLUni::fgXercesSchemaFullChecking, false );
00452                 parser->setFeature( XMLUni::fgXercesDynamic, true );
00453                 parser->setFeature( XMLUni::fgSAX2CoreNameSpaces, true );
00454                 parser->setFeature( XMLUni::fgSAX2CoreNameSpacePrefixes, true );
00455                 parser->setFeature( XMLUni::fgXercesSchema, false );
00456 #endif
00457                 // try to avoid loading the DTD mentioned in the xml file,
00458                 // and load our own grammer.
00459                 using namespace detail;
00460                 parser->setFeature( XMLUni::fgXercesLoadExternalDTD, false );
00461                 //parser->setDoValidation(true);
00462                 int length  = XMLString::stringLen(cpf_dtd);// string length
00463                 XMLByte* buffer = new XMLByte[length];
00464                 memcpy( buffer, cpf_dtd, length );
00465                 MemBufInputSource dtd(buffer, length, "internal_cpf.dtd");
00466                 parser->loadGrammar( dtd, Grammar::DTDGrammarType );
00467                 delete[] buffer;
00468 
00469                 parser->parse( *fis );
00470                 parser->getErrorCount();
00471                 delete parser;
00472                 XMLPlatformUtils::Terminate();
00473             }
00474         catch ( const XMLException & toCatch )
00475             {
00476                 Logger::log() << Logger::Error << "An XML parsing error occurred processing file " <<Logger::nl;
00477                 if ( toCatch.getSrcFile() ) {
00478                     Logger::log() <<  toCatch.getSrcFile() << " parsing line " << toCatch.getSrcLine()<<Logger::nl ;
00479                 }
00480                 Logger::log()  << XMLgetString(toCatch.getMessage()) <<Logger::endl;
00481                 delete parser;
00482                 XMLPlatformUtils::Terminate();
00483                 return false;
00484             }
00485         catch ( const SAXParseException & toCatch )
00486             {
00487                 Logger::log() << Logger::Error << "An XML SAX parsing error occurred processing file " <<Logger::nl;
00488                 Logger::log()  << XMLgetString(toCatch.getMessage()) <<Logger::endl;
00489                 if ( toCatch.getPublicId() )
00490                     {
00491                         Logger::log() << " At entity "<< XMLgetString(toCatch.getPublicId()) <<Logger::nl;
00492                     }
00493                 Logger::log() << " Column "<< toCatch.getColumnNumber()<< " Line " <<toCatch.getLineNumber()<<Logger::endl;
00494                 delete parser;
00495                 XMLPlatformUtils::Terminate();
00496                 return false;
00497             }
00498         catch ( const SAXException & toCatch )
00499             {
00500                 Logger::log() << Logger::Error << "An XML SAX exception occurred processing file " <<Logger::nl;
00501                 Logger::log()  << XMLgetString(toCatch.getMessage()) <<Logger::endl;
00502                 delete parser;
00503                 XMLPlatformUtils::Terminate();
00504                 return false;
00505             }
00506         catch ( ... )
00507             {
00508                 Logger::log() << Logger::Error << "General System Exception !" << Logger::endl;
00509                 delete parser;
00510                 XMLPlatformUtils::Terminate();
00511                 return false;
00512             }
00513         return true;
00514     }
00515 }
00516 
00517 #endif
00518