You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
15 KiB
501 lines
15 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkXMLParser.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
// Hack access to the fstream implementation if necessary. This is
|
|
// only needed on a few SGI MIPSpro compiler versions.
|
|
#if defined(__sgi) && !defined(__GNUC__) && defined(_COMPILER_VERSION)
|
|
# if _COMPILER_VERSION == 730
|
|
# include "vtkConfigure.h"
|
|
# if VTK_STREAM_EOF_SEVERITY == 3
|
|
# define protected public
|
|
# define private public
|
|
# include <fstream>
|
|
# undef private
|
|
# undef protected
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#include "vtkXMLParser.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtk_expat.h"
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
|
|
vtkCxxRevisionMacro(vtkXMLParser, "$Revision: 1.25 $");
|
|
vtkStandardNewMacro(vtkXMLParser);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLParser::vtkXMLParser()
|
|
{
|
|
this->Stream = 0;
|
|
this->Parser = 0;
|
|
this->FileName = 0;
|
|
this->InputString = 0;
|
|
this->InputStringLength = 0;
|
|
this->ParseError = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLParser::~vtkXMLParser()
|
|
{
|
|
this->SetStream(0);
|
|
this->SetFileName(0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
if(this->Stream)
|
|
{
|
|
os << indent << "Stream: " << this->Stream << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "Stream: (none)\n";
|
|
}
|
|
os << indent << "FileName: " << (this->FileName? this->FileName : "(none)")
|
|
<< "\n";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static int vtkXMLParserFail(istream* stream)
|
|
{
|
|
// The fail() method returns true if either the failbit or badbit is set.
|
|
#if defined(__HP_aCC)
|
|
// The HP compiler sets the badbit too often, so we ignore it.
|
|
return (stream->rdstate() & ios::failbit)? 1:0;
|
|
#else
|
|
return stream->fail()? 1:0;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
long vtkXMLParser::TellG()
|
|
{
|
|
// Standard tellg returns -1 if fail() is true.
|
|
if(!this->Stream || vtkXMLParserFail(this->Stream))
|
|
{
|
|
return -1;
|
|
}
|
|
#if VTK_STREAM_EOF_SEVERITY == 0
|
|
// No work-around required. Just return the position.
|
|
return this->Stream->tellg();
|
|
#else
|
|
long pos = this->Stream->tellg();
|
|
if(pos == -1)
|
|
{
|
|
// Clear the fail bit from failing tellg.
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::failbit);
|
|
|
|
// Save the eof bit.
|
|
int eof = this->Stream->eof()?1:0;
|
|
|
|
// Clear the eof bit.
|
|
if(eof)
|
|
{
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::eofbit);
|
|
}
|
|
|
|
# if VTK_STREAM_EOF_SEVERITY == 2
|
|
// Re-seek to the end to escape the buggy stream state.
|
|
this->Stream->seekg(0, ios::end);
|
|
# elif VTK_STREAM_EOF_SEVERITY == 3
|
|
// Call an internal filebuf method to escape the buggy stream
|
|
// state. This is a very ugly hack.
|
|
static_cast<ifstream*>(this->Stream)->rdbuf()->_M_seek_return(0,0);
|
|
# endif
|
|
|
|
// Call tellg to get the position.
|
|
pos = this->Stream->tellg();
|
|
|
|
// Restore the eof bit.
|
|
if(eof)
|
|
{
|
|
this->Stream->clear(this->Stream->rdstate() | ios::eofbit);
|
|
}
|
|
}
|
|
return pos;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::SeekG(long position)
|
|
{
|
|
// Standard seekg does nothing if fail() is true.
|
|
if(!this->Stream || vtkXMLParserFail(this->Stream))
|
|
{
|
|
return;
|
|
}
|
|
#if VTK_STREAM_EOF_SEVERITY == 0
|
|
// No work-around required. Just seek to the position.
|
|
this->Stream->seekg(position);
|
|
#else
|
|
// Save the eof bit.
|
|
int eof = this->Stream->eof()?1:0;
|
|
|
|
// Clear the eof bit.
|
|
if(eof)
|
|
{
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::eofbit);
|
|
}
|
|
|
|
# if VTK_STREAM_EOF_SEVERITY == 3
|
|
// Check if the stream is in the buggy state.
|
|
if(long(this->Stream->tellg()) == -1)
|
|
{
|
|
// Call an internal filebuf method to escape the buggy stream
|
|
// state. This is a very ugly hack.
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::failbit);
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::eofbit);
|
|
static_cast<ifstream*>(this->Stream)->rdbuf()->_M_seek_return(0,0);
|
|
}
|
|
# endif
|
|
|
|
// Seek to the given position.
|
|
this->Stream->seekg(position);
|
|
|
|
// Restore the eof bit.
|
|
if(eof)
|
|
{
|
|
this->Stream->clear(this->Stream->rdstate() | ios::eofbit);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::Parse(const char* inputString)
|
|
{
|
|
this->InputString = inputString;
|
|
this->InputStringLength = -1;
|
|
int result = this->vtkXMLParser::Parse();
|
|
this->InputString = 0;
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::Parse(const char* inputString, unsigned int length)
|
|
{
|
|
this->InputString = inputString;
|
|
this->InputStringLength = length;
|
|
int result = this->vtkXMLParser::Parse();
|
|
this->InputString = 0;
|
|
this->InputStringLength = -1;
|
|
return result;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::Parse()
|
|
{
|
|
// Select source of XML
|
|
ifstream ifs;
|
|
if ( !this->InputString && !this->Stream && this->FileName )
|
|
{
|
|
// If it is file, open it and set the appropriate stream
|
|
struct stat fs;
|
|
if (stat(this->FileName, &fs) != 0)
|
|
{
|
|
vtkErrorMacro("Cannot open XML file: " << this->FileName);
|
|
return 0;
|
|
}
|
|
#ifdef _WIN32
|
|
ifs.open(this->FileName, ios::binary | ios::in);
|
|
#else
|
|
ifs.open(this->FileName, ios::in);
|
|
#endif
|
|
if ( !ifs )
|
|
{
|
|
vtkErrorMacro("Cannot open XML file: " << this->FileName);
|
|
return 0;
|
|
}
|
|
this->Stream = &ifs;
|
|
}
|
|
|
|
// Create the expat XML parser.
|
|
this->Parser = XML_ParserCreate(0);
|
|
XML_SetElementHandler(static_cast<XML_Parser>(this->Parser),
|
|
&vtkXMLParserStartElement,
|
|
&vtkXMLParserEndElement);
|
|
XML_SetCharacterDataHandler(static_cast<XML_Parser>(this->Parser),
|
|
&vtkXMLParserCharacterDataHandler);
|
|
XML_SetUserData(static_cast<XML_Parser>(this->Parser), this);
|
|
|
|
// Parse the input.
|
|
int result = this->ParseXML();
|
|
|
|
if(result)
|
|
{
|
|
// Tell the expat XML parser about the end-of-input.
|
|
if(!XML_Parse(static_cast<XML_Parser>(this->Parser), "", 0, 1))
|
|
{
|
|
this->ReportXmlParseError();
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
// Clean up the parser.
|
|
XML_ParserFree(static_cast<XML_Parser>(this->Parser));
|
|
this->Parser = 0;
|
|
|
|
// If the source was a file, reset the stream
|
|
if ( this->Stream == &ifs )
|
|
{
|
|
this->Stream = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::InitializeParser()
|
|
{
|
|
if ( this->Parser )
|
|
{
|
|
vtkErrorMacro("Parser already initialized");
|
|
this->ParseError = 1;
|
|
return 0;
|
|
}
|
|
// Create the expat XML parser.
|
|
this->Parser = XML_ParserCreate(0);
|
|
XML_SetElementHandler(static_cast<XML_Parser>(this->Parser),
|
|
&vtkXMLParserStartElement,
|
|
&vtkXMLParserEndElement);
|
|
XML_SetCharacterDataHandler(static_cast<XML_Parser>(this->Parser),
|
|
&vtkXMLParserCharacterDataHandler);
|
|
XML_SetUserData(static_cast<XML_Parser>(this->Parser), this);
|
|
this->ParseError = 0;
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::ParseChunk(const char* inputString, unsigned int length)
|
|
{
|
|
if ( !this->Parser )
|
|
{
|
|
vtkErrorMacro("Parser not initialized");
|
|
this->ParseError = 1;
|
|
return 0;
|
|
}
|
|
int res;
|
|
res = this->ParseBuffer(inputString, length);
|
|
if ( res == 0 )
|
|
{
|
|
this->ParseError = 1;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::CleanupParser()
|
|
{
|
|
if ( !this->Parser )
|
|
{
|
|
vtkErrorMacro("Parser not initialized");
|
|
this->ParseError = 1;
|
|
return 0;
|
|
}
|
|
int result = !this->ParseError;
|
|
if(result)
|
|
{
|
|
// Tell the expat XML parser about the end-of-input.
|
|
if(!XML_Parse(static_cast<XML_Parser>(this->Parser), "", 0, 1))
|
|
{
|
|
this->ReportXmlParseError();
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
// Clean up the parser.
|
|
XML_ParserFree(static_cast<XML_Parser>(this->Parser));
|
|
this->Parser = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::ParseXML()
|
|
{
|
|
// Parsing of message
|
|
if ( this->InputString )
|
|
{
|
|
if ( this->InputStringLength >= 0 )
|
|
{
|
|
return this->ParseBuffer(this->InputString, this->InputStringLength);
|
|
}
|
|
else
|
|
{
|
|
return this->ParseBuffer(this->InputString);
|
|
}
|
|
}
|
|
|
|
// Make sure we have input.
|
|
if(!this->Stream)
|
|
{
|
|
vtkErrorMacro("Parse() called with no Stream set.");
|
|
return 0;
|
|
}
|
|
|
|
// Default stream parser just reads a block at a time.
|
|
istream& in = *(this->Stream);
|
|
const int bufferSize = 4096;
|
|
char buffer[bufferSize];
|
|
|
|
// Read in the stream and send its contents to the XML parser. This
|
|
// read loop is very sensitive on certain platforms with slightly
|
|
// broken stream libraries (like HPUX). Normally, it is incorrect
|
|
// to not check the error condition on the fin.read() before using
|
|
// the data, but the fin.gcount() will be zero if an error occurred.
|
|
// Therefore, the loop should be safe everywhere.
|
|
while(!this->ParseError && !this->ParsingComplete() && in)
|
|
{
|
|
in.read(buffer, bufferSize);
|
|
if(in.gcount())
|
|
{
|
|
if(!this->ParseBuffer(buffer, in.gcount()))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the fail and eof bits on the input stream so we can later
|
|
// seek back to read data.
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::eofbit);
|
|
this->Stream->clear(this->Stream->rdstate() & ~ios::failbit);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::ParsingComplete()
|
|
{
|
|
// Default behavior is to parse to end of stream.
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::StartElement(const char *name,
|
|
const char ** vtkNotUsed(atts))
|
|
{
|
|
this->ReportUnknownElement(name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::EndElement(const char * vtkNotUsed(name))
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::CharacterDataHandler(const char* vtkNotUsed(inData),
|
|
int vtkNotUsed(inLength))
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::ReportStrayAttribute(const char* element, const char* attr,
|
|
const char* value)
|
|
{
|
|
vtkWarningMacro("Stray attribute in XML stream: Element " << element
|
|
<< " has " << attr << "=\"" << value << "\"");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::ReportMissingAttribute(const char* element,
|
|
const char* attr)
|
|
{
|
|
vtkErrorMacro("Missing attribute in XML stream: Element " << element
|
|
<< " is missing " << attr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::ReportBadAttribute(const char* element, const char* attr,
|
|
const char* value)
|
|
{
|
|
vtkErrorMacro("Bad attribute value in XML stream: Element " << element
|
|
<< " has " << attr << "=\"" << value << "\"");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::ReportUnknownElement(const char* element)
|
|
{
|
|
vtkErrorMacro("Unknown element in XML stream: " << element);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParser::ReportXmlParseError()
|
|
{
|
|
vtkErrorMacro("Error parsing XML in stream at line "
|
|
<< XML_GetCurrentLineNumber(static_cast<XML_Parser>(this->Parser))
|
|
<< ": " << XML_ErrorString(XML_GetErrorCode(static_cast<XML_Parser>(this->Parser))));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned long vtkXMLParser::GetXMLByteIndex()
|
|
{
|
|
return XML_GetCurrentByteIndex(static_cast<XML_Parser>(this->Parser));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::ParseBuffer(const char* buffer, unsigned int count)
|
|
{
|
|
// Pass the buffer to the expat XML parser.
|
|
if(!XML_Parse(static_cast<XML_Parser>(this->Parser), buffer, count, 0))
|
|
{
|
|
this->ReportXmlParseError();
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::ParseBuffer(const char* buffer)
|
|
{
|
|
return this->ParseBuffer(buffer, static_cast<int>(strlen(buffer)));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLParser::IsSpace(char c)
|
|
{
|
|
return isspace(c);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParserStartElement(void* parser, const char *name,
|
|
const char **atts)
|
|
{
|
|
// Begin element handler that is registered with the XML_Parser.
|
|
// This just casts the user data to a vtkXMLParser and calls
|
|
// StartElement.
|
|
static_cast<vtkXMLParser*>(parser)->StartElement(name, atts);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParserEndElement(void* parser, const char *name)
|
|
{
|
|
// End element handler that is registered with the XML_Parser. This
|
|
// just casts the user data to a vtkXMLParser and calls EndElement.
|
|
static_cast<vtkXMLParser*>(parser)->EndElement(name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLParserCharacterDataHandler(void* parser, const char* data,
|
|
int length)
|
|
{
|
|
// Character data handler that is registered with the XML_Parser.
|
|
// This just casts the user data to a vtkXMLParser and calls
|
|
// CharacterDataHandler.
|
|
static_cast<vtkXMLParser*>(parser)->CharacterDataHandler(data, length);
|
|
}
|
|
|