/* * e4xmlparser.cpp -- * * This file contains the implementation of the e4_XMLParser * class defined in e4xml.h. * * Authors: Jacob Levy and Jean-Claude Wippler. * jyl@best.com jcw@equi4.com * * Copyright: JYL Software, Inc., (c) 2000-2003. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "e4xml.h" /* *************************************************************************** * * * Implementation of the e4_XMLParser class: * * * *************************************************************************** */ /* * This procedure is called for the start of each XML element. */ void e4_XMLParser::HandleStartElement(void *userData, const char *name, const char **attributes) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the element beginning. */ (void) p->ProcessElementBegin(name, attributes); } /* * This procedure is called for the end of each XML element. */ void e4_XMLParser::HandleEndElement(void *userData, const char *name) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessElementEnd(name); } /* * This procedure is called to parse an XML comment. */ void e4_XMLParser::HandleComment(void *userData, const char *comment) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the comment. */ (void) p->ProcessComment(comment); } /* * This procedure handles the start of a CDATA section in the XML input. */ void e4_XMLParser::HandleStartCDATA(void *userData) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessCDATABegin(); } /* * This procedure handles the end of a CDATA section. */ void e4_XMLParser::HandleEndCDATA(void *userData) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessCDATAEnd(); } /* * This procedure handles a processing instruction XML input element. */ void e4_XMLParser::HandleProcessingInstructions(void *userData, const char *target, const char *data) { e4_XMLParser *p = (e4_XMLParser *) userData; e4_Node n; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessInstructions(target, data); } /* * This procedure is called to handle an XML declaration. */ void e4_XMLParser::HandleXMLDeclaration(void *userData, const char *version, const char *encoding, int standalone) { e4_XMLParser *p = (e4_XMLParser *) userData; e4_Node n; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessXMLDeclaration(version, encoding, standalone); } /* * This procedure is called to handle the start of a document decl XML * input element. */ void e4_XMLParser::HandleStartDocType(void *userData, const char *doctypename, const char *sysid, const char *pubid, int hasinternalsubset) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessDTDBegin(doctypename, sysid, pubid, hasinternalsubset); } /* * This procedure is called to handle the end of a doctype declaration. */ void e4_XMLParser::HandleEndDocType(void *userData) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } (void) p->ProcessDTDEnd(); } /* * This procedure is called to handle default data (data that would otherwise * not be handled at all). */ void e4_XMLParser::HandleDefaultData(void *userData, const char *data, int len) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the data. */ (void) p->ProcessDefaultData(data, len); } /* * This procedure is called to handle character data contained within * XML elements. */ void e4_XMLParser::HandleCharData(void *userData, const char *data, int len) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the actual data. */ (void) p->ProcessCharData(data, len); } /* * This procedure is called to handle the start of a namespace scope. */ void e4_XMLParser::HandleStartNamespaceDecl(void *userData, const char *prefix, const char *uri) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the actual data. */ (void) p->ProcessStartNamespaceDecl(prefix, uri); } /* * This procedure is called to handle the start of a namespace scope. */ void e4_XMLParser::HandleEndNamespaceDecl(void *userData, const char *prefix) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the actual data. */ (void) p->ProcessEndNamespaceDecl(prefix); } /* * This procedure is called to handle the declaration of an unparsed entity. */ void e4_XMLParser::HandleUnparsedEntityDecl(void *userData, const char *entityName, const char *base, const char *systemID, const char *publicID, const char *notationName) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the actual data. */ (void) p->ProcessUnparsedEntityDecl(entityName, base, systemID, publicID, notationName); } /* * This procedure is called to handle a notation declaration. */ void e4_XMLParser::HandleNotationDecl(void *userData, const char *notationName, const char *base, const char *systemID, const char *publicID) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the declaration. */ (void) p->ProcessNotationDecl(notationName, base, systemID, publicID); } #ifdef NOTDEF /* * This procedure is called to handle a skipped entity. A skipped entity is * an entity for which no declaration is given and which occurs in a situation * where its occurrence is not an error. */ void e4_XMLParser::HandleSkippedEntity(void *userData, const char *entityName, int isParameterEntity) { e4_XMLParser *p = (e4_XMLParser *) userData; /* * If there was an error before this call, do nothing more so that * the state will be at least partially correct! */ if ((p == NULL) || (p->HasError() == true)) { return; } /* * Process the skipped entity. */ (void) p->ProcessSkippedEntity(entityName, isParameterEntity); } #endif /* * This constructor creates an empty XML parser. */ e4_XMLParser::e4_XMLParser() : ready(false), n(invalidNode), s(invalidStorage), inVertex(false), parser(NULL), depth(0), started(false), error(false), errorString(NULL), base64bytes(NULL), inputProcessor(&defaultXMLInputProcessor), nodeVertexCreator(&defaultNodeVertexCreator) { inputProcessor->SetParser(this); nodeVertexCreator->SetParser(this); } /* * This constructor creates an XML parser from a given node and * stores all XML input in an e4Graph under the given node. */ e4_XMLParser::e4_XMLParser(e4_Node nn) : ready(true), n(nn), inVertex(false), parser(NULL), depth(0), started(false), error(false), errorString(NULL), base64bytes(NULL), inputProcessor(&defaultXMLInputProcessor), nodeVertexCreator(&defaultNodeVertexCreator) { ConstructParser(); (void) n.GetStorage(s); inputProcessor->SetParser(this); nodeVertexCreator->SetParser(this); } /* * The destructor deletes the instance parser if one was created, and * sets the instance node to invalidNode to ensure that the refcount is * decremented properly. */ e4_XMLParser::~e4_XMLParser() { if (parser != NULL) { XML_ParserFree(parser); } n = invalidNode; s = invalidStorage; if (base64bytes != NULL) { free(base64bytes); } } /* * Is this parse finished? */ bool e4_XMLParser::Finished() { if ((started == true) && (depth <= 0)) { return true; } return false; } /* * Get the storage associated with this parser. */ bool e4_XMLParser::GetStorage(e4_Storage &ss) const { ss = s; return true; } /* * This operation assigns a node to the parser. This allows the parser * to switch where it is going to insert new elements. * * NOTE: Care must be taken to only switch nodes between parse operations * and not during a parse operation, or unpredictable behavior may result. */ void e4_XMLParser::SetNode(e4_Node nn) { if (inVertex == false) { n = nn; n.GetStorage(s); } else { FlagError("Can't set node while inside vertex!"); } } /* * This operation returns the current node in nn. */ bool e4_XMLParser::GetNode(e4_Node &nn) const { nn = n; return true; } /* * This operation begins a vertex-add. */ bool e4_XMLParser::EnterVertex() { if (inVertex) { FlagError("Already inside a vertex!"); return false; } inVertex = true; return true; } /* * This operation checks whether we're currently inside a vertex-add. */ bool e4_XMLParser::InVertex() const { return inVertex; } /* * This operation finishes a vertex-add. */ void e4_XMLParser::ExitVertex() { if (inVertex == false) { FlagError("Not in vertex-add!"); } inVertex = false; } /* * This operation constructs a new parser from the file pointer and * node stored already in the instance. It returns true if the construction * succeeded. */ bool e4_XMLParser::ConstructParser() { error = false; errorString = NULL; if (!n.IsValid()) { ready = false; } else { if (parser != NULL) { XML_ParserFree(parser); } parser = XML_ParserCreate(NULL); if (parser == NULL) { ready = false; } else { ready = true; XML_SetUserData(parser, this); XML_SetElementHandler(parser, HandleStartElement, HandleEndElement); XML_SetCommentHandler(parser, HandleComment); XML_SetCdataSectionHandler(parser, HandleStartCDATA, HandleEndCDATA); XML_SetProcessingInstructionHandler(parser, HandleProcessingInstructions); XML_SetXmlDeclHandler(parser, HandleXMLDeclaration); XML_SetCharacterDataHandler(parser, HandleCharData); XML_SetDoctypeDeclHandler(parser, HandleStartDocType, HandleEndDocType); XML_SetDefaultHandlerExpand(parser, HandleDefaultData); XML_SetUnparsedEntityDeclHandler(parser, HandleUnparsedEntityDecl); XML_SetNotationDeclHandler(parser, HandleNotationDecl); #ifdef NOTDEF XML_SetSkippedEntityHandler(parser, HandleSkippedEntity); #endif XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); } } return ready; } /* * This operation parses a buffer of data. */ bool e4_XMLParser::Parse(char *buf, size_t len) { if (!ready) { return false; } error = false; started = true; /* * Parse and check for Expat errors as well as errors generated from * the e4xml routines. */ if (!XML_Parse(parser, buf, len, false) || (error == true)) { error = true; if (errorString == NULL) { FlagError("Input following XML expression"); } return false; } return true; } /* * This operation flags an error and stores an error string into the * parser. */ void e4_XMLParser::FlagError(const char *msg) { char *format = "Error: %s (line %d, column %d, byte %d)"; error = true; errorString = new char[50 /* strlen(format) */ + strlen(msg) + 64]; sprintf(errorString, format, msg, XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser), XML_GetCurrentByteIndex(parser)); } /* * This operation returns true if there was an error in a previous * operation that has not yet been cleared. */ bool e4_XMLParser::HasError() { return error; } /* * This operation retrieves the error string if there was an error. */ const char * e4_XMLParser::ErrorString() { if (errorString == NULL) { return ""; } return (const char *) errorString; } /* * This operation clears an error state from the parser. */ void e4_XMLParser::ClearError() { error = false; errorString = NULL; } /* * This operation decodes a BASE64 encoded string into a binary value. */ byte * e4_XMLParser::Base64_Decode(const char *base64str, int *len) { if (base64bytes != NULL) { free(base64bytes); } base64bytes = base64_decode(base64str, len); return base64bytes; } /* * Process the declaration of an unparsed entity. */ bool e4_XMLParser::ProcessUnparsedEntityDecl(const char *entityName, const char *base, const char *systemID, const char *publicID, const char *notationName) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the declaration: */ return GetXMLProcessor()->ProcessUnparsedEntityDecl(entityName, base, systemID, publicID, notationName); } /* * Process a notation declaration. */ bool e4_XMLParser::ProcessNotationDecl(const char *notationName, const char *base, const char *systemID, const char *publicID) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the declaration: */ return GetXMLProcessor()->ProcessNotationDecl(notationName, base, systemID, publicID); } /* * Process a skipped entity. */ bool e4_XMLParser::ProcessSkippedEntity(const char *entityName, int isParameterEntity) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the skipped entity: */ return GetXMLProcessor()->ProcessSkippedEntity(entityName, isParameterEntity); } /* * Process default data. */ bool e4_XMLParser::ProcessDefaultData(const char *data, int len) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the unclassified data. */ return GetXMLProcessor()->ProcessUnclassifiedData(data, len); } /* * Process character data. */ bool e4_XMLParser::ProcessCharData(const char *data, int len) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the character data. */ return GetXMLProcessor()->ProcessCharData(data, len); } /* * Process the start of a namespace scope. */ bool e4_XMLParser::ProcessStartNamespaceDecl(const char *prefix, const char *uri) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the start of the namespace scope. */ return GetXMLProcessor()->ProcessStartNamespaceDecl(prefix, uri); } /* * Process the end of a namespace scope. */ bool e4_XMLParser::ProcessEndNamespaceDecl(const char *prefix) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Process the start of the namespace scope. */ return GetXMLProcessor()->ProcessEndNamespaceDecl(prefix); } /* * This operation starts the processing of an XML element. */ bool e4_XMLParser::ProcessElementBegin(const char *name, const char **attributes) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Then process the element beginning. */ return GetXMLProcessor()->ProcessElementBegin(name, attributes); } /* * Process an XML element end. */ bool e4_XMLParser::ProcessElementEnd(const char *name) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Then process the element beginning. */ return GetXMLProcessor()->ProcessElementEnd(name); } /* * Process an XML comment */ bool e4_XMLParser::ProcessComment(const char *comment) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Then process the element beginning. */ return GetXMLProcessor()->ProcessComment(comment); } /* * Process the beginning of a CDATA element. */ bool e4_XMLParser::ProcessCDATABegin() { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot add CDATA section"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Start the CDATA section. */ return GetXMLProcessor()->ProcessCDATABegin(); } /* * Process the end of a CDATA section. */ bool e4_XMLParser::ProcessCDATAEnd() { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot close CDATA section"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Process the end of the CDATA section. */ return GetXMLProcessor()->ProcessCDATAEnd(); } /* * Process XML processing instructions. */ bool e4_XMLParser::ProcessInstructions(const char *target, const char *data) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot process XML processing instructions"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Process the XML instructions. */ return GetXMLProcessor()->ProcessInstructions(target, data); } /* * Process XML declaration. */ bool e4_XMLParser::ProcessXMLDeclaration(const char *version, const char *encoding, int standalone) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot process XML declaration"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Process the XML declaration. */ return GetXMLProcessor()->ProcessXMLDeclaration(version, encoding, standalone); } /* * Process the start of an XML DTD. */ bool e4_XMLParser::ProcessDTDBegin(const char *doctypename, const char *sysid, const char *pubid, int hasinternalsubset) { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot process XML DTD"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Process the beginning of the DTD element. */ return GetXMLProcessor()->ProcessDTDBegin(doctypename, sysid, pubid, hasinternalsubset); } /* * Process the end of an XML DTD. */ bool e4_XMLParser::ProcessDTDEnd() { if (!n.IsValid()) { FlagError("Invalid node"); return false; } /* * If we're in the process of adding a vertex, error out. */ if (InVertex()) { FlagError("In vertex-add, cannot process end of XML DTD"); return false; } /* * Finish any open unclassified data. */ (void) GetXMLProcessor()->ProcessUnclassifiedData(NULL, 0); /* * Finish any open character data. */ (void) GetXMLProcessor()->ProcessCharData(NULL, 0); /* * Start the end of the DTD element. */ return GetXMLProcessor()->ProcessDTDEnd(); } /* * Set the value of the new style vertex saved in the parser to the string * retrieved from the passed dynamic string. */ bool e4_XMLParser::AssignVertex(e4_DString &ds) { e4_Vertex v; const void *base64bytes; int len; if (savedvertex == invalidVertex) { return false; } v = savedvertex; savedvertex = invalidVertex; if (v.Type() == E4_VTSTRING) { v.Set(ds.Get()); /* * Finished adding the new vertex, fire the completion event. */ CauseVertexCompletionEvent(v, NULL); return true; } if (v.Type() == E4_VTBINARY) { base64bytes = (const void *) base64_decode(ds.Get(), &len); if (base64bytes == NULL) { return false; } v.Set(base64bytes, len); free((char *) base64bytes); /* * Finished adding the new vertex, fire the completion event. */ CauseVertexCompletionEvent(v, NULL); return true; } return false; } /* * Mechanism for generating callbacks on completion of vertex parsing. */ /* * This variable contains the event ID for the vertex completion event. */ static int vertexCompleteCB = -1; bool e4_XMLParser::DeclareVertexCompletionCallback(e4_CallbackFunction fn, void *clientData) { /* * Ensure the event code is defined. */ if ((vertexCompleteCB == -1) || !e4_Storage::IsEventCodeDefined(vertexCompleteCB)) { if (!e4_Storage::DefineEventCode(vertexCompleteCB)) { return false; } } /* * Only install the callback if the storage is valid. */ if (!s.IsValid() || !s.DeclareCallback(vertexCompleteCB, fn, clientData)) { return false; } return true; } bool e4_XMLParser::DeleteVertexCompletionCallback(e4_CallbackFunction fn, void *clientData) { /* * Only do something if the event code is defined. */ if ((vertexCompleteCB == -1) || !e4_Storage::IsEventCodeDefined(vertexCompleteCB)) { return false; } /* * Only do something if the storage is valid. */ if (!s.IsValid() || !s.DeleteCallback(vertexCompleteCB, fn, clientData)) { return false; } return true; } bool e4_XMLParser::CauseVertexCompletionEvent(const e4_Vertex &v, void *csdata) { /* * Only do something if the event code is defined. */ if ((vertexCompleteCB == -1) || !e4_Storage::IsEventCodeDefined(vertexCompleteCB)) { return false; } /* * Only do something if the storage is valid. */ if (!s.IsValid() || !s.CauseEvent(vertexCompleteCB, v, csdata)) { return false; } return true; }