Hi J-S, You changes all look good and real improvement to the cleanness of the implementation ;-)
Thanks, Robert, On Fri, Jan 8, 2010 at 8:59 PM, Jean-Sébastien Guay <[email protected]> wrote: > Hi Robert, > >> Let me rework my changes and I'll get back to you. > > OK, so here are new changes. > > 1. The node type will be set to ATOM on read of <tag prop="..." ... /> type > tags. > 2. GROUP and NODE are now written using the same code (and not just > duplicated code). Also NODE will not be written as an ATOM if it has no > children or contents, so you need to set the type to ATOM if you want the > <tag ... /> style. > 3. You had put the write of "/>" for ATOM after the "return true", so it had > no effect... Moved to before the return. > 4. ATOM did not write its properties correctly, fixed. > 5. As an added bonus, I made the write() method indent the output so it's > more readable. It brings a small public interface change but the indent > argument has a default value so client code doesn't need to change (if there > even is any). > 6. Another added bonus, I've simplified the write() method a bit by > factoring out the write for children and properties into protected methods. > > Thanks, > > J-S > -- > ______________________________________________________ > Jean-Sebastien Guay [email protected] > http://www.cm-labs.com/ > http://whitestar02.webhop.org/ > > /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield > * > * This library is open source and may be redistributed and/or modified > under > * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or > * (at your option) any later version. The full license is in LICENSE file > * included with this distribution, and on the openscenegraph.org website. > * > * This library is distributed in the hope that it will be useful, > * but WITHOUT ANY WARRANTY; without even the implied warranty of > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > * OpenSceneGraph Public License for more details. > */ > > #ifndef OSGDB_XML_PARSER > #define OSGDB_XML_PARSER 1 > > #include <osgDB/Registry> > > namespace osgDB { > > // forward declare > class XmlNode; > > /** read an Xml file, find the file in Options DataFilePathList.*/ > extern OSGDB_EXPORT XmlNode* readXmlFile(const std::string& filename,const > Options* options); > > /** read an Xml file, find the file in osgDB::Registry's > eaderWriter::Options DataFilePathList.*/ > inline XmlNode* readXmlFile(const std::string& filename) > { > return readXmlFile(filename, osgDB::Registry::instance()->getOptions()); > } > > /** read an Xml from from an istream.*/ > extern OSGDB_EXPORT XmlNode* readXmlStream(std::istream& fin); > > extern OSGDB_EXPORT std::string trimEnclosingSpaces(const std::string& str); > > /** XmlNode class for very basic reading and writing of xml files.*/ > class OSGDB_EXPORT XmlNode : public osg::Referenced > { > public: > > XmlNode(); > > enum NodeType > { > UNASSIGNED, > ATOM, > NODE, > GROUP, > ROOT, > COMMENT, > INFORMATION > }; > > typedef std::map< std::string, std::string > Properties; > typedef std::vector< osg::ref_ptr<XmlNode> > Children; > > NodeType type; > std::string name; > std::string contents; > Properties properties; > Children children; > > std::string getTrimmedContents() const { return > trimEnclosingSpaces(contents); } > > public: > > class OSGDB_EXPORT Input > { > public: > > Input(); > Input(const Input&); > > ~Input(); > > typedef std::string::size_type size_type; > > void open(const std::string& filename); > void attach(std::istream& istream); > > void readAllDataIntoBuffer(); > > operator bool () const { return _currentPos<_buffer.size(); } > > size_type currentPosition() const { return _currentPos; } > > int get() { if (_currentPos<_buffer.size()) return > _buffer[_currentPos++]; else return -1; } > > int operator [] (size_type i) const { if > ((_currentPos+i)<_buffer.size()) return _buffer[_currentPos+i]; else return > -1; } > > void operator ++ () { if (_currentPos<_buffer.size()) > ++_currentPos; } > > void operator += (size_type n) { if > ((_currentPos+n)<_buffer.size()) _currentPos+=n; else _currentPos = > _buffer.size(); } > > void skipWhiteSpace(); > > std::string substr(size_type pos, size_type > n=std::string::npos) { return (_currentPos<_buffer.size()) ? > _buffer.substr(_currentPos+pos,n) : std::string(); } > > size_type find(const std::string& str) > { > if (_currentPos<_buffer.size()) > { > size_type pos = _buffer.find(str, _currentPos); > if (pos==std::string::npos) return std::string::npos; > else return pos-_currentPos; > } else return std::string::npos; > } > > bool match(const std::string& str) { return > (_currentPos<_buffer.size()) ? _buffer.compare(_currentPos, str.size(), > str)==0 : false; } > > > typedef std::map< std::string, int > ControlToCharacterMap; > typedef std::map< int, std::string> CharacterToControlMap; > > void addControlToCharacter(const std::string& control, int > c); > > ControlToCharacterMap _controlToCharacterMap; > CharacterToControlMap _characterToControlMap; > > private: > > void setUpControlMappings(); > > size_type _currentPos; > > std::ifstream _fin; > std::string _buffer; > > }; > > bool read(Input& input); > > bool write(std::ostream& fout, const std::string& indent = "") const; > bool writeString(std::ostream& fout, const std::string& str) const; > > protected: > > bool writeChildren(std::ostream& fout, const std::string& indent) > const; > bool writeProperties(std::ostream& fout) const; > }; > > } > #endif > > /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield > * > * This library is open source and may be redistributed and/or modified > under > * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or > * (at your option) any later version. The full license is in LICENSE file > * included with this distribution, and on the openscenegraph.org website. > * > * This library is distributed in the hope that it will be useful, > * but WITHOUT ANY WARRANTY; without even the implied warranty of > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > * OpenSceneGraph Public License for more details. > */ > > #include <osgDB/XmlParser> > #include <osgDB/FileUtils> > > #include <osg/Notify> > > using namespace osgDB; > > XmlNode* osgDB::readXmlFile(const std::string& filename,const Options* > options) > { > std::string foundFile = osgDB::findDataFile(filename, options); > if (!foundFile.empty()) > { > XmlNode::Input input; > input.open(foundFile); > input.readAllDataIntoBuffer(); > > if (!input) > { > osg::notify(osg::NOTICE)<<"Could not open XML file: > "<<filename<<std::endl; > return 0; > } > > osg::ref_ptr<XmlNode> root = new XmlNode; > root->read(input); > > return root.release(); > } > else > { > osg::notify(osg::NOTICE)<<"Could not find XML file: > "<<filename<<std::endl; > return 0; > } > } > > std::string osgDB::trimEnclosingSpaces(const std::string& str) > { > if (str.empty()) return str; > > std::string::size_type start = str.find_first_not_of(' '); > if (start==std::string::npos) return std::string(); > > std::string::size_type end = str.find_last_not_of(' '); > if (end==std::string::npos) return std::string(); > > return std::string(str, start, (end-start)+1); > } > > > XmlNode* osgDB::readXmlStream(std::istream& fin) > { > XmlNode::Input input; > input.attach(fin); > input.readAllDataIntoBuffer(); > > if (!input) > { > osg::notify(osg::NOTICE)<<"Could not attach to XML > stream."<<std::endl; > return 0; > } > > osg::ref_ptr<XmlNode> root = new XmlNode; > root->read(input); > > return root.release(); > } > > > XmlNode::Input::Input(): > _currentPos(0) > { > setUpControlMappings(); > } > > XmlNode::Input::Input(const Input&): > _currentPos(0) > { > setUpControlMappings(); > } > > XmlNode::Input::~Input() > { > } > > void XmlNode::Input::setUpControlMappings() > { > addControlToCharacter("&",'&'); > addControlToCharacter("<",'<'); > addControlToCharacter(">",'>'); > addControlToCharacter(""",'"'); > addControlToCharacter("'",'\''); > } > > void XmlNode::Input::addControlToCharacter(const std::string& control, int > c) > { > _controlToCharacterMap[control] = c; > _characterToControlMap[c] = control; > } > > void XmlNode::Input::open(const std::string& filename) > { > _fin.open(filename.c_str()); > } > > void XmlNode::Input::attach(std::istream& fin) > { > std::ios &fios = _fin; > fios.rdbuf(fin.rdbuf()); > } > > void XmlNode::Input::readAllDataIntoBuffer() > { > while(_fin) > { > int c = _fin.get(); > if (c>=0 && c<=255) > { > _buffer.push_back(c); > } > } > } > > void XmlNode::Input::skipWhiteSpace() > { > while(_currentPos<_buffer.size() && (_buffer[_currentPos]==' ' || > _buffer[_currentPos]=='\t')) > { > // > osg::notify(osg::NOTICE)<<"_currentPos="<<_currentPos<<"_buffer.size()="<<_buffer.size()<<" > v="<<_buffer[_currentPos]<<std::endl; > ++_currentPos; > } > //osg::notify(osg::NOTICE)<<"done"<<std::endl; > } > > XmlNode::XmlNode() > { > type = UNASSIGNED; > } > > bool XmlNode::read(Input& input) > { > if (type == UNASSIGNED) type = ROOT; > > while(input) > { > //input.skipWhiteSpace(); > if (input.match("<!--")) > { > XmlNode* commentNode = new XmlNode; > commentNode->type = XmlNode::COMMENT; > children.push_back(commentNode); > > input += 4; > XmlNode::Input::size_type end = input.find("-->"); > commentNode->contents = input.substr(0, end); > if (end!=std::string::npos) > { > osg::notify(osg::INFO)<<"Valid Comment record > ["<<commentNode->contents<<"]"<<std::endl; > input += (end+3); > } > else > { > osg::notify(osg::NOTICE)<<"Error: Unclosed Comment record > ["<<commentNode->contents<<"]"<<std::endl; > input += end; > } > } > else if (input.match("</")) > { > input += 2; > XmlNode::Input::size_type end = input.find(">"); > std::string comment = input.substr(0, end); > if (end!=std::string::npos) > { > osg::notify(osg::INFO)<<"Valid end tag > ["<<comment<<"]"<<std::endl; > input += (end+1); > } > else > { > osg::notify(osg::NOTICE)<<"Error: Unclosed end tag > ["<<comment<<"]"<<std::endl; > input += end; > } > > if (comment==name) osg::notify(osg::INFO)<<"end tag is matched > correctly"<<std::endl; > else osg::notify(osg::NOTICE)<<"Error: end tag is not matched > correctly"<<std::endl; > > return true; > } > else if (input.match("<?")) > { > XmlNode* commentNode = new XmlNode; > commentNode->type = XmlNode::INFORMATION; > children.push_back(commentNode); > > input += 2; > XmlNode::Input::size_type end = input.find("?>"); > commentNode->contents = input.substr(0, end); > if (end!=std::string::npos) > { > osg::notify(osg::INFO)<<"Valid infomation record > ["<<commentNode->contents<<"]"<<std::endl; > input += (end+2); > } > else > { > osg::notify(osg::NOTICE)<<"Error: Unclosed infomation record > ["<<commentNode->contents<<"]"<<std::endl; > input += end; > } > } > else if (input.match("<")) > { > XmlNode* childNode = new XmlNode; > childNode->type = XmlNode::NODE; > children.push_back(childNode); > > input += 1; > > input.skipWhiteSpace(); > > int c = 0; > while ((c=input[0])>=0 && c!=' ' && c!='>' && c!='/') > { > childNode->name.push_back(c); > ++input; > } > > while ((c=input[0])>=0 && c!='>' && c!='/') > { > Input::size_type prev_pos = input.currentPosition(); > > input.skipWhiteSpace(); > std::string option; > std::string value; > while((c=input[0])>=0 && c!='>' && c!='/' && c!='"' && > c!='\'' && c!='=' && c!=' ') > { > option.push_back(c); > ++input; > } > input.skipWhiteSpace(); > if (input[0]=='=') > { > ++input; > > input.skipWhiteSpace(); > > if (input[0]=='"') > { > ++input; > while((c=input[0])>=0 && c!='"') > { > value.push_back(c); > ++input; > } > ++input; > } > else if (input[0]=='\'') > { > ++input; > while((c=input[0])>=0 && c!='\'') > { > value.push_back(c); > ++input; > } > ++input; > } > else > { > ++input; > while((c=input[0])>=0 && c!=' ' && c!='"' && c!='\'' > && c!='>') > { > value.push_back(c); > ++input; > } > } > } > > if (prev_pos == input.currentPosition()) > { > osg::notify(osg::NOTICE)<<"Error, parser iterator note > advanced, position: "<<input.substr(0,50)<<std::endl; > ++input; > } > > if (!option.empty()) > { > osg::notify(osg::INFO)<<"Assigning option "<<option<<" > with value "<<value<<std::endl; > childNode->properties[option] = value; > } > } > > if ((c=input[0])>=0 && (c=='>' || c=='/')) > { > ++input; > > osg::notify(osg::INFO)<<"Valid tag > ["<<childNode->name<<"]"<<std::endl; > > if (c=='/') > { > if ((c=input[0])>=0 && c=='>') > { > ++input; > osg::notify(osg::INFO)<<"tag is closed > correctly"<<std::endl; > childNode->type = ATOM; > } > else > osg::notify(osg::NOTICE)<<"Error: tag is not closed > correctly"<<std::endl; > } > else > { > bool result = childNode->read(input); > if (!result) return false; > } > > if (type==NODE && !children.empty()) type = GROUP; > } > else > { > osg::notify(osg::NOTICE)<<"Unclosed tag > ["<<childNode->name<<"]"<<std::endl; > return false; > } > > } > else > { > int c = input[0]; > > if (c=='&') > { > std::string value; > while(input && (c=input.get())!=';') { value.push_back(c); } > value.push_back(c); > > if (input._controlToCharacterMap.count(value)!=0) > { > c = input._controlToCharacterMap[value]; > osg::notify(osg::INFO)<<"Read control character > "<<value<<" converted to "<<char(c)<<std::endl; > contents.push_back(c); > } > else > { > osg::notify(osg::NOTICE)<<"Warning: read control > character "<<value<<", but have no mapping to convert it to."<<std::endl; > } > } > else > { > contents.push_back( c ); > ++input; > } > > } > } > > if (type==NODE && !children.empty()) type = GROUP; > return false; > } > > bool XmlNode::write(std::ostream& fout, const std::string& indent) const > { > switch(type) > { > case(UNASSIGNED): > return false; > case(ATOM): > { > fout<<indent<<"<"<<name; > writeProperties(fout); > fout<<" />"<<std::endl; > return true; > } > case(ROOT): > { > writeChildren(fout, indent); > return true; > } > case(NODE): > case(GROUP): > { > fout<<indent<<"<"<<name; > writeProperties(fout); > fout<<">"<<std::endl; > > writeChildren(fout, indent + " "); > > fout<<indent<<"</"<<name<<">"<<std::endl; > return true; > } > case(COMMENT): > { > fout<<indent<<"<!--"<<contents<<"-->"<<std::endl; > return true; > } > case(INFORMATION): > { > fout<<indent<<"<?"<<contents<<"?>"<<std::endl; > return true; > } > } > return false; > } > > bool XmlNode::writeString(std::ostream& fout, const std::string& str) const > { > fout<<str; > return true; > } > > bool XmlNode::writeChildren(std::ostream& fout, const std::string& indent) > const > { > for(Children::const_iterator citr = children.begin(); > citr != children.end(); > ++citr) > { > if (!(*citr)->write(fout, indent)) > return false; > } > > return true; > } > > bool XmlNode::writeProperties(std::ostream& fout) const > { > for(Properties::const_iterator oitr = properties.begin(); > oitr != properties.end(); > ++oitr) > { > fout<<" "<<oitr->first<<"=\""; > if (!writeString(fout,oitr->second)) > return false; > fout<<"\""; > } > > return true; > } > > _______________________________________________ > osg-submissions mailing list > [email protected] > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org > > _______________________________________________ osg-submissions mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
