From e082b01f26487d8d30ea29546489086d086dbe0b Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 10 Mar 2010 13:48:41 +0000 Subject: [PATCH] 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. " --- include/osgDB/InputStream | 9 +- include/osgDB/OutputStream | 5 +- include/osgDB/StreamOperator | 7 + src/osgDB/InputStream.cpp | 93 +--- src/osgDB/OutputStream.cpp | 37 +- src/osgPlugins/osg/AsciiStreamOperator.h | 79 ++++ src/osgPlugins/osg/BinaryStreamOperator.h | 6 + src/osgPlugins/osg/ReaderWriterOSG2.cpp | 200 ++++++--- src/osgPlugins/osg/XmlStreamOperator.h | 510 ++++++++++++++++++++++ 9 files changed, 778 insertions(+), 168 deletions(-) create mode 100644 src/osgPlugins/osg/XmlStreamOperator.h 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