diff --git a/include/osgDB/InputStream b/include/osgDB/InputStream index 801abf7bd..d17f3c7b3 100644 --- a/include/osgDB/InputStream +++ b/include/osgDB/InputStream @@ -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(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. diff --git a/include/osgDB/OutputStream b/include/osgDB/OutputStream index f1cbdd773..4519bb569 100644 --- a/include/osgDB/OutputStream +++ b/include/osgDB/OutputStream @@ -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. diff --git a/include/osgDB/StreamOperator b/include/osgDB/StreamOperator index 77ecc7bd9..2f4944c23 100644 --- a/include/osgDB/StreamOperator +++ b/include/osgDB/StreamOperator @@ -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; diff --git a/src/osgDB/InputStream.cpp b/src/osgDB/InputStream.cpp index 55b7409f5..1558243b3 100644 --- a/src/osgDB/InputStream.cpp +++ b/src/osgDB/InputStream.cpp @@ -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 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( 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; igetFileName()); *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." ); diff --git a/src/osgPlugins/osg/AsciiStreamOperator.h b/src/osgPlugins/osg/AsciiStreamOperator.h index 20f5043c3..7296ab242 100644 --- a/src/osgPlugins/osg/AsciiStreamOperator.h +++ b/src/osgPlugins/osg/AsciiStreamOperator.h @@ -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; iget( 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 diff --git a/src/osgPlugins/osg/BinaryStreamOperator.h b/src/osgPlugins/osg/BinaryStreamOperator.h index 19a01df88..6b3cdce13 100644 --- a/src/osgPlugins/osg/BinaryStreamOperator.h +++ b/src/osgPlugins/osg/BinaryStreamOperator.h @@ -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; }; diff --git a/src/osgPlugins/osg/ReaderWriterOSG2.cpp b/src/osgPlugins/osg/ReaderWriterOSG2.cpp index a9060360d..165421158 100644 --- a/src/osgPlugins/osg/ReaderWriterOSG2.cpp +++ b/src/osgPlugins/osg/ReaderWriterOSG2.cpp @@ -18,6 +18,7 @@ #include #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, "getOptionString().find("XML")!=std::string::npos ) + { + fout << std::string("") << 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=", "Import/Export option: Use/Record a ascii schema file" ); supportsOption( "Compressor=", "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 local_opt = options ? static_cast(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 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 local_opt = options ? - static_cast(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( &object ); - if ( node ) return writeNode( *node, fileName, options ); - - const osg::Image* image = dynamic_cast( &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( &object ); - if ( node ) return writeNode( *node, fout, options ); - - const osg::Image* image = dynamic_cast( &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 local_opt = options ? static_cast(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 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 local_opt = options ? - static_cast(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 ); diff --git a/src/osgPlugins/osg/XmlStreamOperator.h b/src/osgPlugins/osg/XmlStreamOperator.h new file mode 100644 index 000000000..c2e69754b --- /dev/null +++ b/src/osgPlugins/osg/XmlStreamOperator.h @@ -0,0 +1,510 @@ +#ifndef OSGDB_XMLSTREAMOPERATOR +#define OSGDB_XMLSTREAMOPERATOR + +#include +#include +#include + +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::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 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 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 XmlNodePath; + XmlNodePath _nodePath; + + osg::ref_ptr _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()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 > XmlNodePath; + XmlNodePath _nodePath; + + osg::ref_ptr _root; + std::stringstream _sstream; +}; + +#endif