Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
XMLDocument.cpp
Go to the documentation of this file.
1 #include "XMLDocument.h"
2 
3 #include <assert.h>
4 #include <fstream>
5 #include <expat.h>
6 #include <algorithm>
7 
8 #define PARSING_BUFFER_SIZE 4096
9 
10 namespace XMLDOM
11 {
12 /******************************************************************
13 * *
14 * *
15 * XMLElement implementation *
16 * *
17 * *
18 ******************************************************************/
19 
21  mType( XET_CDATA )
22  , mAttributes()
23  , mParent( 0 )
24  , mDocument( 0 )
25  , mIdAttribute( mAttributes.end() )
26  , mNameAttribute( mAttributes.end() )
27 {}
28 
29 XMLElement::XMLElement( const std::string &cdata ) :
30  mType( XET_CDATA )
31  , mContents( cdata )
32  , mAttributes()
33  , mParent( 0 )
34  , mDocument( 0 )
35  , mIdAttribute( mAttributes.end() )
36  , mNameAttribute( mAttributes.end() )
37 {}
38 
39 XMLElement::XMLElement( Type type, const std::string &data ) :
40  mType( type )
41  , mContents( (type == XET_CDATA || type == XET_COMMENT) ? data : std::string() ),
42  mAttributes(),
43  mParent( 0 ),
44  mDocument( 0 ),
45  mIdAttribute( mAttributes.end() ),
46  mNameAttribute( mAttributes.end() )
47 {}
48 
49 XMLElement::XMLElement( const char *tagName, const char*const *attrValuePairList, unsigned int nAttr ) :
50  mType( XET_TAG )
51  , mTagName( tagName )
52  , mAttributes()
53  , mParent( 0 )
54  , mDocument( 0 )
55  , mIdAttribute( mAttributes.end() )
56  , mNameAttribute( mAttributes.end() )
57 {
58  for (; (nAttr >= 2 && attrValuePairList[0] && attrValuePairList[1]); attrValuePairList += 2)
59  setAttribute( attrValuePairList[0], attrValuePairList[1] );
60 }
61 
62 XMLElement::XMLElement( const char *tagName, const char*const *attrValuePairList ) :
63  mType( XET_TAG )
64  , mTagName( tagName )
65  , mAttributes()
66  , mParent( 0 )
67  , mDocument( 0 )
68  , mIdAttribute( mAttributes.end() )
69  , mNameAttribute( mAttributes.end() )
70 {
71  for (; (attrValuePairList[0] && attrValuePairList[1]); attrValuePairList += 2)
72  setAttribute( attrValuePairList[0], attrValuePairList[1] );
73 }
74 
75 XMLElement::XMLElement( const std::string &tagName, const std::vector< std::string > &attrValuePairList ) :
76  mType( XET_TAG )
77  , mTagName( tagName )
78  , mAttributes()
79  , mParent( 0 )
80  , mDocument( 0 )
81  , mIdAttribute( mAttributes.end() )
82  , mNameAttribute( mAttributes.end() )
83 {
84  for (std::vector< std::string >::size_type i = 0; i+1 < attrValuePairList.size(); i += 2)
85  setAttribute( attrValuePairList[i], attrValuePairList[i+1] );
86 }
87 
88 XMLElement::XMLElement( const std::string &tagName, const std::map< std::string, std::string > &attrValuePairList ) :
89  mType( XET_TAG )
90  , mTagName( tagName )
91  , mAttributes( attrValuePairList )
92  , mParent( 0 )
93  , mDocument( 0 )
94  , mIdAttribute( mAttributes.find( "id" ) )
95  , mNameAttribute( mAttributes.find( "name" ) )
96 {}
97 
99 {
100  clear();
101  mParent = 0;
102  mDocument = 0;
103 }
104 
105 void XMLElement::clear( bool doAttributes )
106 {
107  //Remove all - quickly
108  for (child_iterator cit = childrenBegin(); cit != childrenEnd(); ++cit)
109  delete *cit;
110  mChildren.clear();
111  mById.clear();
112  mByName.clear();
113  if (doAttributes)
114  mAttributes.clear();
115  mIdAttribute = mNameAttribute = mAttributes.end();
116 }
117 
118 const std::string& XMLElement::contents() const
119 {
120  static std::string empty;
121  switch ( type() )
122  {
123  case XET_CDATA
124  : case XET_COMMENT:
125  return mContents;
126 
127  case XET_TAG:
128  case XET_ROOT:
129  if (numChildren() > 0 && getChild( 0 )->type() == XET_CDATA)
130  return getChild( 0 )->contents();
131 
132  else
133  return empty;
134  default:
135  return empty;
136  }
137 }
138 
139 std::string& XMLElement::contents()
140 {
141  assert( type() == XET_CDATA || type() == XET_COMMENT );
142  //Dirtify document
143  if (mDocument)
144  mDocument->dirty = true;
145  return mContents;
146 }
147 
148 const XMLElement* XMLElement::getChildById( const std::string &id ) const
149 {
150  ElementMap::const_iterator cit = mById.find( id );
151  if ( cit == mById.end() )
152  return 0;
153 
154  else
155  return cit->second;
156 }
157 
158 XMLElement* XMLElement::getChildById( const std::string &id )
159 {
160  ElementMap::iterator cit = mById.find( id );
161  if ( cit == mById.end() )
162  return 0;
163 
164  else
165  return cit->second;
166 }
167 
168 const XMLElement* XMLElement::getChildByName( const std::string &name ) const
169 {
170  ElementMap::const_iterator cit = mByName.find( name );
171  if ( cit == mByName.end() )
172  return 0;
173 
174  else
175  return cit->second;
176 }
177 
178 XMLElement* XMLElement::getChildByName( const std::string &name )
179 {
180  ElementMap::iterator cit = mByName.find( name );
181  if ( cit == mByName.end() )
182  return 0;
183 
184  else
185  return cit->second;
186 }
187 
188 unsigned int XMLElement::appendChild( XMLElement *newElem )
189 {
190  //Dirtify document
191  if (mDocument)
192  mDocument->dirty = true;
193  mChildren.push_back( newElem );
194  newElem->setParent( this );
195  newElem->setDocument( mDocument );
196  return mChildren.size()-1;
197 }
198 
199 void XMLElement::removeChild( unsigned int idx )
200 {
201  if (idx < mChildren.size() && mChildren[idx]) {
202  //Dirtify document
203  if (mDocument)
204  mDocument->dirty = true;
205  delete mChildren[idx];
206  mChildren.erase( mChildren.begin()+idx );
207  }
208 }
209 
211 {
212  removeChild( std::find( mChildren.begin(), mChildren.end(), which )-mChildren.begin() );
213 }
214 
215 void XMLElement::removeAttribute( const std::string &name )
216 {
217  mAttributes.erase( name );
218 }
219 
220 void XMLElement::setAttribute( const std::string &name, const std::string &value )
221 {
222  static std::string _id( "id" );
223  static std::string _name( "name" );
224  //Dirtify document
225  if (mDocument)
226  mDocument->dirty = true;
227  std::pair< attribute_iterator, bool >rv =
228  mAttributes.insert( std::pair< std::string, std::string > ( name, value ) );
229  if (rv.second) {
230  if (name == _id)
231  mIdAttribute = rv.first;
232 
233  else if (name == _name)
234  mNameAttribute = rv.first;
235  }
236 }
237 
238 void XMLElement::appendContents( const std::string &cont )
239 {
240  switch ( type() )
241  {
242  case XET_CDATA:
243  case XET_COMMENT:
244  //Dirtify document
245  if (mDocument)
246  mDocument->dirty = true;
247  mContents += cont;
248 
249  break;
250  case XET_TAG:
251  case XET_ROOT:
252  if (numChildren() == 0 || getChild( numChildren()-1 )->type() != XET_CDATA)
253  appendChild( new XMLElement( cont ) );
254 
255  else
256  getChild( numChildren()-1 )->appendContents( cont );
257  break;
258  }
259 }
260 
262 {
263  mParent = parent;
264 }
265 
267 {
268  mDocument = document;
269  for (child_iterator cit = childrenBegin(); cit != childrenEnd(); ++cit)
270  (*cit)->setDocument( document );
271 }
272 
273 void XMLElement::setType( Type newType )
274 {
275  if ( newType != type() ) {
276  if (mParent) {
277  //Harder
278  //Remove children by hand - to allow them to deregister themselves.
279  while ( numChildren() )
280  removeChild( numChildren()-1 );
281  removeAttribute( "id" );
282  removeAttribute( "name" );
283  mAttributes.clear();
284  } else {
285  //Faster
286  clear();
287  }
288  mType = newType;
289  }
290 }
291 
292 void XMLElement::JoinMaps( ElementMap &dst, const ElementMap &src )
293 {
294  ElementMap::iterator dit = dst.begin();
295  ElementMap::const_iterator sit = src.begin();
296  while ( sit != src.end() ) {
297  dit = dst.insert( dit, *sit );
298  ++sit;
299  }
300 }
301 
302 void XMLElement::rebuildNamedBindings( bool deepScan )
303 {
304  mById.clear();
305  mByName.clear();
306  for (child_iterator it = childrenBegin(); it != childrenEnd(); ++it) {
307  (*it)->rebuildNamedBindings( deepScan );
308  if (deepScan) {
309  JoinMaps( mById, (*it)->mById );
310  JoinMaps( mByName, (*it)->mByName );
311  }
312  const_attribute_iterator iit = (*it)->mIdAttribute;
313  if ( iit != (*it)->attributesEnd() )
314  mById.insert( std::pair< std::string, XMLElement* > ( iit->second, *it ) );
315  const_attribute_iterator nit = (*it)->mNameAttribute;
316  if ( nit != (*it)->attributesEnd() )
317  mByName.insert( std::pair< std::string, XMLElement* > ( nit->second, *it ) );
318  }
319 }
320 
321 const XMLElement* XMLElement::getChildByHierarchicalId( const std::string &id ) const
322 {
323  std::string::size_type sep = id.find( '/' );
324  if (sep != std::string::npos) {
325  const XMLElement *sub = getChildById( id.substr( 0, sep ) );
326  return sub ? sub->getChildByHierarchicalId( id.substr( sep+1 ) ) : 0;
327  } else {
328  return getChildById( id );
329  }
330 }
331 
333 {
334  std::string::size_type sep = id.find( '/' );
335  if (sep != std::string::npos) {
336  XMLElement *sub = getChildById( id.substr( 0, sep ) );
337  return sub ? sub->getChildByHierarchicalId( id.substr( sep+1 ) ) : 0;
338  } else {
339  return getChildById( id );
340  }
341 }
342 
343 const XMLElement* XMLElement::getChildByHierarchicalName( const std::string &name ) const
344 {
345  std::string::size_type sep = name.find( '/' );
346  if (sep != std::string::npos) {
347  const XMLElement *sub = getChildByName( name.substr( 0, sep ) );
348  return sub ? sub->getChildByHierarchicalName( name.substr( sep+1 ) ) : 0;
349  } else {
350  return getChildByName( name );
351  }
352 }
353 
355 {
356  std::string::size_type sep = name.find( '/' );
357  if (sep != std::string::npos) {
358  XMLElement *sub = getChildByName( name.substr( 0, sep ) );
359  return sub ? sub->getChildByHierarchicalName( name.substr( sep+1 ) ) : 0;
360  } else {
361  return getChildByName( name );
362  }
363 }
364 
365 /******************************************************************
366 * *
367 * *
368 * XMLParser implementation *
369 * *
370 * *
371 ******************************************************************/
372 
374 {
375  typedef std::map< std::string, XMLProcessor* >ProcessorMap;
376 
381 
382  XML_Parser parser;
383 };
384 
385 namespace ExpatHandlers
386 {
387 /******************************************************************
388 * *
389 * XMLSerializer implementation *
390 * *
391 * (ExpatHandlers) *
392 * *
393 ******************************************************************/
394 
395 static void Doctype( void *userData,
396  const XML_Char *doctypeName,
397  const XML_Char *sysid,
398  const XML_Char *pubid,
399  int has_internal_subset )
400 {
401  XMLParserContext *internals = (XMLParserContext*) userData;
402  assert( internals );
403  assert( internals->document );
404  internals->document->docType = doctypeName;
405  internals->document->sysId = sysid;
406  internals->document->pubId = pubid;
407 }
408 
409 /* atts is array of name/value pairs, terminated by 0;
410  * names and values are 0 terminated. */
411 static void StartElement( void *userData, const XML_Char *name, const XML_Char **atts )
412 {
413  XMLParserContext *internals = (XMLParserContext*) userData;
414  assert( internals );
415  assert( internals->current );
416  internals->current =
417  internals->current->getChild(
418  internals->current->appendChild(
419  new XMLElement(
420  (const char*) name,
421  (const char*const*) atts
422  )
423  )
424  );
425 }
426 
427 static void EndElement( void *userData, const XML_Char *name )
428 {
429  XMLParserContext *internals = (XMLParserContext*) userData;
430  assert( internals );
431  assert( internals->current );
432  internals->current = internals->current->getParent();
433 }
434 
435 /* s is not 0 terminated. */
436 static void CData( void *userData, const XML_Char *s, int len )
437 {
438  XMLParserContext *internals = (XMLParserContext*) userData;
439  assert( internals );
440  assert( internals->current );
441  assert( internals->xparser );
442  if ( !(internals->xparser->options&XMLSerializer::OPT_WANT_CDATA) )
443  return;
444  internals->current->appendContents( std::string( s, len ) );
445 }
446 
447 /* target and data are 0 terminated */
448 static void PI( void *userData, const XML_Char *target, const XML_Char *data )
449 {
450  XMLParserContext *internals = (XMLParserContext*) userData;
451  assert( internals );
452  XMLParserContext::ProcessorMap::const_iterator it = internals->processors.find( std::string( target ) );
453  if ( it != internals->processors.end() ) {
454  it->second->execute(
455  internals->xparser,
456  internals->document,
457  internals->current,
458  std::string( data ) );
459  }
460 }
461 
462 static void Comment( void *userData, const XML_Char *data )
463 {
464  XMLParserContext *internals = (XMLParserContext*) userData;
465  assert( internals );
466  assert( internals->current );
467  assert( internals->xparser );
468  if ( !(internals->xparser->options&XMLSerializer::OPT_WANT_COMMENTS) )
469  return;
470  internals->current->appendChild( new XMLElement( XMLElement::XET_COMMENT, std::string( data ) ) );
471 }
472 };
473 
474 /******************************************************************
475 * *
476 * XMLSerializer implementation *
477 * *
478 * (member functions) *
479 * *
480 ******************************************************************/
481 
482 XMLSerializer::XMLSerializer( const char *encoding, XMLDocument *doc, XMLElement *elem ) :
483  options( OPT_DEFAULT )
484  , internals( 0 )
485 {
486  initialise( encoding, doc, elem );
487 }
488 
490 {
491  delete close();
492 }
493 
494 bool XMLSerializer::parse( const void *buf, unsigned int len )
495 {
496  XMLParserContext *ctxt = (XMLParserContext*) internals;
497  return XML_Parse( ctxt->parser, (const XML_Char*) buf, len, false ) != 0;
498 }
499 
500 bool XMLSerializer::importXML( const std::string &path )
501 {
502  std::ifstream fi( path.c_str() );
503  if ( fi.fail() ) {
504  return false;
505  } else {
506  bool bok = true;
507  while ( bok && !fi.fail() && !fi.eof() ) {
509  fi.read( buffer, sizeof (buffer) );
510  bok = parse( buffer, fi.gcount() );
511  }
512  fi.close();
513  return bok;
514  }
515 }
516 
517 bool _exportXML( std::ostream &stream, XMLElement *elem )
518 {
519  if (!elem)
520  return false;
522  unsigned int i;
523  //Prolog
524  switch ( elem->type() )
525  {
526  case XMLElement::XET_TAG:
527  stream<<"<"<<elem->tagName().c_str();
528  for (ait = elem->attributesBegin(); ait != elem->attributesEnd(); ++ait)
529  if (ait->second.find( "\"" ) != std::string::npos)
530  stream<<" "<<ait->first.c_str()<<"=\'"<<ait->second.c_str()<<"\'";
531 
532  else
533  stream<<" "<<ait->first.c_str()<<"=\""<<ait->second.c_str()<<"\"";
534  if (elem->numChildren() > 0)
535  stream<<">";
536 
537  else
538  stream<<"/>";
539  break;
541  //To-do: Find out if it needs to be enveloped within '<![CDATA['/']]>' constructs.
542  stream<<elem->contents().c_str();
543  return !stream.fail(); //No children
544 
546  stream<<"<!--"<<elem->contents().c_str()<<"-->";
547  return !stream.fail(); //No children
548 
550  //WTF?
551  return true;
552  }
553  if ( stream.fail() )
554  return false;
555  //Children
556  for (i = 0; i < elem->numChildren(); ++i)
557  if ( !_exportXML( stream, elem->getChild( i ) ) )
558  return false;
559  if ( stream.fail() )
560  return false;
561  //Epilog
562  switch ( elem->type() )
563  {
564  case XMLElement::XET_TAG:
565  if (elem->numChildren() > 0)
566  stream<<"</"<<elem->tagName().c_str()<<">";
567  break;
571  //WTF?
572  return true;
573  }
574  if ( stream.fail() )
575  return false;
576  return true;
577 }
578 
579 bool _exportXML( std::ostream &stream, XMLDocument *doc )
580 {
581  if (!doc)
582  return false;
583  //Output xml && <!DOCTYPE...> tags
584  if ( doc->sysId.empty() )
585  stream<<"<?xml version=\"1.0\"?>\n";
586 
587  else
588  stream<<"<?xml version=\"1.1\"?>\n";
589  if ( !doc->sysId.empty() ) {
590  stream<<"<!DOCTYPE "<<doc->docType.c_str();
591  if ( !doc->pubId.empty() )
592  stream<<" PUBLIC \""<<doc->pubId.c_str()<<"\"";
593 
594  else
595  stream<<" SYSTEM";
596  if (doc->sysId.find( '\"' ) != std::string::npos)
597  stream<<" \'"<<doc->sysId.c_str()<<"\'";
598 
599  else
600  stream<<" \""<<doc->sysId.c_str()<<"\"";
601  stream<<">\n";
602  }
603  //Did we fail?
604  if ( stream.fail() )
605  return false;
606  //Output the rest of the document
607  //NOTE: Although XML 1.1 requires that 'root' may only have one child,
608  //we cant't make that assumption because we count as elements comments
609  //and other productions referred to as 'misc' by the standard.
610  for (unsigned int i = 0; i < doc->root.numChildren(); ++i)
611  if ( !_exportXML( stream, doc->root.getChild( i ) ) )
612  return false;
613  return true;
614 }
615 
616 bool XMLSerializer::exportXML( std::ostream &stream )
617 {
618  return !stream.fail()
619  && _exportXML( stream, ( (XMLParserContext*) internals )->document );
620 }
621 
622 bool XMLSerializer::exportXML( const std::string &filename )
623 {
624  std::ofstream of( filename.c_str() );
625  return exportXML( of );
626 }
627 
629 {
630  assert( internals );
631 
632  XMLParserContext *ctxt = (XMLParserContext*) internals;
633  ctxt->processors.insert( std::pair< std::string, XMLProcessor* > ( proc->getTarget(), proc ) );
634 }
635 
636 bool XMLSerializer::initialise( const char *encoding, XMLDocument *doc, XMLElement *elem )
637 {
638  //DO NOT assert encoding==0... in case an encoding natively supported by expat is passed.
639  //DO assert, though, that doc==0 => elem==0
640  assert( !(!doc && elem) );
641 
642  delete close();
644  ctxt->xparser = this;
645  ctxt->document = doc ? doc : new XMLDocument();
646  ctxt->current = elem ? elem : &(ctxt->document->root);
647 
648  //Create Expat parser, and initialize
649  ctxt->parser = XML_ParserCreate( (const XML_Char*) encoding );
650  XML_SetUserData( ctxt->parser, ctxt );
651  XML_SetElementHandler( ctxt->parser, &ExpatHandlers::StartElement, &ExpatHandlers::EndElement );
652  XML_SetCharacterDataHandler( ctxt->parser, &ExpatHandlers::CData );
653  XML_SetProcessingInstructionHandler( ctxt->parser, &ExpatHandlers::PI );
654  XML_SetCommentHandler( ctxt->parser, ExpatHandlers::Comment );
655 
656  internals = ctxt;
657  return true;
658 }
659 
661 {
662  XMLDocument *doc = 0;
663  XMLParserContext *ctxt = (XMLParserContext*) internals;
664  if (ctxt) {
665  XML_ParserFree( ctxt->parser );
666  doc = ctxt->document;
669  delete ctxt;
670  }
671  internals = 0;
672 
673  return doc;
674 }
675 } //namespace XMLDOM
676