523 lines
16 KiB
C++
523 lines
16 KiB
C++
#ifndef OSGDB_XMLSTREAMOPERATOR
|
|
#define OSGDB_XMLSTREAMOPERATOR
|
|
|
|
#include <osgDB/StreamOperator>
|
|
#include <osgDB/XmlParser>
|
|
#include <sstream>
|
|
|
|
class XmlOutputIterator : public osgDB::OutputIterator
|
|
{
|
|
public:
|
|
enum ReadLineType
|
|
{
|
|
FIRST_LINE=0, // The first line of file
|
|
NEW_LINE, // A new line without checking its type
|
|
PROP_LINE, // A line starting with osgDB::PROPERTY
|
|
SUB_PROP_LINE, // A property line containing another osgDB::PROPERTY
|
|
BEGIN_BRACKET_LINE, // A line ending with a '{'
|
|
END_BRACKET_LINE, // A line starting with a '}'
|
|
TEXT_LINE // A text line, e.g. recording array elements
|
|
};
|
|
|
|
XmlOutputIterator( std::ostream* ostream )
|
|
: _readLineType(FIRST_LINE), _prevReadLineType(FIRST_LINE), _hasSubProperty(false)
|
|
{
|
|
_out = ostream;
|
|
_root = new osgDB::XmlNode;
|
|
_root->type = osgDB::XmlNode::GROUP;
|
|
}
|
|
|
|
virtual ~XmlOutputIterator() {}
|
|
|
|
virtual bool isBinary() const { return false; }
|
|
|
|
virtual void writeBool( bool b )
|
|
{ addToCurrentNode( b ? std::string("TRUE") : std::string("FALSE") ); }
|
|
|
|
virtual void writeChar( char c )
|
|
{ _sstream << (short)c; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeUChar( unsigned char c )
|
|
{ _sstream << (unsigned short)c; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeShort( short s )
|
|
{ _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeUShort( unsigned short s )
|
|
{ _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeInt( int i )
|
|
{ _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeUInt( unsigned int i )
|
|
{ _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeLong( long l )
|
|
{ _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeULong( unsigned long l )
|
|
{ _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeFloat( float f )
|
|
{ _sstream << f; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeDouble( double d )
|
|
{ _sstream << d; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
|
|
|
|
virtual void writeString( const std::string& s )
|
|
{ addToCurrentNode( s, true ); }
|
|
|
|
virtual void writeStream( std::ostream& (*fn)(std::ostream&) )
|
|
{
|
|
if ( fn==static_cast<std::ostream& (*)(std::ostream&)>(std::endl) )
|
|
{
|
|
if ( _readLineType==PROP_LINE || _readLineType==END_BRACKET_LINE )
|
|
{
|
|
if ( _hasSubProperty )
|
|
{
|
|
_hasSubProperty = false;
|
|
popNode(); // Exit the sub-property element
|
|
}
|
|
popNode(); // Exit the property element
|
|
}
|
|
else if ( _readLineType==SUB_PROP_LINE )
|
|
{
|
|
_hasSubProperty = false;
|
|
popNode(); // Exit the sub-property element
|
|
popNode(); // Exit the property element
|
|
}
|
|
else if ( _readLineType==TEXT_LINE )
|
|
addToCurrentNode( fn );
|
|
|
|
setLineType( NEW_LINE );
|
|
}
|
|
else
|
|
addToCurrentNode( fn );
|
|
}
|
|
|
|
virtual void writeBase( std::ios_base& (*fn)(std::ios_base&) )
|
|
{
|
|
_sstream << fn;
|
|
}
|
|
|
|
virtual void writeGLenum( const osgDB::ObjectGLenum& value )
|
|
{
|
|
GLenum e = value.get();
|
|
const std::string& enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString("GL", e);
|
|
addToCurrentNode( enumString, true );
|
|
}
|
|
|
|
virtual void writeProperty( const osgDB::ObjectProperty& prop )
|
|
{
|
|
std::string enumString = prop._name;
|
|
if ( prop._mapProperty )
|
|
{
|
|
enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString(prop._name, prop._value);
|
|
addToCurrentNode( enumString, true );
|
|
}
|
|
else
|
|
{
|
|
if ( _readLineType==NEW_LINE || _readLineType==BEGIN_BRACKET_LINE )
|
|
{
|
|
pushNode( enumString );
|
|
setLineType( PROP_LINE );
|
|
}
|
|
else if ( _readLineType==PROP_LINE )
|
|
{
|
|
pushNode( enumString );
|
|
setLineType( SUB_PROP_LINE );
|
|
_hasSubProperty = true;
|
|
}
|
|
else if ( _readLineType==SUB_PROP_LINE )
|
|
{
|
|
popNode();
|
|
pushNode( enumString );
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void writeMark( const osgDB::ObjectMark& mark )
|
|
{
|
|
int delta = mark._indentDelta;
|
|
if ( delta>0 )
|
|
{
|
|
setLineType( BEGIN_BRACKET_LINE );
|
|
}
|
|
else if ( delta<0 )
|
|
{
|
|
setLineType( END_BRACKET_LINE );
|
|
}
|
|
}
|
|
|
|
virtual void writeCharArray( const char* s, unsigned int size ) {}
|
|
|
|
virtual void writeWrappedString( const std::string& str )
|
|
{
|
|
std::string realStr;
|
|
for ( std::string::const_iterator itr=str.begin(); itr!=str.end(); ++itr )
|
|
{
|
|
if ( *itr=='\"' )
|
|
realStr += "''";
|
|
else
|
|
realStr += *itr;
|
|
}
|
|
addToCurrentNode( realStr );
|
|
}
|
|
|
|
virtual void flush()
|
|
{
|
|
osg::ref_ptr<osgDB::XmlNode> xmlRoot = new osgDB::XmlNode;
|
|
xmlRoot->type = osgDB::XmlNode::ROOT;
|
|
xmlRoot->children.push_back( _root.get() );
|
|
xmlRoot->write( *_out );
|
|
}
|
|
|
|
protected:
|
|
void addToCurrentNode( const std::string& str, bool isString=false )
|
|
{
|
|
if ( _readLineType==FIRST_LINE )
|
|
{
|
|
_root->name = str;
|
|
return;
|
|
}
|
|
|
|
if ( _readLineType==NEW_LINE )
|
|
{
|
|
if ( isString )
|
|
{
|
|
pushNode( str );
|
|
setLineType( PROP_LINE );
|
|
return;
|
|
}
|
|
else
|
|
setLineType( TEXT_LINE );
|
|
}
|
|
|
|
if ( _readLineType==TEXT_LINE )
|
|
{
|
|
std::string& text = _nodePath.back()->properties["text"];
|
|
text += str + ' ';
|
|
}
|
|
else if ( _nodePath.size()>0 )
|
|
{
|
|
std::string& prop = _nodePath.back()->properties["attribute"];
|
|
if ( !prop.empty() ) prop += ' ';
|
|
prop += str;
|
|
}
|
|
else
|
|
{
|
|
pushNode( str );
|
|
setLineType( PROP_LINE );
|
|
}
|
|
}
|
|
|
|
void addToCurrentNode( std::ostream& (*fn)(std::ostream&) )
|
|
{
|
|
if ( _nodePath.size()>0 )
|
|
{
|
|
osgDB::XmlNode* node = _nodePath.back();
|
|
_sstream << fn;
|
|
if ( _readLineType==TEXT_LINE ) node->properties["text"] += _sstream.str();
|
|
else node->properties["attribute"] += _sstream.str();
|
|
_sstream.str("");
|
|
}
|
|
}
|
|
|
|
osgDB::XmlNode* pushNode( const std::string& name )
|
|
{
|
|
osg::ref_ptr<osgDB::XmlNode> node = new osgDB::XmlNode;
|
|
node->type = osgDB::XmlNode::ATOM;
|
|
|
|
// Set element name without '#' and '::' characters
|
|
std::string realName;
|
|
if ( name.length()>0 && name[0]=='#' )
|
|
realName = name.substr(1);
|
|
else
|
|
{
|
|
realName = name;
|
|
|
|
std::string::size_type pos = realName.find("::");
|
|
if ( pos!=std::string::npos )
|
|
realName.replace( pos, 2, "--" );
|
|
}
|
|
node->name = realName;
|
|
|
|
if ( _nodePath.size()>0 )
|
|
{
|
|
_nodePath.back()->type = osgDB::XmlNode::GROUP;
|
|
_nodePath.back()->children.push_back(node);
|
|
}
|
|
else
|
|
_root->children.push_back(node);
|
|
|
|
_nodePath.push_back( node.get() );
|
|
return node.get();
|
|
}
|
|
|
|
osgDB::XmlNode* popNode()
|
|
{
|
|
osgDB::XmlNode* node = NULL;
|
|
if ( _nodePath.size()>0 )
|
|
{
|
|
node = _nodePath.back();
|
|
trimEndMarkers( node, "attribute" );
|
|
trimEndMarkers( node, "text" );
|
|
_nodePath.pop_back();
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void trimEndMarkers( osgDB::XmlNode* node, const std::string& name )
|
|
{
|
|
osgDB::XmlNode::Properties::iterator itr = node->properties.find(name);
|
|
if ( itr==node->properties.end() ) return;
|
|
|
|
std::string& str = itr->second;
|
|
if ( !str.empty() )
|
|
{
|
|
std::string::size_type end = str.find_last_not_of( " \t\r\n" );
|
|
if ( end==std::string::npos ) return;
|
|
str.erase( end+1 );
|
|
}
|
|
|
|
if ( str.empty() )
|
|
node->properties.erase(itr);
|
|
}
|
|
|
|
void setLineType( ReadLineType type )
|
|
{
|
|
_prevReadLineType = _readLineType;
|
|
_readLineType = type;
|
|
}
|
|
|
|
typedef std::vector<osgDB::XmlNode*> XmlNodePath;
|
|
XmlNodePath _nodePath;
|
|
|
|
osg::ref_ptr<osgDB::XmlNode> _root;
|
|
std::stringstream _sstream;
|
|
|
|
ReadLineType _readLineType;
|
|
ReadLineType _prevReadLineType;
|
|
bool _hasSubProperty;
|
|
};
|
|
|
|
class XmlInputIterator : public osgDB::InputIterator
|
|
{
|
|
public:
|
|
XmlInputIterator( std::istream* istream )
|
|
{
|
|
_in = istream;
|
|
_root = osgDB::readXmlStream( *istream );
|
|
|
|
if ( _root.valid() && _root->children.size()>0 )
|
|
_nodePath.push_back( _root->children[0] );
|
|
}
|
|
|
|
virtual ~XmlInputIterator() {}
|
|
|
|
virtual bool isBinary() const { return false; }
|
|
|
|
virtual void readBool( bool& b )
|
|
{
|
|
std::string boolString;
|
|
if ( prepareStream() ) _sstream >> boolString;
|
|
if ( boolString=="TRUE" ) b = true;
|
|
else b = false;
|
|
}
|
|
|
|
virtual void readChar( char& c )
|
|
{
|
|
short s = 0;
|
|
if ( prepareStream() ) _sstream >> s;
|
|
c = (char)s;
|
|
}
|
|
|
|
virtual void readSChar( signed char& c )
|
|
{
|
|
short s = 0;
|
|
if ( prepareStream() ) _sstream >> s;
|
|
c = (signed char)s;
|
|
}
|
|
|
|
virtual void readUChar( unsigned char& c )
|
|
{
|
|
unsigned short s = 0;
|
|
if ( prepareStream() ) _sstream >> s;
|
|
c = (unsigned char)s;
|
|
}
|
|
|
|
virtual void readShort( short& s )
|
|
{ if ( prepareStream() ) _sstream >> s; }
|
|
|
|
virtual void readUShort( unsigned short& s )
|
|
{ if ( prepareStream() ) _sstream >> s; }
|
|
|
|
virtual void readInt( int& i )
|
|
{ if ( prepareStream() ) _sstream >> i; }
|
|
|
|
virtual void readUInt( unsigned int& i )
|
|
{ if ( prepareStream() ) _sstream >> i; }
|
|
|
|
virtual void readLong( long& l )
|
|
{ if ( prepareStream() ) _sstream >> l; }
|
|
|
|
virtual void readULong( unsigned long& l )
|
|
{ if ( prepareStream() ) _sstream >> l; }
|
|
|
|
virtual void readFloat( float& f )
|
|
{ if ( prepareStream() ) _sstream >> f; }
|
|
|
|
virtual void readDouble( double& d )
|
|
{ if ( prepareStream() ) _sstream >> d; }
|
|
|
|
virtual void readString( std::string& s )
|
|
{
|
|
if ( prepareStream() ) _sstream >> s;
|
|
|
|
// Replace '--' to '::' to get correct wrapper class
|
|
std::string::size_type pos = s.find("--");
|
|
if ( pos!=std::string::npos )
|
|
s.replace( pos, 2, "::" );
|
|
}
|
|
|
|
virtual void readStream( std::istream& (*fn)(std::istream&) )
|
|
{ if ( prepareStream() ) _sstream >> fn; }
|
|
|
|
virtual void readBase( std::ios_base& (*fn)(std::ios_base&) )
|
|
{ _sstream >> fn; }
|
|
|
|
virtual void readGLenum( osgDB::ObjectGLenum& value )
|
|
{
|
|
GLenum e = 0;
|
|
std::string enumString;
|
|
if ( prepareStream() ) _sstream >> enumString;
|
|
e = osgDB::Registry::instance()->getObjectWrapperManager()->getValue("GL", enumString);
|
|
value.set( e );
|
|
}
|
|
|
|
virtual void readProperty( osgDB::ObjectProperty& prop )
|
|
{
|
|
int value = 0;
|
|
std::string enumString;
|
|
if ( prepareStream() ) _sstream >> enumString;
|
|
if ( prop._mapProperty )
|
|
{
|
|
value = osgDB::Registry::instance()->getObjectWrapperManager()->getValue(prop._name, enumString);
|
|
}
|
|
else
|
|
{
|
|
// Replace '--' to '::' to get correct wrapper class
|
|
std::string::size_type pos = enumString.find("--");
|
|
if ( pos!=std::string::npos )
|
|
enumString.replace( pos, 2, "::" );
|
|
|
|
if ( prop._name!=enumString )
|
|
{
|
|
if ( prop._name[0]=='#' )
|
|
enumString = '#' + enumString;
|
|
if ( prop._name!=enumString )
|
|
{
|
|
OSG_NOTIFY(osg::WARN) << "XmlInputIterator::readProperty(): Unmatched property "
|
|
<< enumString << ", expecting " << prop._name << std::endl;
|
|
}
|
|
}
|
|
prop._name = enumString;
|
|
}
|
|
prop.set( value );
|
|
}
|
|
|
|
virtual void readMark( osgDB::ObjectMark& mark ) {}
|
|
|
|
virtual void readCharArray( char* s, unsigned int size ) {}
|
|
|
|
virtual void readWrappedString( std::string& str )
|
|
{
|
|
std::string realStr;
|
|
if ( prepareStream() ) std::getline( _sstream, realStr );
|
|
for ( std::string::const_iterator itr=realStr.begin(); itr!=realStr.end(); ++itr )
|
|
{
|
|
if ( *itr=='\'' )
|
|
{
|
|
itr++;
|
|
if ( itr==realStr.end() ) break;
|
|
|
|
if ( *itr=='\'' ) str += '\"';
|
|
else str += '\'' + *itr;
|
|
}
|
|
else
|
|
str += *itr;
|
|
}
|
|
}
|
|
|
|
virtual bool matchString( const std::string& str )
|
|
{
|
|
prepareStream();
|
|
unsigned int size = str.length();
|
|
if ( _sstream.str().length()<size ) return false;
|
|
|
|
int result = _sstream.str().compare( 0, size, str );
|
|
if ( !result )
|
|
{
|
|
std::string prop; readString( prop );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void advanceToCurrentEndBracket() {}
|
|
|
|
protected:
|
|
bool isReadable() const { return _sstream.rdbuf()->in_avail()>0; }
|
|
|
|
bool prepareStream()
|
|
{
|
|
if ( !_nodePath.size() ) return false;
|
|
if ( isReadable() ) return true;
|
|
_sstream.clear();
|
|
|
|
osgDB::XmlNode* current = _nodePath.back().get();
|
|
if ( !current->name.empty() )
|
|
{
|
|
_sstream.str( current->name );
|
|
current->name.clear();
|
|
return true;
|
|
}
|
|
|
|
if ( current->properties.size()>0 )
|
|
{
|
|
if ( applyPropertyToStream(current, "attribute") ) return true;
|
|
else if ( applyPropertyToStream(current, "text") ) return true;
|
|
}
|
|
|
|
if ( current->children.size()>0 )
|
|
{
|
|
_nodePath.push_back( current->children.front() );
|
|
current->children.erase( current->children.begin() );
|
|
return prepareStream();
|
|
}
|
|
|
|
_nodePath.pop_back();
|
|
return prepareStream();
|
|
}
|
|
|
|
bool applyPropertyToStream( osgDB::XmlNode* node, const std::string& name )
|
|
{
|
|
osgDB::XmlNode::Properties::iterator itr = node->properties.find(name);
|
|
if ( itr!=node->properties.end() )
|
|
{
|
|
_sstream.str( itr->second );
|
|
node->properties.erase( itr );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
typedef std::vector< osg::ref_ptr<osgDB::XmlNode> > XmlNodePath;
|
|
XmlNodePath _nodePath;
|
|
|
|
osg::ref_ptr<osgDB::XmlNode> _root;
|
|
std::stringstream _sstream;
|
|
};
|
|
|
|
#endif
|