Created a simple XmlNode parser class for reading of basic xml files, such as used by present3D.
Converted Present3D across from using libxml2 to using the new osgDB::XmlNode/XmlNode::Input classes from Xml Parsing. This changes removes the dependency on libxml2, and allows the present3D application and p3d to work on all platforms.
This commit is contained in:
415
src/osgDB/XmlParser.cpp
Normal file
415
src/osgDB/XmlParser.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/* -*-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 ReaderWriter::Options* options)
|
||||
{
|
||||
std::string foundFile = osgDB::findDataFile(filename, options);
|
||||
if (!foundFile.empty())
|
||||
{
|
||||
XmlNode::Input input;
|
||||
input.open(foundFile);
|
||||
if (!input)
|
||||
{
|
||||
osg::notify(osg::NOTICE)<<"Could not open XML file: "<<filename<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
input.readAllDataIntoBuffer();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
XmlNode* osgDB::readXmlStream(std::istream& fin)
|
||||
{
|
||||
XmlNode::Input input;
|
||||
input.attach(fin);
|
||||
if (!input)
|
||||
{
|
||||
osg::notify(osg::NOTICE)<<"Could not attach to XML stream."<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
input.readAllDataIntoBuffer();
|
||||
|
||||
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]==' ') ++_currentPos;
|
||||
}
|
||||
|
||||
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!='>' )
|
||||
{
|
||||
childNode->name.push_back(c);
|
||||
++input;
|
||||
}
|
||||
|
||||
while ((c=input[0])>=0 && c!='>')
|
||||
{
|
||||
input.skipWhiteSpace();
|
||||
std::string option;
|
||||
std::string value;
|
||||
while((c=input[0])>=0 && c!='>' && c!='"' && c!='\'' && c!='=')
|
||||
{
|
||||
option.push_back(c);
|
||||
++input;
|
||||
}
|
||||
input.skipWhiteSpace();
|
||||
if (input[0]=='=')
|
||||
{
|
||||
++input;
|
||||
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 (!option.empty())
|
||||
{
|
||||
osg::notify(osg::INFO)<<"Assigning option "<<option<<" with value "<<value<<std::endl;
|
||||
childNode->properties[option] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if ((c=input[0])>=0 && c=='>' )
|
||||
{
|
||||
++input;
|
||||
|
||||
osg::notify(osg::INFO)<<"Valid tag ["<<childNode->name<<"]"<<std::endl;
|
||||
|
||||
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
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case(UNASSIGNED):
|
||||
return false;
|
||||
case(ATOM):
|
||||
{
|
||||
fout<<"<"<<name;
|
||||
for(Properties::const_iterator oitr = properties.begin();
|
||||
oitr != properties.end();
|
||||
++oitr)
|
||||
{
|
||||
fout<<oitr->first<<"\"";
|
||||
writeString(fout,oitr->second);
|
||||
fout<<"\""<<std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case(ROOT):
|
||||
{
|
||||
for(Children::const_iterator citr = children.begin();
|
||||
citr != children.end();
|
||||
++citr)
|
||||
{
|
||||
(*citr)->write(fout);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case(NODE):
|
||||
{
|
||||
fout<<"<"<<name;
|
||||
for(Properties::const_iterator oitr = properties.begin();
|
||||
oitr != properties.end();
|
||||
++oitr)
|
||||
{
|
||||
fout<<" "<<oitr->first<<"=\"";
|
||||
writeString(fout,oitr->second);
|
||||
fout<<"\"";
|
||||
}
|
||||
fout<<">";
|
||||
|
||||
for(Children::const_iterator citr = children.begin();
|
||||
citr != children.end();
|
||||
++citr)
|
||||
{
|
||||
(*citr)->write(fout);
|
||||
}
|
||||
|
||||
if (!contents.empty()) writeString(fout,contents);
|
||||
|
||||
fout<<"</"<<name<<">"<<std::endl;
|
||||
return true;
|
||||
}
|
||||
case(GROUP):
|
||||
{
|
||||
fout<<"<"<<name;
|
||||
for(Properties::const_iterator oitr = properties.begin();
|
||||
oitr != properties.end();
|
||||
++oitr)
|
||||
{
|
||||
fout<<" "<<oitr->first<<"=\"";
|
||||
writeString(fout,oitr->second);
|
||||
fout<<"\"";
|
||||
}
|
||||
fout<<">"<<std::endl;
|
||||
|
||||
for(Children::const_iterator citr = children.begin();
|
||||
citr != children.end();
|
||||
++citr)
|
||||
{
|
||||
(*citr)->write(fout);
|
||||
}
|
||||
|
||||
fout<<"</"<<name<<">"<<std::endl;
|
||||
return true;
|
||||
}
|
||||
case(COMMENT):
|
||||
{
|
||||
fout<<"<!--"<<contents<<"-->"<<std::endl;
|
||||
return true;
|
||||
}
|
||||
case(INFORMATION):
|
||||
{
|
||||
fout<<"<?"<<contents<<"?>"<<std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool XmlNode::writeString(std::ostream& fout, const std::string& str) const
|
||||
{
|
||||
fout<<str;
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user