openModeller  Version 1.5.0
Configuration_xmlio.cpp
Go to the documentation of this file.
1 
4 #include <openmodeller/Log.hh>
5 
6 #include <iostream>
7 #include <fstream>
8 #include <sstream>
9 #include <string>
10 #include <memory> // For auto_ptr
11 
12 using namespace std;
13 
14 #include <expat.h>
15 
16 #undef DEBUG_XML_PARSE
17 
18 /***********************************************************************************
19  *
20  * Write xml formatted configurations
21  *
22  **********************************************************************************/
23 
24 static void
25 escapeXml( const char *data, ostream &_stream )
26 {
27  while ( *data != 0 ) {
28  switch ( *data ) {
29  case '<': _stream << "&lt;"; break;
30  case '>': _stream << "&gt;"; break;
31  case '&': _stream << "&amp;"; break;
32  case '"': _stream << "&quot;"; break;
33  default: _stream << *data; break;
34  }
35  data += 1;
36  }
37 }
38 
39 static void
40 writeXmlStartTag( const string &name, Configuration::attribute_list nvlist, bool empty, ostream &_stream ) {
41 
42  _stream << endl << "<" << name;
43 
44  Configuration::attribute_list::const_iterator nvi = nvlist.begin();
45  if ( nvi != nvlist.end() ) {
46 
47  while ( nvi != nvlist.end() ) {
48  _stream << " " << nvi->first << "=\"";
49  escapeXml( nvi->second.c_str(), _stream );
50  _stream << "\"";
51  ++nvi;
52  }
53  }
54 
55  if ( empty )
56  _stream << "/";
57 
58  _stream << ">";
59 
60 }
61 
62 static void
63 writeXmlEndTag( const string &name, ostream &_stream ) {
64 
65  _stream << "</" << name << ">";
66 
67 }
68 
69 void
70 Configuration::writeXml( const ConstConfigurationPtr& config, ostream &_stream ) {
71 
72  Configuration::subsection_list subsects = config->getAllSubsections();
73 
74  string value = config->getValue();
75  bool empty = subsects.empty() && value.empty();
76 
77  writeXmlStartTag( config->getName(), config->getAllAttributes(), empty, _stream );
78 
79  if ( empty )
80  return;
81 
82  if ( ! value.empty() )
83  escapeXml( value.c_str(), _stream );
84 
85  for ( Configuration::subsection_list::const_iterator subsecti = subsects.begin();
86  subsecti != subsects.end();
87  subsecti++ ) {
88  writeXml( *subsecti, _stream );
89  }
90 
91  writeXmlEndTag( config->getName(), _stream );
92 }
93 
94 void
95 Configuration::writeXml( const ConstConfigurationPtr& config, char const *filename ) {
96 
97  ofstream file( filename );
98 
99  writeXml( config, file );
100 
101  file.close();
102 
103 }
104 
105 /***********************************************************************************
106  *
107  * Read xml formatted configurations
108  *
109  **********************************************************************************/
110 
111 class expatState {
112 
113 public:
114 
115  expatState( );
116  expatState( expatState* parent );
117  ~expatState();
118 
119  expatState* finalize( string name );
120 
121  void addChars( char const * chars, int len );
122 
124  return config;
125  }
126 
127  expatState* makeChild( string name, const char **attribs );
128 
129  void setName( string name );
130  void setAttributes( const char **attribs );
131 
132 private:
133 
134  string tmpChars;
135 
137 
139 
140 };
141 
143  tmpChars(),
144  parent (),
145  config( new ConfigurationImpl() )
146 {
147 #if defined(DEBUG_XML_PARSE)
148  cout << "expatState::expatState( ): " << this << endl;
149 #endif
150 }
151 
153  tmpChars(),
154  parent ( parent ),
155  config( new ConfigurationImpl() )
156 {
157 #if defined(DEBUG_XML_PARSE)
158  cout << "expatState::expatState( expatState* ): " << this << endl;
159 #endif
160 }
161 
163  // Don't do anything!
164  // The parser is free'd externally.
165  // The Strings take care of themselves
166  // The config needs to be controlled externally.
167 #if defined(DEBUG_XML_PARSE)
168  cout << "expatState::~expatState(): " << this << endl;
169 #endif
170 
171 }
172 
173 expatState* expatState::makeChild( string name, const char **attribs ) {
174 
175 #if defined(DEBUG_XML_PARSE)
176  cout << "expatState::makeChild(): " << this << endl;
177 #endif
178 
179  // Create our child
180  expatState *child = new expatState(this);
181 
182  // Attach it's configuration as our subsection.
183  this->config->addSubsection( child->config );
184 
185  // Set the child's name:
186  // Call the child's StartTag method by hand since expat won't do it for us.
187  child->setName( name );
188  child->setAttributes( attribs );
189 
190  return child;
191 }
192 
193 void expatState::setName( string name ) {
194 #if defined(DEBUG_XML_PARSE)
195  cout << "expatState::setName(): " << this << endl;
196  cout << " name: " << name << endl;
197 #endif
198  config->setName( name );
199 }
200 
201 void expatState::setAttributes( const char **attribs ) {
202 #if defined(DEBUG_XML_PARSE)
203  cout << "expatState::setAttributes(): " << this << endl;
204  cout << " attribs: " << attribs << endl;
205 #endif
206 
207  const char **name = attribs++;
208  const char **value = attribs;
209 
210  for( ; *name != NULL ; ) {
211 #if defined(DEBUG_XML_PARSE)
212  cout << " name: " << *name << endl;
213  cout << " value: " << *value << endl;
214 #endif
215  config->addNameValue( *name, *value );
216  name = value +1;
217  value = name +1;
218  }
219 
220 }
221 
223 #if defined(DEBUG_XML_PARSE)
224  cout << "expatState::finalize(): " << this << endl;
225 #endif
226 
227  // cout << " - condition 2" << endl;
228  // Since we don't have characters, that means we are comming out of a subsection.
229  config->setValue( tmpChars );
230 
231  expatState* returnVal = parent;
232  // Since this is the hook for returning from recursion,
233  // we need to set the expat user data to this.
234  if (parent) {
235  delete this;
236  }
237  return returnVal;
238 }
239 
240 void expatState::addChars( const XML_Char *chars, int len ) {
241 #if defined(DEBUG_XML_PARSE)
242  cout << "expatState::addChars(): " << this << endl;
243  cout << " prev = " << tmpChars <<endl;
244 #endif
245 
246  tmpChars.append( chars, len );
247 #if defined(DEBUG_XML_PARSE)
248  cout << " new = " << tmpChars <<endl;
249 #endif
250 }
251 
252 /***********************************************************************************
253  *
254  * expat handler functions.
255  *
256  **********************************************************************************/
257 
258 void endElementHandler( void *, const XML_Char * );
259 void startElementHandler( void *state, const XML_Char *name, const XML_Char **atts );
260 
261 void firstStartElementHandler( void * state, const XML_Char *name, const XML_Char **atts ) {
262 #if defined(DEBUG_XML_PARSE)
263  cout << "firstStartElementHandler: " << name << endl;
264 #endif
265  XML_Parser parser = (XML_Parser) state;
266  expatState *myState = (expatState*) XML_GetUserData(parser);
267  // The first start tag is handled differently from the rest.
268  // This one needs to modify the current state object
269  // rather than force the recursion.
270  myState->setName( name );
271  myState->setAttributes( atts );
272  XML_SetElementHandler( parser, startElementHandler, endElementHandler );
273 }
274 
275 void startElementHandler( void *state, const XML_Char *name, const XML_Char **atts ) {
276 #if defined(DEBUG_XML_PARSE)
277  cout << "startElementHandler: " << name << endl;
278 #endif
279  XML_Parser parser = (XML_Parser) state;
280  expatState *myState = (expatState*) XML_GetUserData(parser);
281  //
282  // At every start tag, we force a recursion.
283  //
284  expatState *childState = myState->makeChild( name, atts );
285  // Direct all expat handlers this the child.
286  XML_SetUserData( parser, (void *)childState );
287 
288 }
289 
290 void endElementHandler( void * state, const XML_Char *name ) {
291 #if defined(DEBUG_XML_PARSE)
292  cout << "endElementHandler: " << name << endl;
293 #endif
294  XML_Parser parser = (XML_Parser) state;
295  expatState *myState = (expatState*) XML_GetUserData(parser);
296 
297  expatState* parent = myState->finalize( name );
298  XML_SetUserData( parser, (void*)parent );
299 }
300 
301 void charHandler( void * state, const XML_Char *name, int len ) {
302 #if defined(DEBUG_XML_PARSE)
303  cout << "charHandler: " << endl;
304  cout << " chars: " << string( name, len ) << endl;
305 #endif
306  XML_Parser parser = (XML_Parser) state;
307  expatState *myState = (expatState*) XML_GetUserData(parser);
308  myState->addChars( name, len );
309 }
310 
311 /***********************************************************************************
312  *
313  * User routines
314  *
315  **********************************************************************************/
316 
318 Configuration::readXml( char const *filename ) {
319 
320  ifstream file( filename, ios::in );
321  ConfigurationPtr returnValue( readXml( file ) );
322  file.close();
323  return returnValue;
324 }
325 
327 public:
328  ExpatAutoPtr( XML_Parser p ) :
329  p(p)
330  {};
332  XML_ParserFree( p );
333  }
334  operator XML_Parser() {
335  return p;
336  }
337 private:
338  XML_Parser p;
339 };
340 
342 Configuration::readXml( istream &file ) {
343 
344  ExpatAutoPtr parser = XML_ParserCreate( NULL );
345 
346  // We need to have access to the raw pointer for the call into XML_SetUserData.
347  // it is wrapped in an auto_ptr later for security.
348  expatState *state = new expatState();
349 
350  XML_SetUserData( parser, state );
351 
352  // Now, let auto_ptr do it's trick for state.
353  auto_ptr<expatState> apstate( state );
354 
355  XML_UseParserAsHandlerArg( parser );
356  XML_SetElementHandler( parser, firstStartElementHandler, endElementHandler );
357  XML_SetCharacterDataHandler( parser, charHandler );
358 
359  const int BUFF_SIZE = 1024;
360 
361  // expat's parse loop.
362  Log::instance()->debug( "XML Parser at start of document\n" );
363  for (;;) {
364  int bytes_read =0;
365  char *buf = (char *)XML_GetBuffer( parser, BUFF_SIZE );
366  if ( buf == NULL ) {
367  // cout << " - no buf" << endl;
368  std::string msg = "Unable to allocate buffer during XML read";
369  Log::instance()->error( msg.c_str() );
370  throw XmlParseException( msg.c_str() );
371  }
372 
373  // iostream::read will read up to the specified number of characters
374  // it does not null terminate.
375  file.read( buf, BUFF_SIZE);
376 
377  // To find the number of characters read, call iostream::gcount().
378  bytes_read = file.gcount();
379 
380  // cout << "Bytes read: " << buf << endl;
381  // if ( bytes_read < 0 ) {
382  // cout << " - read" << endl;
383  // goto cleanup;
384  // }
385 
386  if ( !XML_ParseBuffer( parser, bytes_read, bytes_read == 0 ) ) {
387 
388  XML_Error x = XML_GetErrorCode( parser );
389  stringstream errormsg( ios::out );
390  errormsg << XML_ErrorString(x)
391  << " at Line "
392  << XML_GetCurrentLineNumber( parser )
393  << " column "
394  << XML_GetCurrentColumnNumber( parser )
395  << ends;
396 
397  Log::instance()->error( "XML Parser fatal error: %s\n", errormsg.str().c_str() );
398 
399  throw XmlParseException( errormsg.str() );
400  }
401 
402  if ( bytes_read == 0 ) {
403  Log::instance()->debug( "XML Parser reached end of document\n" );
404  break;
405  }
406 
407  }
408 
409  return state->getConfig();
410 
411 }
expatState * parent
static ConfigurationPtr readXml(char const *filename)
void firstStartElementHandler(void *state, const XML_Char *name, const XML_Char **atts)
std::vector< ConfigurationPtr > subsection_list
void setName(string name)
void charHandler(void *state, const XML_Char *name, int len)
ConfigurationPtr getConfig()
static Log * instance()
Returns the instance pointer, creating the object on the first call.
Definition: Log.cpp:45
static void writeXmlStartTag(const string &name, Configuration::attribute_list nvlist, bool empty, ostream &_stream)
expatState * makeChild(string name, const char **attribs)
static void escapeXml(const char *data, ostream &_stream)
static void writeXmlEndTag(const string &name, ostream &_stream)
void error(const char *format,...)
'Error' level.
Definition: Log.cpp:290
void setAttributes(const char **attribs)
void endElementHandler(void *, const XML_Char *)
static void writeXml(const ConstConfigurationPtr &config, char const *fileaname)
void startElementHandler(void *state, const XML_Char *name, const XML_Char **atts)
std::vector< attribute > attribute_list
expatState * finalize(string name)
void addChars(char const *chars, int len)
ExpatAutoPtr(XML_Parser p)
ConfigurationPtr config
void debug(const char *format,...)
'Debug' level.
Definition: Log.cpp:237