/*========================================================================= Program: Visualization Toolkit Module: vtkXMLParser.cxx 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. =========================================================================*/ #include "vtkXMLParser.h" #include "vtkObjectFactory.h" #include "vtk_expat.h" #include "vtksys/FStream.hxx" #include #include vtkStandardNewMacro(vtkXMLParser); //------------------------------------------------------------------------------ vtkXMLParser::vtkXMLParser() { this->Stream = nullptr; this->Parser = nullptr; this->FileName = nullptr; this->Encoding = nullptr; this->InputString = nullptr; this->InputStringLength = 0; this->ParseError = 0; this->IgnoreCharacterData = 0; } //------------------------------------------------------------------------------ vtkXMLParser::~vtkXMLParser() { this->SetStream(nullptr); this->SetFileName(nullptr); this->SetEncoding(nullptr); } //------------------------------------------------------------------------------ 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"; os << indent << "IgnoreCharacterData: " << (this->IgnoreCharacterData ? "On" : "Off") << endl; os << indent << "Encoding: " << (this->Encoding ? this->Encoding : "(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 } //------------------------------------------------------------------------------ vtkTypeInt64 vtkXMLParser::TellG() { // Standard tellg returns -1 if fail() is true. if (!this->Stream || vtkXMLParserFail(this->Stream)) { return -1; } return this->Stream->tellg(); } //------------------------------------------------------------------------------ void vtkXMLParser::SeekG(vtkTypeInt64 position) { // Standard seekg does nothing if fail() is true. if (!this->Stream || vtkXMLParserFail(this->Stream)) { return; } this->Stream->seekg(std::streampos(position)); } //------------------------------------------------------------------------------ int vtkXMLParser::Parse(const char* inputString) { this->InputString = inputString; this->InputStringLength = -1; int result = this->Parse(); this->InputString = nullptr; return result; } //------------------------------------------------------------------------------ int vtkXMLParser::Parse(const char* inputString, unsigned int length) { this->InputString = inputString; this->InputStringLength = length; int result = this->Parse(); this->InputString = nullptr; this->InputStringLength = -1; return result; } //------------------------------------------------------------------------------ int vtkXMLParser::Parse() { // Select source of XML vtksys::ifstream ifs; if (!this->InputString && !this->Stream && this->FileName) { // If it is file, open it and set the appropriate stream vtksys::SystemTools::Stat_t fs; if (vtksys::SystemTools::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->CreateParser(); XML_SetElementHandler( static_cast(this->Parser), &vtkXMLParserStartElement, &vtkXMLParserEndElement); if (!this->IgnoreCharacterData) { XML_SetCharacterDataHandler( static_cast(this->Parser), &vtkXMLParserCharacterDataHandler); } else { XML_SetCharacterDataHandler(static_cast(this->Parser), nullptr); } XML_SetUserData(static_cast(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(this->Parser), "", 0, 1)) { this->ReportXmlParseError(); result = 0; } } // Clean up the parser. XML_ParserFree(static_cast(this->Parser)); this->Parser = nullptr; // If the source was a file, reset the stream if (this->Stream == &ifs) { this->Stream = nullptr; } return result; } //------------------------------------------------------------------------------ int vtkXMLParser::CreateParser() { if (this->Parser) { vtkErrorMacro("Parser already created"); return 0; } // Create the expat XML parser. this->Parser = XML_ParserCreate(this->Encoding); return this->Parser ? 1 : 0; } //------------------------------------------------------------------------------ int vtkXMLParser::InitializeParser() { // Create the expat XML parser. if (!this->CreateParser()) { vtkErrorMacro("Parser already initialized"); this->ParseError = 1; return 0; } XML_SetElementHandler( static_cast(this->Parser), &vtkXMLParserStartElement, &vtkXMLParserEndElement); if (!this->IgnoreCharacterData) { XML_SetCharacterDataHandler( static_cast(this->Parser), &vtkXMLParserCharacterDataHandler); } else { XML_SetCharacterDataHandler(static_cast(this->Parser), nullptr); } XML_SetUserData(static_cast(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(this->Parser), "", 0, 1)) { this->ReportXmlParseError(); result = 0; } } // Clean up the parser. XML_ParserFree(static_cast(this->Parser)); this->Parser = nullptr; 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(this->Parser)) << ", column " << XML_GetCurrentColumnNumber(static_cast(this->Parser)) << ", byte index " << XML_GetCurrentByteIndex(static_cast(this->Parser)) << ": " << XML_ErrorString(XML_GetErrorCode(static_cast(this->Parser)))); } //------------------------------------------------------------------------------ vtkTypeInt64 vtkXMLParser::GetXMLByteIndex() { return XML_GetCurrentByteIndex(static_cast(this->Parser)); } //------------------------------------------------------------------------------ int vtkXMLParser::ParseBuffer(const char* buffer, unsigned int count) { // Pass the buffer to the expat XML parser. if (!XML_Parse(static_cast(this->Parser), buffer, count, 0)) { this->ReportXmlParseError(); return 0; } return 1; } //------------------------------------------------------------------------------ int vtkXMLParser::ParseBuffer(const char* buffer) { return this->ParseBuffer(buffer, static_cast(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(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(parser)->EndElement(name); }