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