1 #ifndef OSGDB_XMLSTREAMOPERATOR 2 #define OSGDB_XMLSTREAMOPERATOR 3 4 #include <osgDB/StreamOperator> 5 #include <osgDB/XmlParser> 6 #include <sstream> 7 8 class XmlOutputIterator : public osgDB::OutputIterator 9 { 10 public: 11 enum ReadLineType 12 { 13 FIRST_LINE=0, // The first line of file 14 NEW_LINE, // A new line without checking its type 15 PROP_LINE, // A line starting with osgDB::PROPERTY 16 SUB_PROP_LINE, // A property line containing another osgDB::PROPERTY 17 BEGIN_BRACKET_LINE, // A line ending with a '{' 18 END_BRACKET_LINE, // A line starting with a '}' 19 TEXT_LINE // A text line, e.g. recording array elements 20 }; 21 XmlOutputIterator(std::ostream * ostream,int precision)22 XmlOutputIterator( std::ostream* ostream, int precision ) 23 : _readLineType(FIRST_LINE), _prevReadLineType(FIRST_LINE), _hasSubProperty(false) 24 { 25 _out = ostream; 26 if (precision>0) _sstream.precision(precision); 27 _root = new osgDB::XmlNode; 28 _root->type = osgDB::XmlNode::GROUP; 29 } 30 ~XmlOutputIterator()31 virtual ~XmlOutputIterator() {} 32 isBinary()33 virtual bool isBinary() const { return false; } 34 writeBool(bool b)35 virtual void writeBool( bool b ) 36 { addToCurrentNode( b ? std::string("TRUE") : std::string("FALSE") ); } 37 writeChar(char c)38 virtual void writeChar( char c ) 39 { _sstream << (short)c; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 40 writeUChar(unsigned char c)41 virtual void writeUChar( unsigned char c ) 42 { _sstream << (unsigned short)c; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 43 writeShort(short s)44 virtual void writeShort( short s ) 45 { _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 46 writeUShort(unsigned short s)47 virtual void writeUShort( unsigned short s ) 48 { _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 49 writeInt(int i)50 virtual void writeInt( int i ) 51 { _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 52 writeUInt(unsigned int i)53 virtual void writeUInt( unsigned int i ) 54 { _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 55 writeLong(long l)56 virtual void writeLong( long l ) 57 { _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 58 writeULong(unsigned long l)59 virtual void writeULong( unsigned long l ) 60 { _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 61 writeFloat(float f)62 virtual void writeFloat( float f ) 63 { _sstream << f; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 64 writeDouble(double d)65 virtual void writeDouble( double d ) 66 { _sstream << d; addToCurrentNode( _sstream.str() ); _sstream.str(""); } 67 writeString(const std::string & s)68 virtual void writeString( const std::string& s ) 69 { addToCurrentNode( s, true ); } 70 writeStream(std::ostream & (* fn)(std::ostream &))71 virtual void writeStream( std::ostream& (*fn)(std::ostream&) ) 72 { 73 if ( isEndl( fn ) ) 74 { 75 if ( _readLineType==PROP_LINE || _readLineType==END_BRACKET_LINE ) 76 { 77 if ( _hasSubProperty ) 78 { 79 _hasSubProperty = false; 80 popNode(); // Exit the sub-property element 81 } 82 popNode(); // Exit the property element 83 } 84 else if ( _readLineType==SUB_PROP_LINE ) 85 { 86 _hasSubProperty = false; 87 popNode(); // Exit the sub-property element 88 popNode(); // Exit the property element 89 } 90 else if ( _readLineType==TEXT_LINE ) 91 addToCurrentNode( fn ); 92 93 setLineType( NEW_LINE ); 94 } 95 else 96 addToCurrentNode( fn ); 97 } 98 writeBase(std::ios_base & (* fn)(std::ios_base &))99 virtual void writeBase( std::ios_base& (*fn)(std::ios_base&) ) 100 { 101 _sstream << fn; 102 } 103 writeGLenum(const osgDB::ObjectGLenum & value)104 virtual void writeGLenum( const osgDB::ObjectGLenum& value ) 105 { 106 GLenum e = value.get(); 107 const std::string& enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString("GL", e); 108 addToCurrentNode( enumString, true ); 109 } 110 writeProperty(const osgDB::ObjectProperty & prop)111 virtual void writeProperty( const osgDB::ObjectProperty& prop ) 112 { 113 std::string enumString = prop._name; 114 if ( prop._mapProperty ) 115 { 116 enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString(prop._name, prop._value); 117 addToCurrentNode( enumString, true ); 118 } 119 else 120 { 121 if ( _readLineType==NEW_LINE || _readLineType==BEGIN_BRACKET_LINE ) 122 { 123 pushNode( enumString ); 124 setLineType( PROP_LINE ); 125 } 126 else if ( _readLineType==PROP_LINE ) 127 { 128 pushNode( enumString ); 129 setLineType( SUB_PROP_LINE ); 130 _hasSubProperty = true; 131 } 132 else if ( _readLineType==SUB_PROP_LINE ) 133 { 134 popNode(); 135 pushNode( enumString ); 136 } 137 } 138 } 139 writeMark(const osgDB::ObjectMark & mark)140 virtual void writeMark( const osgDB::ObjectMark& mark ) 141 { 142 int delta = mark._indentDelta; 143 if ( delta>0 ) 144 { 145 setLineType( BEGIN_BRACKET_LINE ); 146 } 147 else if ( delta<0 ) 148 { 149 setLineType( END_BRACKET_LINE ); 150 } 151 } 152 writeCharArray(const char *,unsigned int)153 virtual void writeCharArray( const char* /*s*/, unsigned int /*size*/ ) {} 154 writeWrappedString(const std::string & str)155 virtual void writeWrappedString( const std::string& str ) 156 { 157 std::string realStr; 158 for ( std::string::const_iterator itr=str.begin(); itr!=str.end(); ++itr ) 159 { 160 char ch = *itr; 161 if ( ch=='\"' ) realStr += '\\'; 162 else if ( ch=='\\' ) realStr += '\\'; 163 realStr += ch; 164 } 165 realStr.insert( std::string::size_type(0), 1, '\"' ); 166 realStr += '\"'; 167 addToCurrentNode( realStr ); 168 } 169 flush()170 virtual void flush() 171 { 172 osg::ref_ptr<osgDB::XmlNode> xmlRoot = new osgDB::XmlNode; 173 xmlRoot->type = osgDB::XmlNode::ROOT; 174 xmlRoot->children.push_back( _root.get() ); 175 xmlRoot->write( *_out ); 176 } 177 178 protected: 179 void addToCurrentNode( const std::string& str, bool isString=false ) 180 { 181 if ( _readLineType==FIRST_LINE ) 182 { 183 _root->name = str; 184 return; 185 } 186 187 if ( _readLineType==NEW_LINE ) 188 { 189 if ( isString ) 190 { 191 pushNode( str ); 192 setLineType( PROP_LINE ); 193 return; 194 } 195 else 196 setLineType( TEXT_LINE ); 197 } 198 199 if ( _readLineType==TEXT_LINE ) 200 { 201 std::string& text = _nodePath.back()->properties["text"]; 202 text += str + ' '; 203 } 204 else if ( _nodePath.size()>0 ) 205 { 206 std::string& prop = _nodePath.back()->properties["attribute"]; 207 if ( !prop.empty() ) prop += ' '; 208 prop += str; 209 } 210 else 211 { 212 pushNode( str ); 213 setLineType( PROP_LINE ); 214 } 215 } 216 addToCurrentNode(std::ostream & (* fn)(std::ostream &))217 void addToCurrentNode( std::ostream& (*fn)(std::ostream&) ) 218 { 219 if ( _nodePath.size()>0 ) 220 { 221 osgDB::XmlNode* node = _nodePath.back(); 222 _sstream << fn; 223 if ( _readLineType==TEXT_LINE ) node->properties["text"] += _sstream.str(); 224 else node->properties["attribute"] += _sstream.str(); 225 _sstream.str(""); 226 } 227 } 228 pushNode(const std::string & name)229 osgDB::XmlNode* pushNode( const std::string& name ) 230 { 231 osg::ref_ptr<osgDB::XmlNode> node = new osgDB::XmlNode; 232 node->type = osgDB::XmlNode::ATOM; 233 234 // Set element name without '#' and '::' characters 235 std::string realName; 236 if ( name.length()>0 && name[0]=='#' ) 237 realName = name.substr(1); 238 else 239 { 240 realName = name; 241 242 std::string::size_type pos = realName.find("::"); 243 if ( pos!=std::string::npos ) 244 realName.replace( pos, 2, "--" ); 245 } 246 node->name = realName; 247 248 if ( _nodePath.size()>0 ) 249 { 250 _nodePath.back()->type = osgDB::XmlNode::GROUP; 251 _nodePath.back()->children.push_back(node); 252 } 253 else 254 _root->children.push_back(node); 255 256 _nodePath.push_back( node.get() ); 257 return node.get(); 258 } 259 popNode()260 osgDB::XmlNode* popNode() 261 { 262 osgDB::XmlNode* node = NULL; 263 if ( _nodePath.size()>0 ) 264 { 265 node = _nodePath.back(); 266 trimEndMarkers( node, "attribute" ); 267 trimEndMarkers( node, "text" ); 268 _nodePath.pop_back(); 269 } 270 return node; 271 } 272 trimEndMarkers(osgDB::XmlNode * node,const std::string & name)273 void trimEndMarkers( osgDB::XmlNode* node, const std::string& name ) 274 { 275 osgDB::XmlNode::Properties::iterator itr = node->properties.find(name); 276 if ( itr==node->properties.end() ) return; 277 278 std::string& str = itr->second; 279 if ( !str.empty() ) 280 { 281 std::string::size_type end = str.find_last_not_of( " \t\r\n" ); 282 if ( end==std::string::npos ) return; 283 str.erase( end+1 ); 284 } 285 286 if ( str.empty() ) 287 node->properties.erase(itr); 288 } 289 setLineType(ReadLineType type)290 void setLineType( ReadLineType type ) 291 { 292 _prevReadLineType = _readLineType; 293 _readLineType = type; 294 } 295 296 typedef std::vector<osgDB::XmlNode*> XmlNodePath; 297 XmlNodePath _nodePath; 298 299 osg::ref_ptr<osgDB::XmlNode> _root; 300 std::stringstream _sstream; 301 302 ReadLineType _readLineType; 303 ReadLineType _prevReadLineType; 304 bool _hasSubProperty; 305 }; 306 307 class XmlInputIterator : public osgDB::InputIterator 308 { 309 public: XmlInputIterator(std::istream * istream)310 XmlInputIterator( std::istream* istream ) 311 { 312 _in = istream; 313 _root = osgDB::readXmlStream( *istream ); 314 315 if ( _root.valid() && _root->children.size()>0 ) 316 _nodePath.push_back( _root->children[0] ); 317 } 318 ~XmlInputIterator()319 virtual ~XmlInputIterator() {} 320 isBinary()321 virtual bool isBinary() const { return false; } 322 readBool(bool & b)323 virtual void readBool( bool& b ) 324 { 325 std::string boolString; 326 if ( prepareStream() ) _sstream >> boolString; 327 if ( boolString=="TRUE" ) b = true; 328 else b = false; 329 } 330 readChar(char & c)331 virtual void readChar( char& c ) 332 { 333 short s = 0; 334 if ( prepareStream() ) _sstream >> s; 335 c = (char)s; 336 } 337 readSChar(signed char & c)338 virtual void readSChar( signed char& c ) 339 { 340 short s = 0; 341 if ( prepareStream() ) _sstream >> s; 342 c = (signed char)s; 343 } 344 readUChar(unsigned char & c)345 virtual void readUChar( unsigned char& c ) 346 { 347 unsigned short s = 0; 348 if ( prepareStream() ) _sstream >> s; 349 c = (unsigned char)s; 350 } 351 readShort(short & s)352 virtual void readShort( short& s ) 353 { std::string str; if (prepareStream()) _sstream >> str; s = static_cast<short>(strtol(str.c_str(), NULL, 0)); } 354 readUShort(unsigned short & s)355 virtual void readUShort( unsigned short& s ) 356 { std::string str; if (prepareStream()) _sstream >> str; s = static_cast<unsigned short>(strtoul(str.c_str(), NULL, 0)); } 357 readInt(int & i)358 virtual void readInt( int& i ) 359 { std::string str; if (prepareStream()) _sstream >> str; i = static_cast<int>(strtol(str.c_str(), NULL, 0)); } 360 readUInt(unsigned int & i)361 virtual void readUInt( unsigned int& i ) 362 { std::string str; if (prepareStream()) _sstream >> str; i = static_cast<unsigned int>(strtoul(str.c_str(), NULL, 0)); } 363 readLong(long & l)364 virtual void readLong( long& l ) 365 { std::string str; if (prepareStream()) _sstream >> str; l = strtol(str.c_str(), NULL, 0); } 366 readULong(unsigned long & l)367 virtual void readULong( unsigned long& l ) 368 { std::string str; if (prepareStream()) _sstream >> str; l = strtoul(str.c_str(), NULL, 0); } 369 readFloat(float & f)370 virtual void readFloat( float& f ) 371 { std::string str; if (prepareStream()) _sstream >> str; f = osg::asciiToFloat(str.c_str()); } 372 readDouble(double & d)373 virtual void readDouble( double& d ) 374 { std::string str; if (prepareStream()) _sstream >> str; d = osg::asciiToDouble(str.c_str()); } 375 readString(std::string & s)376 virtual void readString( std::string& s ) 377 { 378 if ( prepareStream() ) _sstream >> s; 379 380 // Replace '--' to '::' to get correct wrapper class 381 std::string::size_type pos = s.find("--"); 382 if ( pos!=std::string::npos ) 383 s.replace( pos, 2, "::" ); 384 } 385 readStream(std::istream & (* fn)(std::istream &))386 virtual void readStream( std::istream& (*fn)(std::istream&) ) 387 { if ( prepareStream() ) _sstream >> fn; } 388 readBase(std::ios_base & (* fn)(std::ios_base &))389 virtual void readBase( std::ios_base& (*fn)(std::ios_base&) ) 390 { _sstream >> fn; } 391 readGLenum(osgDB::ObjectGLenum & value)392 virtual void readGLenum( osgDB::ObjectGLenum& value ) 393 { 394 GLenum e = 0; 395 std::string enumString; 396 if ( prepareStream() ) _sstream >> enumString; 397 e = osgDB::Registry::instance()->getObjectWrapperManager()->getValue("GL", enumString); 398 value.set( e ); 399 } 400 readProperty(osgDB::ObjectProperty & prop)401 virtual void readProperty( osgDB::ObjectProperty& prop ) 402 { 403 int value = 0; 404 std::string enumString; 405 if ( prepareStream() ) _sstream >> enumString; 406 if ( prop._mapProperty ) 407 { 408 value = osgDB::Registry::instance()->getObjectWrapperManager()->getValue(prop._name, enumString); 409 } 410 else 411 { 412 // Replace '--' to '::' to get correct wrapper class 413 std::string::size_type pos = enumString.find("--"); 414 if ( pos!=std::string::npos ) 415 enumString.replace( pos, 2, "::" ); 416 417 if ( prop._name!=enumString ) 418 { 419 if ( prop._name[0]=='#' ) 420 enumString = '#' + enumString; 421 if ( prop._name!=enumString ) 422 { 423 OSG_WARN << "XmlInputIterator::readProperty(): Unmatched property " 424 << enumString << ", expecting " << prop._name << std::endl; 425 } 426 } 427 prop._name = enumString; 428 } 429 prop.set( value ); 430 } 431 readMark(osgDB::ObjectMark &)432 virtual void readMark( osgDB::ObjectMark& /*mark*/ ) {} 433 readCharArray(char *,unsigned int)434 virtual void readCharArray( char* /*s*/, unsigned int /*size*/ ) {} 435 readWrappedString(std::string & str)436 virtual void readWrappedString( std::string& str ) 437 { 438 if ( !prepareStream() ) return; 439 440 // Read available string in the stream buffer 441 unsigned int availSize = _sstream.rdbuf()->in_avail(); 442 std::string realStr = _sstream.str(); 443 _sstream.str(""); 444 445 // Find the first quot or valid character 446 bool hasQuot = false; 447 std::string::iterator itr = realStr.begin() + (realStr.size() - availSize); 448 for ( ; itr!=realStr.end(); ++itr ) 449 { 450 char ch = *itr; 451 if ((ch==' ') || (ch=='\n') || (ch=='\r')) continue; 452 else if (ch=='"') hasQuot = true; 453 else str += ch; 454 455 itr++; 456 break; 457 } 458 459 for ( ; itr!=realStr.end(); ++itr ) 460 { 461 char ch = *itr; 462 if (ch=='\\') 463 { 464 itr++; 465 if (itr == realStr.end()) break; 466 str += *itr; 467 } 468 else if (hasQuot && ch=='"') 469 { 470 // Get to the end of the wrapped string 471 itr++; 472 break; 473 } 474 else 475 str += ch; 476 } 477 if (itr != realStr.end()) 478 { 479 _sstream << std::string(itr, realStr.end()); 480 } 481 } 482 matchString(const std::string & str)483 virtual bool matchString( const std::string& str ) 484 { 485 prepareStream(); 486 std::string strInStream = osgDB::trimEnclosingSpaces(_sstream.str()); 487 if ( strInStream==str ) 488 { 489 std::string prop; readString( prop ); 490 return true; 491 } 492 return false; 493 } 494 advanceToCurrentEndBracket()495 virtual void advanceToCurrentEndBracket() {} 496 497 protected: isReadable()498 bool isReadable() const { return _sstream.rdbuf()->in_avail()>0; } 499 prepareStream()500 bool prepareStream() 501 { 502 if ( !_nodePath.size() ) return false; 503 if ( isReadable() ) return true; 504 _sstream.clear(); 505 506 osgDB::XmlNode* current = _nodePath.back().get(); 507 if ( current->type!=osgDB::XmlNode::COMMENT ) 508 { 509 if ( !current->name.empty() ) 510 { 511 _sstream.str( current->name ); 512 current->name.clear(); 513 return true; 514 } 515 516 if ( current->properties.size()>0 ) 517 { 518 if ( applyPropertyToStream(current, "attribute") ) return true; 519 else if ( applyPropertyToStream(current, "text") ) return true; 520 } 521 522 if ( current->children.size()>0 ) 523 { 524 _nodePath.push_back( current->children.front() ); 525 current->children.erase( current->children.begin() ); 526 return prepareStream(); 527 } 528 } 529 _nodePath.pop_back(); 530 return prepareStream(); 531 } 532 applyPropertyToStream(osgDB::XmlNode * node,const std::string & name)533 bool applyPropertyToStream( osgDB::XmlNode* node, const std::string& name ) 534 { 535 osgDB::XmlNode::Properties::iterator itr = node->properties.find(name); 536 if ( itr!=node->properties.end() ) 537 { 538 _sstream.str( itr->second ); 539 node->properties.erase( itr ); 540 return true; 541 } 542 return false; 543 } 544 545 typedef std::vector< osg::ref_ptr<osgDB::XmlNode> > XmlNodePath; 546 XmlNodePath _nodePath; 547 548 osg::ref_ptr<osgDB::XmlNode> _root; 549 std::stringstream _sstream; 550 }; 551 552 #endif 553