openModeller
Version 1.4.0
|
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 << "<"; break; 00030 case '>': _stream << ">"; break; 00031 case '&': _stream << "&"; break; 00032 case '"': _stream << """; 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 }