From Wang Rui, "I've initially added the XML support of the new native osg format,

using osgDB::XmlParser. The extension for XML-formatted scenes is
.osgx, corresponding to .osgb for binary and .osgt for ascii. It could
either be rendered in osgviewer or edited by common web browsers and
xml editors because of a range of changes to fit the XML syntax. For
example, the recorded class names are slight modified, from
'osg::Geode' to 'osg--Geode'.

To quickly get an XML file:
# ./osgconv cow.osg cow.osgx

The StreamOperator header, InputStreram and OutputStream classes are
modified to be more portable for triple ascii/binary/XML formats. I
also fixed a bug in readImage()/writeImage() to share image objects if
needed.

The ReaderWriterOSG2 class now supports all three formats and
reading/writing scene objects (not nodes or images), thanks to
Torben's advice before.
"
This commit is contained in:
Robert Osfield 2010-03-10 13:48:41 +00:00
parent ce19b37981
commit e082b01f26
9 changed files with 778 additions and 168 deletions

View File

@ -61,7 +61,8 @@ public:
{
READ_UNKNOWN = 0,
READ_SCENE,
READ_IMAGE
READ_IMAGE,
READ_OBJECT
};
InputStream( const osgDB::Options* options );
@ -123,9 +124,9 @@ public:
{ ptr = static_cast<T*>(readObject()); return *this; }
// Convenient methods for reading
bool matchString( const std::string& str );
void advanceToCurrentEndBracket();
void readWrappedString( std::string& str );
bool matchString( const std::string& str ) { return _in->matchString(str); }
void advanceToCurrentEndBracket() { _in->advanceToCurrentEndBracket(); }
void readWrappedString( std::string& str ) { _in->readWrappedString(str); checkStream(); }
void readCharArray( char* s, unsigned int size ) { _in->readCharArray(s, size); }
// readSize() use unsigned int for all sizes.

View File

@ -60,7 +60,8 @@ public:
{
WRITE_UNKNOWN = 0,
WRITE_SCENE,
WRITE_IMAGE
WRITE_IMAGE,
WRITE_OBJECT
};
enum WriteImageHint
@ -133,7 +134,7 @@ public:
{ writeObject(ptr.get()); return *this; }
// Convenient methods for writing
void writeWrappedString( const std::string& str );
void writeWrappedString( const std::string& str ) { _out->writeWrappedString(str); }
void writeCharArray( const char* s, unsigned int size ) { _out->writeCharArray(s, size); }
// method for converting all data structure sizes to unsigned int to ensure architecture portability.

View File

@ -39,6 +39,9 @@ public:
virtual void writeProperty( const ObjectProperty& prop ) = 0;
virtual void writeMark( const ObjectMark& mark ) = 0;
virtual void writeCharArray( const char* s, unsigned int size ) = 0;
virtual void writeWrappedString( const std::string& str ) = 0;
virtual void flush() { _out->flush(); }
protected:
std::ostream* _out;
@ -79,6 +82,10 @@ public:
virtual void readProperty( ObjectProperty& prop ) = 0;
virtual void readMark( ObjectMark& mark ) = 0;
virtual void readCharArray( char* s, unsigned int size ) = 0;
virtual void readWrappedString( std::string& str ) = 0;
virtual bool matchString( const std::string& str ) { return false; }
virtual void advanceToCurrentEndBracket() {}
protected:
std::istream* _in;

View File

@ -161,77 +161,6 @@ InputStream& InputStream::operator>>( osg::Matrixd& mat )
return *this;
}
bool InputStream::matchString( const std::string& str )
{
if ( !isBinary() )
{
std::string s; *this >> s;
if ( s==str ) return true;
else _in->getStream()->seekg( -(int)(s.length()), std::ios::cur );
}
return false;
}
void InputStream::advanceToCurrentEndBracket()
{
if ( isBinary() )
return;
std::string passString;
unsigned int blocks = 0;
while ( !_in->getStream()->eof() )
{
passString.clear();
*this >> passString;
if ( passString=="}" )
{
if ( blocks<=0 ) return;
else blocks--;
}
else if ( passString=="{" )
blocks++;
}
}
void InputStream::readWrappedString( std::string& str )
{
*this >> str;
if ( !isBinary() )
{
if ( str[0]=='\"' )
{
if ( str.size()==1 || (*str.rbegin())!='\"' )
{
char ch;
do
{
_in->getStream()->get( ch ); checkStream();
if ( ch=='\\' )
{
_in->getStream()->get( ch ); checkStream();
if ( ch=='\"' )
{
str += ch; ch = 0;
}
else if ( ch=='\\' )
{
str += ch;
}
else
{
str += '\\'; str += ch;
}
}
else
str += ch;
} while ( ch!='\"' );
}
str = str.substr(1, str.size()-2);
}
}
}
osg::Array* InputStream::readArray()
{
osg::ref_ptr<osg::Array> array = NULL;
@ -486,6 +415,17 @@ osg::PrimitiveSet* InputStream::readPrimitiveSet()
osg::Image* InputStream::readImage()
{
unsigned int id = 0;
*this >> PROPERTY("ImageID") >> id;
if ( getException() ) return NULL;
IdentifierMap::iterator itr = _identifierMap.find( id );
if ( itr!=_identifierMap.end() )
{
advanceToCurrentEndBracket();
return static_cast<osg::Image*>( itr->second.get() );
}
std::string name;
int writeHint, decision = IMAGE_EXTERNAL;
*this >> PROPERTY("FileName"); readWrappedString(name);
@ -693,6 +633,7 @@ InputStream::ReadType InputStream::start( InputIterator* inIterator )
std::string typeString; *this >> typeString;
if ( typeString=="Scene" ) type = READ_SCENE;
else if ( typeString=="Image" ) type = READ_IMAGE;
else if ( typeString=="Object" ) type = READ_OBJECT;
std::string osgName, osgVersion;
*this >> PROPERTY("#Version") >> version;
@ -712,12 +653,16 @@ InputStream::ReadType InputStream::start( InputIterator* inIterator )
void InputStream::decompress()
{
if ( !isBinary() ) return;
_fields.clear();
_fields.push_back( "Decompression" );
if ( !isBinary() ) return;
std::string compressorName; *this >> compressorName;
if ( compressorName=="0" ) return;
if ( compressorName=="0" )
{
_fields.pop_back();
return;
}
BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(compressorName);
if ( !compressor )
@ -773,7 +718,7 @@ void InputStream::readArrayImplementation( T* a, int read_size, bool useByteSwap
a->resize( size );
if ( isBinary() )
{
_in->getStream()->read( (char*)&((*a)[0]), read_size*size ); checkStream();
readCharArray( (char*)&((*a)[0]), read_size*size ); checkStream();
if ( useByteSwap && _byteSwap )
{
for ( int i=0; i<size; ++i )

View File

@ -132,28 +132,6 @@ OutputStream& OutputStream::operator<<( const osg::Matrixd& mat )
return *this;
}
void OutputStream::writeWrappedString( const std::string& str )
{
if ( !isBinary() )
{
std::string wrappedStr;
unsigned int size = str.size();
for ( unsigned int i=0; i<size; ++i )
{
char ch = str[i];
if ( ch=='\"' ) wrappedStr += '\\';
else if ( ch=='\\' ) wrappedStr += '\\';
wrappedStr += ch;
}
wrappedStr.insert( 0, 1, '\"' );
wrappedStr += '\"';
*this << wrappedStr;
}
else
*this << str;
}
void OutputStream::writeArray( const osg::Array* a )
{
if ( !a ) return;
@ -312,6 +290,10 @@ void OutputStream::writeImage( const osg::Image* img )
{
if ( !img ) return;
unsigned int id = findOrCreateObjectID( img );
*this << PROPERTY("ImageID") << id << std::endl; // Write image ID
if ( getException() ) return;
*this << PROPERTY("FileName"); writeWrappedString(img->getFileName()); *this << std::endl;
*this << PROPERTY("WriteHint") << (int)img->getWriteHint();
if ( getException() ) return;
@ -487,7 +469,7 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
else
{
*this << _compressorName;
_out->getStream()->flush();
_out->flush();
_out->setStream( &_compressSource );
return;
}
@ -501,6 +483,7 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
{
case WRITE_SCENE: typeString = "Scene"; break;
case WRITE_IMAGE: typeString = "Image"; break;
case WRITE_OBJECT: typeString = "Object"; break;
default: break;
}
@ -515,12 +498,16 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
void OutputStream::compress( std::ostream* ostream )
{
if ( _compressorName.empty() || !isBinary() ) return;
_fields.clear();
_fields.push_back( "Compression" );
if ( _compressorName.empty() || !isBinary() ) return;
BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName);
if ( !compressor || !ostream ) return;
if ( !compressor || !ostream )
{
_fields.pop_back();
return;
}
if ( !compressor->compress(*ostream, _compressSource.str()) )
throwException( "OutputStream: Failed to compress stream." );

View File

@ -100,6 +100,23 @@ public:
virtual void writeCharArray( const char* s, unsigned int size ) {}
virtual void writeWrappedString( const std::string& str )
{
std::string wrappedStr;
unsigned int size = str.size();
for ( unsigned int i=0; i<size; ++i )
{
char ch = str[i];
if ( ch=='\"' ) wrappedStr += '\\';
else if ( ch=='\\' ) wrappedStr += '\\';
wrappedStr += ch;
}
wrappedStr.insert( 0, 1, '\"' );
wrappedStr += '\"';
writeString( wrappedStr );
}
protected:
bool _readyForEndBracket;
int _indent;
@ -212,6 +229,68 @@ public:
}
virtual void readCharArray( char* s, unsigned int size ) {}
virtual void readWrappedString( std::string& str )
{
readString( str );
if ( str[0]=='\"' )
{
if ( str.size()==1 || (*str.rbegin())!='\"' )
{
char ch;
do
{
_in->get( ch ); checkStream();
if ( ch=='\\' )
{
_in->get( ch ); checkStream();
if ( ch=='\"' )
{
str += ch; ch = 0;
}
else if ( ch=='\\' )
{
str += ch;
}
else
{
str += '\\'; str += ch;
}
}
else
str += ch;
} while ( ch!='\"' );
}
str = str.substr(1, str.size()-2);
}
}
virtual bool matchString( const std::string& str )
{
std::string s; readString(s);
if ( s==str ) return true;
else _in->seekg( -(int)(s.length()), std::ios::cur );
return false;
}
virtual void advanceToCurrentEndBracket()
{
std::string passString;
unsigned int blocks = 0;
while ( !_in->eof() )
{
passString.clear();
readString( passString );
if ( passString=="}" )
{
if ( blocks<=0 ) return;
else blocks--;
}
else if ( passString=="{" )
blocks++;
}
}
};
#endif

View File

@ -65,6 +65,9 @@ public:
virtual void writeCharArray( const char* s, unsigned int size )
{ if ( size>0 ) _out->write( s, size ); }
virtual void writeWrappedString( const std::string& str )
{ writeString( str ); }
};
class BinaryInputIterator : public osgDB::InputIterator
@ -177,6 +180,9 @@ public:
virtual void readCharArray( char* s, unsigned int size )
{ if ( size>0 ) _in->read( s, size ); }
virtual void readWrappedString( std::string& str )
{ readString( str ); }
protected:
int _byteSwap;
};

View File

@ -18,6 +18,7 @@
#include <osgDB/ObjectWrapper>
#include "AsciiStreamOperator.h"
#include "BinaryStreamOperator.h"
#include "XmlStreamOperator.h"
using namespace osgDB;
@ -26,11 +27,15 @@ using namespace osgDB;
InputIterator* readInputIterator( std::istream& fin, const Options* options )
{
bool extensionIsAscii = false;
if ( options && options->getOptionString().find("Ascii")!=std::string::npos )
extensionIsAscii = true;
bool extensionIsAscii = false, extensionIsXML = false;
if ( options )
{
const std::string& optionString = options->getOptionString();
if ( optionString.find("Ascii")!=std::string::npos ) extensionIsAscii = true;
else if ( optionString.find("XML")!=std::string::npos ) extensionIsXML = true;
}
if ( !extensionIsAscii )
if ( !extensionIsAscii && !extensionIsXML )
{
unsigned int headerLow = 0, headerHigh = 0;
fin.read( (char*)&headerLow, INT_SIZE );
@ -42,10 +47,24 @@ InputIterator* readInputIterator( std::istream& fin, const Options* options )
fin.seekg( 0, std::ios::beg );
}
std::string header; fin >> header;
if ( header=="#Ascii" )
if ( !extensionIsXML )
{
return new AsciiInputIterator(&fin);
std::string header; fin >> header;
if ( header=="#Ascii" )
{
return new AsciiInputIterator(&fin);
}
fin.seekg( 0, std::ios::beg );
}
if ( 1 )
{
std::string header; std::getline( fin, header );
if ( !header.compare(0, 5, "<?xml") )
{
return new XmlInputIterator(&fin);
}
fin.seekg( 0, std::ios::beg );
}
return NULL;
}
@ -57,6 +76,11 @@ OutputIterator* writeInputIterator( std::ostream& fout, const Options* options )
fout << std::string("#Ascii") << ' ';
return new AsciiOutputIterator(&fout);
}
else if ( options && options->getOptionString().find("XML")!=std::string::npos )
{
fout << std::string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>") << std::endl;
return new XmlOutputIterator(&fout);
}
else
{
unsigned int low = OSG_HEADER_LOW, high = OSG_HEADER_HIGH;
@ -74,8 +98,10 @@ public:
supportsExtension( "osg2", "OpenSceneGraph extendable format" );
supportsExtension( "osgt", "OpenSceneGraph extendable ascii format" );
supportsExtension( "osgb", "OpenSceneGraph extendable binary format" );
supportsExtension( "osgx", "OpenSceneGraph extendable XML format" );
supportsOption( "Ascii", "Import/Export option: Force reading/writing ascii file" );
supportsOption( "XML", "Import/Export option: Force reading/writing XML file" );
supportsOption( "ForceReadingImage", "Import option: Load an empty image instead if required file missed" );
supportsOption( "SchemaFile=<file>", "Import/Export option: Use/Record a ascii schema file" );
supportsOption( "Compressor=<name>", "Export option: Use an inbuilt or user-defined compressor" );
@ -99,26 +125,58 @@ public:
virtual const char* className() const
{ return "OpenSceneGraph Native Format Reader/Writer"; }
virtual ReadResult readObject( const std::string& file, const Options* options ) const
{ return readNode(file, options); }
virtual ReadResult readObject( std::istream& fin, const Options* options ) const
{ return readNode(fin, options); }
virtual ReadResult readImage( const std::string& file, const Options* options ) const
Options* prepareReading( ReadResult& result, std::string& fileName, const Options* options ) const
{
std::string ext = osgDB::getLowerCaseFileExtension( file );
if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options );
if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND;
std::string ext = osgDB::getLowerCaseFileExtension( fileName );
if ( !acceptsExtension(ext) ) result = ReadResult::FILE_NOT_HANDLED;
fileName = osgDB::findDataFile( fileName, options );
if ( fileName.empty() ) result = ReadResult::FILE_NOT_FOUND;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
return local_opt.release();
}
virtual ReadResult readObject( const std::string& file, const Options* options ) const
{
ReadResult result = ReadResult::FILE_LOADED;
std::string fileName = file;
Options* local_opt = prepareReading( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readImage( istream, local_opt.get() );
return readObject( istream, local_opt );
}
virtual ReadResult readObject( std::istream& fin, const Options* options ) const
{
osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
if ( !ii ) return ReadResult::FILE_NOT_HANDLED;
InputStream is( options );
if ( is.start(ii.get())!=InputStream::READ_OBJECT )
{
CATCH_EXCEPTION(is);
return ReadResult::FILE_NOT_HANDLED;
}
is.decompress(); CATCH_EXCEPTION(is);
osg::Object* obj = is.readObject(); CATCH_EXCEPTION(is);
return obj;
}
virtual ReadResult readImage( const std::string& file, const Options* options ) const
{
ReadResult result = ReadResult::FILE_LOADED;
std::string fileName = file;
Options* local_opt = prepareReading( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readImage( istream, local_opt );
}
virtual ReadResult readImage( std::istream& fin, const Options* options ) const
@ -139,18 +197,13 @@ public:
virtual ReadResult readNode( const std::string& file, const Options* options ) const
{
std::string ext = osgDB::getLowerCaseFileExtension( file );
if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options );
if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
ReadResult result = ReadResult::FILE_LOADED;
std::string fileName = file;
Options* local_opt = prepareReading( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readNode( istream, local_opt.get() );
return readNode( istream, local_opt );
}
virtual ReadResult readNode( std::istream& fin, const Options* options ) const
@ -170,41 +223,65 @@ public:
return node;
}
virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const
{
const osg::Node* node = dynamic_cast<const osg::Node*>( &object );
if ( node ) return writeNode( *node, fileName, options );
const osg::Image* image = dynamic_cast<const osg::Image*>( &object );
if ( image ) return writeImage( *image, fileName, options );
return WriteResult::FILE_NOT_HANDLED;
}
virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
{
const osg::Node* node = dynamic_cast<const osg::Node*>( &object );
if ( node ) return writeNode( *node, fout, options );
const osg::Image* image = dynamic_cast<const osg::Image*>( &object );
if ( image ) return writeImage( *image, fout, options );
return WriteResult::FILE_NOT_HANDLED;
}
virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
Options* prepareWriting( WriteResult& result, const std::string& fileName, const Options* options ) const
{
std::string ext = osgDB::getFileExtension( fileName );
if ( !acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED;
if ( !acceptsExtension(ext) ) result = WriteResult::FILE_NOT_HANDLED;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
if( local_opt->getDatabasePathList().empty() )
local_opt->setDatabasePath( osgDB::getFilePath(fileName) );
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
return local_opt.release();
}
virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const
{
WriteResult result = WriteResult::FILE_SAVED;
Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
WriteResult result = writeImage( image, fout, local_opt.get() );
result = writeObject( object, fout, local_opt );
fout.close();
return result;
}
virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
{
osg::ref_ptr<OutputIterator> oi = writeInputIterator(fout, options);
OutputStream os( options );
os.start( oi.get(), OutputStream::WRITE_OBJECT ); CATCH_EXCEPTION(os);
os.writeObject( &object ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() )
{
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
schemaStream.close();
}
if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
return WriteResult::FILE_SAVED;
}
virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
{
WriteResult result = WriteResult::FILE_SAVED;
Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
result = writeImage( image, fout, local_opt );
fout.close();
return result;
}
@ -218,6 +295,7 @@ public:
os.writeImage( &image ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() )
{
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
@ -231,19 +309,14 @@ public:
virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const
{
std::string ext = osgDB::getFileExtension( fileName );
if ( !acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
if ( local_opt->getDatabasePathList().empty() )
local_opt->setDatabasePath( osgDB::getFilePath(fileName) );
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
WriteResult result = WriteResult::FILE_SAVED;
Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
WriteResult result = writeNode( node, fout, local_opt.get() );
result = writeNode( node, fout, local_opt );
fout.close();
return result;
}
@ -257,6 +330,7 @@ public:
os.writeObject( &node ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() )
{
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );

View File

@ -0,0 +1,510 @@
#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 << c; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeUChar( unsigned char c )
{ _sstream << 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 )
{ if ( prepareStream() ) _sstream >> c; }
virtual void readSChar( signed char& c )
{ if ( prepareStream() ) _sstream >> c; }
virtual void readUChar( unsigned char& c )
{ if ( prepareStream() ) _sstream >> c; }
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();
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