openModeller  Version 1.4.0
Configuration_xmlio.cpp
Go to the documentation of this file.
00001 
00002 #include <openmodeller/Configuration.hh>
00003 #include <openmodeller/Exceptions.hh>
00004 #include <openmodeller/Log.hh>
00005 
00006 #include <iostream>
00007 #include <fstream>
00008 #include <sstream>
00009 #include <string>
00010 #include <memory>  // For auto_ptr
00011 
00012 using namespace std;
00013 
00014 #include <expat.h>
00015 
00016 #undef DEBUG_XML_PARSE
00017 
00018 /***********************************************************************************
00019  *
00020  * Write xml formatted configurations
00021  *
00022  **********************************************************************************/
00023 
00024 static void 
00025 escapeXml( const char *data, ostream &_stream )
00026 {
00027   while ( *data != 0 ) {
00028     switch ( *data ) {
00029       case '<': _stream << "&lt;"; break;
00030       case '>': _stream << "&gt;"; break;
00031       case '&': _stream << "&amp;"; break;
00032       case '"': _stream << "&quot;"; break;
00033       default: _stream << *data; break;
00034     }
00035     data += 1;
00036   }
00037 }
00038 
00039 static void
00040 writeXmlStartTag( const string &name, Configuration::attribute_list nvlist, bool empty, ostream &_stream ) {
00041 
00042     _stream << endl << "<" << name;
00043 
00044   Configuration::attribute_list::const_iterator nvi = nvlist.begin();
00045   if ( nvi != nvlist.end() ) {
00046 
00047     while ( nvi != nvlist.end() ) {
00048       _stream << " " << nvi->first << "=\"";
00049       escapeXml( nvi->second.c_str(), _stream );
00050       _stream << "\"";
00051       ++nvi;
00052     }
00053   }
00054 
00055   if ( empty )
00056     _stream << "/";
00057 
00058   _stream << ">";
00059 
00060 }
00061 
00062 static void
00063 writeXmlEndTag( const string &name, ostream &_stream ) {
00064 
00065   _stream << "</" << name << ">";
00066 
00067 }
00068 
00069 void
00070 Configuration::writeXml( const ConstConfigurationPtr& config, ostream &_stream ) {
00071 
00072   Configuration::subsection_list subsects = config->getAllSubsections();
00073 
00074   string value = config->getValue();
00075   bool empty = subsects.empty() && value.empty();
00076 
00077   writeXmlStartTag( config->getName(), config->getAllAttributes(), empty, _stream );
00078 
00079   if ( empty )
00080     return;
00081 
00082   if ( ! value.empty() )
00083     escapeXml( value.c_str(), _stream );
00084 
00085   for ( Configuration::subsection_list::const_iterator subsecti = subsects.begin();
00086     subsecti != subsects.end();
00087     subsecti++ ) {
00088     writeXml( *subsecti, _stream );
00089   }
00090 
00091   writeXmlEndTag( config->getName(), _stream );
00092 }
00093 
00094 void
00095 Configuration::writeXml( const ConstConfigurationPtr& config, char const *filename ) {
00096 
00097   ofstream file( filename );
00098 
00099   writeXml( config, file );
00100 
00101   file.close();
00102 
00103 }
00104 
00105 /***********************************************************************************
00106  *
00107  * Read xml formatted configurations
00108  *
00109  **********************************************************************************/
00110 
00111 class expatState {
00112 
00113 public:
00114 
00115   expatState( );
00116   expatState( expatState* parent );
00117   ~expatState();
00118 
00119   expatState* finalize( string name );
00120 
00121   void addChars( char const * chars, int len );
00122 
00123   ConfigurationPtr getConfig() {
00124     return config;
00125   }
00126 
00127   expatState* makeChild( string name, const char **attribs );
00128 
00129   void setName( string name );
00130   void setAttributes( const char **attribs );
00131 
00132 private:
00133 
00134   string tmpChars;
00135 
00136   expatState *parent;
00137 
00138   ConfigurationPtr config;
00139 
00140 };
00141 
00142 expatState::expatState( ) :
00143   tmpChars(),
00144   parent (),
00145   config( new ConfigurationImpl() )
00146 {
00147 #if defined(DEBUG_XML_PARSE)
00148   cout << "expatState::expatState( ): " << this << endl;
00149 #endif
00150 }
00151 
00152 expatState::expatState( expatState* parent) :
00153   tmpChars(),
00154   parent ( parent ),
00155   config( new ConfigurationImpl() )
00156 {
00157 #if defined(DEBUG_XML_PARSE)
00158   cout << "expatState::expatState( expatState* ): " << this << endl;
00159 #endif
00160 }
00161 
00162 expatState::~expatState() {
00163   // Don't do anything!
00164   // The parser is free'd externally.
00165   // The Strings take care of themselves
00166   // The config needs to be controlled externally.
00167 #if defined(DEBUG_XML_PARSE)
00168   cout << "expatState::~expatState(): " << this << endl;
00169 #endif
00170 
00171 }
00172 
00173 expatState* expatState::makeChild( string name, const char **attribs ) {
00174 
00175 #if defined(DEBUG_XML_PARSE)
00176   cout << "expatState::makeChild(): " << this << endl;
00177 #endif
00178 
00179   // Create our child
00180   expatState *child = new expatState(this);
00181 
00182   // Attach it's configuration as our subsection.
00183   this->config->addSubsection( child->config );
00184 
00185   // Set the child's name:
00186   // Call the child's StartTag method by hand since expat won't do it for us.
00187   child->setName( name );
00188   child->setAttributes( attribs );
00189 
00190   return child;
00191 }
00192 
00193 void expatState::setName( string name ) {
00194 #if defined(DEBUG_XML_PARSE)
00195   cout << "expatState::setName(): " << this << endl;
00196   cout << "  name: " << name << endl;
00197 #endif
00198   config->setName( name );
00199 }
00200 
00201 void expatState::setAttributes( const char **attribs ) {
00202 #if defined(DEBUG_XML_PARSE)
00203   cout << "expatState::setAttributes(): " << this << endl;
00204   cout << "  attribs: " << attribs << endl;
00205 #endif
00206 
00207   const char **name = attribs++;
00208   const char **value = attribs;
00209 
00210   for( ; *name != NULL ;  ) {
00211 #if defined(DEBUG_XML_PARSE)
00212     cout << "  name: " << *name << endl;
00213     cout << "  value: " << *value << endl;
00214 #endif
00215     config->addNameValue( *name, *value );
00216     name = value +1;
00217     value = name +1;
00218   }
00219 
00220 }
00221 
00222 expatState* expatState::finalize( string name ) {
00223 #if defined(DEBUG_XML_PARSE)
00224   cout << "expatState::finalize(): " << this << endl;
00225 #endif
00226 
00227   //    cout << " - condition 2" << endl;
00228   // Since we don't have characters, that means we are comming out of a subsection.
00229   config->setValue( tmpChars );
00230 
00231   expatState* returnVal = parent;
00232   // Since this is the hook for returning from recursion,
00233   // we need to set the expat user data to this.
00234   if (parent) {
00235     delete this;
00236   }
00237   return returnVal;
00238 }
00239 
00240 void expatState::addChars( const XML_Char *chars, int len ) {
00241 #if defined(DEBUG_XML_PARSE)
00242   cout << "expatState::addChars(): " << this << endl;
00243   cout << "  prev = " << tmpChars <<endl;
00244 #endif
00245 
00246   tmpChars.append(  chars, len );
00247 #if defined(DEBUG_XML_PARSE)
00248   cout << "  new  = " << tmpChars <<endl;
00249 #endif
00250 }
00251 
00252 /***********************************************************************************
00253  *
00254  * expat handler functions.
00255  *
00256  **********************************************************************************/
00257 
00258 void endElementHandler( void *, const XML_Char * );
00259 void startElementHandler( void *state, const XML_Char *name, const XML_Char **atts );
00260 
00261 void firstStartElementHandler( void * state, const XML_Char *name, const XML_Char **atts ) {
00262 #if defined(DEBUG_XML_PARSE)
00263   cout << "firstStartElementHandler: " << name << endl;
00264 #endif
00265   XML_Parser parser = (XML_Parser) state;
00266   expatState *myState = (expatState*) XML_GetUserData(parser);
00267   // The first start tag is handled differently from the rest.
00268   // This one needs to modify the current state object
00269   // rather than force the recursion.
00270   myState->setName( name );
00271   myState->setAttributes( atts );
00272   XML_SetElementHandler( parser, startElementHandler, endElementHandler );
00273 }
00274 
00275 void startElementHandler( void *state, const XML_Char *name, const XML_Char **atts ) {
00276 #if defined(DEBUG_XML_PARSE)
00277   cout << "startElementHandler: " << name << endl;
00278 #endif
00279   XML_Parser parser = (XML_Parser) state;
00280   expatState *myState = (expatState*) XML_GetUserData(parser);
00281   //
00282   // At every start tag, we force a recursion.
00283   //
00284   expatState *childState = myState->makeChild( name, atts );
00285   // Direct all expat handlers this the child.
00286   XML_SetUserData( parser, (void *)childState );
00287 
00288 }
00289 
00290 void endElementHandler( void * state, const XML_Char *name ) {
00291 #if defined(DEBUG_XML_PARSE)
00292   cout << "endElementHandler: " << name << endl;
00293 #endif
00294   XML_Parser parser = (XML_Parser) state;
00295   expatState *myState = (expatState*) XML_GetUserData(parser);
00296 
00297   expatState* parent = myState->finalize( name );
00298   XML_SetUserData( parser, (void*)parent );
00299 }
00300 
00301 void charHandler( void * state, const XML_Char *name, int len ) {
00302 #if defined(DEBUG_XML_PARSE)
00303   cout << "charHandler: " << endl;
00304   cout << "  chars: " << string( name, len ) << endl;
00305 #endif
00306   XML_Parser parser = (XML_Parser) state;
00307   expatState *myState = (expatState*) XML_GetUserData(parser);
00308   myState->addChars( name, len );
00309 }
00310 
00311 /***********************************************************************************
00312  *
00313  * User routines
00314  *
00315  **********************************************************************************/
00316 
00317 ConfigurationPtr
00318 Configuration::readXml( char const *filename ) {
00319 
00320   ifstream file( filename, ios::in );
00321   ConfigurationPtr returnValue( readXml( file ) );
00322   file.close();
00323   return returnValue;
00324 }
00325 
00326 class ExpatAutoPtr {
00327 public:
00328   ExpatAutoPtr( XML_Parser p ) :
00329     p(p)
00330   {};
00331   ~ExpatAutoPtr() {
00332     XML_ParserFree( p );
00333   }
00334   operator XML_Parser() {
00335     return p;
00336   }
00337 private:
00338   XML_Parser p;
00339 };
00340 
00341 ConfigurationPtr
00342 Configuration::readXml( istream &file ) {
00343 
00344   ExpatAutoPtr parser = XML_ParserCreate( NULL );
00345 
00346   // We need to have access to the raw pointer for the call into XML_SetUserData.
00347   // it is wrapped in an auto_ptr later for security.
00348   expatState *state = new expatState();
00349  
00350   XML_SetUserData( parser, state );
00351 
00352   // Now, let auto_ptr do it's trick for state.
00353   auto_ptr<expatState> apstate( state );
00354 
00355   XML_UseParserAsHandlerArg( parser );
00356   XML_SetElementHandler( parser, firstStartElementHandler, endElementHandler );
00357   XML_SetCharacterDataHandler( parser, charHandler );
00358 
00359   const int BUFF_SIZE = 1024;
00360 
00361   // expat's parse loop.
00362   Log::instance()->debug( "XML Parser at start of document\n" );
00363   for (;;) {
00364     int bytes_read =0;
00365     char *buf = (char *)XML_GetBuffer( parser, BUFF_SIZE );
00366     if ( buf == NULL ) {
00367       //      cout << " - no buf" << endl;
00368       std::string msg = "Unable to allocate buffer during XML read";
00369       Log::instance()->error( msg.c_str() );
00370       throw XmlParseException( msg.c_str() );
00371     }
00372 
00373     // iostream::read will read up to the specified number of characters
00374     // it does not null terminate.
00375     file.read( buf, BUFF_SIZE);
00376 
00377     // To find the number of characters read, call iostream::gcount().
00378     bytes_read = file.gcount();
00379 
00380     //    cout << "Bytes read: " << buf << endl;
00381     //    if ( bytes_read < 0 ) {
00382     //      cout << " - read" << endl;
00383     //      goto cleanup;
00384     //    }
00385 
00386     if ( !XML_ParseBuffer( parser, bytes_read, bytes_read == 0 ) ) {
00387 
00388       XML_Error x =  XML_GetErrorCode( parser );
00389       stringstream errormsg( ios::out );
00390       errormsg << XML_ErrorString(x)
00391          << " at Line "
00392          << XML_GetCurrentLineNumber( parser )
00393          << " column "
00394          << XML_GetCurrentColumnNumber( parser )
00395          << ends;
00396 
00397       Log::instance()->error( "XML Parser fatal error: %s\n", errormsg.str().c_str() );
00398 
00399       throw XmlParseException( errormsg.str() );
00400     }
00401 
00402     if ( bytes_read == 0 ) {
00403       Log::instance()->debug( "XML Parser reached end of document\n" );
00404       break;
00405     }
00406 
00407   }
00408   
00409   return state->getConfig();
00410 
00411 }