/* * e4xmlinputprocessor.cpp -- * * This file contains the implementation of the e4_XMLInputProcessor * 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 e4_XMLInputProcessor class: * * * *************************************************************************** */ /* * Default constructor */ e4_XMLInputProcessor::e4_XMLInputProcessor() : parser(NULL), ns(NULL), uri(NULL) { uc.Reset(); ds.Reset(); } /* * Constructor with parser argument. */ e4_XMLInputProcessor::e4_XMLInputProcessor(e4_XMLParser *p) : parser(p), ns(NULL), uri(NULL) { uc.Reset(); ds.Reset(); } /* * Destructor. */ e4_XMLInputProcessor::~e4_XMLInputProcessor() { /* * Reset the dynamic strings so as to free any character buffers. */ uc.Reset(); ds.Reset(); /* * If the namespace fields are non-NULL, free their memory. */ if (ns != NULL) { free(ns); } if (uri != NULL) { free(uri); } } /* * Process the start of a namespace scope. */ bool e4_XMLInputProcessor::ProcessStartNamespaceDecl(const char *prefix, const char *theuri) { if (prefix != NULL) { if (ns != NULL) { free(ns); } ns = strdup(prefix); } if (theuri != NULL) { if (uri != NULL) { free(uri); } uri = strdup(theuri); } return true; } /* * Process the end of a namespace scope. We ignore the prefix given. */ bool e4_XMLInputProcessor::ProcessEndNamespaceDecl(const char *prefix) { if (ns != NULL) { free(ns); ns = NULL; } if (uri != NULL) { free(uri); uri = NULL; } return true; } /* * Process the declaration of an unparsed entity. */ bool e4_XMLInputProcessor::ProcessUnparsedEntityDecl(const char *entityName, const char *base, const char *systemID, const char *publicID, const char *notationName) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Add a vertex named "__unparsedentity__" with a new node. */ if ((!parser->GetNodeVertexCreator()->AddNodeRef( n, "__unparsedentity__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add UNPARSEDENTITY section"); return false; } /* * Add up to five vertices for the specified arguments: */ if (entityName != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__entityname__", E4_IOLAST, rank, entityName, v)) { parser->FlagError("Could not add ENTITYNAME declaration"); return false; } } if (base != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__base__", E4_IOLAST, rank, base, v)) { parser->FlagError("Could not add BASE declaration"); return false; } } if (systemID != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__systemid__", E4_IOLAST, rank, systemID, v)) { parser->FlagError("Could not add SYSTEMID declaration"); return false; } } if (publicID != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__publicid__", E4_IOLAST, rank, publicID, v)) { parser->FlagError("Could not add PUBLICID declaration"); return false; } } if (notationName != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__notationname__", E4_IOLAST, rank, notationName, v)) { parser->FlagError("Could not add NOTATIONNAME declaration"); return false; } } return true; } /* * This method processes skipped entities. It creates a node that is * the value of a new vertex named "__skippedentity__" and whose vertices * are "__entityname__" and "__isparameterentity__". */ bool e4_XMLInputProcessor::ProcessSkippedEntity(const char *entityName, int isParameterEntity) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Add a vertex named "__skippedentity__" with a new node. */ if ((!parser->GetNodeVertexCreator()->AddNodeRef( n, "__skippedentity__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add SKIPPED ENTITY section"); return false; } /* * Add vertices for the entity name and the isParameterEntity int. */ if (entityName != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__entityname__", E4_IOLAST, rank, entityName, v)) { parser->FlagError("Could not add ENTITYNAME declaration"); return false; } } if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__isparameterentity__", E4_IOLAST, rank, isParameterEntity, v)) { parser->FlagError("Could not add ISPARAMETERENTITY declaration"); return false; } return true; } /* * This method processes notation declarations. */ bool e4_XMLInputProcessor::ProcessNotationDecl(const char *notationName, const char *base, const char *systemID, const char *publicID) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Add a vertex named "__notation__" with a new node. */ if ((!parser->GetNodeVertexCreator()->AddNodeRef( n, "__notation__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add NOTATION section"); return false; } /* * Add vertices for each of the given attributes. */ if (notationName != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__notationname__", E4_IOLAST, rank, notationName, v)) { parser->FlagError("Could not add NOTATIONNAME declaration"); return false; } } if (base != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__base__", E4_IOLAST, rank, base, v)) { parser->FlagError("Could not add BASE declaration"); return false; } } if (systemID != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__systemid__", E4_IOLAST, rank, systemID, v)) { parser->FlagError("Could not add SYSTEMID declaration"); return false; } } if (publicID != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__publicid__", E4_IOLAST, rank, publicID, v)) { parser->FlagError("Could not add PUBLICID declaration"); return false; } } return true; } /* * This method determines whether the data is all whitespace. */ bool e4_XMLInputProcessor::IsBlankCharData(const char *data, int len) { int i; for (i = 0; i < len; i++) { if ((data[i] != '\n') && (data[i] != ' ') && (data[i] != '\t')) { return false; } } return true; } /* * Process unclassified data (data that appears in the input but would * otherwise not be handled). * * Input char buffer is not null-terminated. */ bool e4_XMLInputProcessor::ProcessUnclassifiedData(const char *data, int len) { /* * Does the current invocation present unclassified data? */ if ((data != NULL) && (len > 0)) { /* * If we are in vertex-add, do not allow adding unclassified data! */ if (parser->InVertex()) { parser->FlagError("In vertex-add, cannot add unclassified data"); return false; } /* * Accummulate the data. */ uc.Append(data, len); } else { /* * If there is no more input data it means the end of the unclassified * data. Finish collecting the data and save it as the value of a * vertex named "__unclassifieddata__" in the current node. */ if (uc.Length() > 0) { int rank = 0; e4_Node n; e4_Vertex v; /* * Get the current node -- assume it is valid. */ parser->GetNode(n); /* * Add a vertex in the current node for holding the unclassified * data as a character string. */ if (!parser->GetNodeVertexCreator()->AddVertexRef( n, "__unclassifieddata__", E4_IOLAST, rank, uc.Get(), v)) { parser->FlagError( "Can't add unclassified data to current node"); uc.Reset(); return false; } /* * Ensure that the parser state reflects that this collection of * unclassified data is done. */ uc.Reset(); } } return true; } /* * Process character data (the data that appears between the start and end * tags of an xml element). * * Input char buffer is not null-terminated. */ bool e4_XMLInputProcessor::ProcessCharData(const char *data, int len) { /* * Does the current invocation present character data? */ if ((data != NULL) && (len > 0)) { /* * If we are in an old style vertex-add, do not allow adding data! */ if (parser->InVertex() && !parser->HasSavedVertex()) { parser->FlagError("In vertex-add, cannot add data"); return false; } /* * If the data is "artificial", ignore it. If we're parsing data * for a new style vertex, save the data even if it is blank. */ if (parser->HasSavedVertex() || !IsBlankCharData(data,len)) { /* * Append the new character data to the accumulating * character data. */ ds.Append(data, len); } } else { /* * If there is no more input data it means the end of the char data. * If there is any saved data, then if we're parsing a new style * vertex, just leave the data in the dynstring for the close vertex * to pick up. Otherwise, finish collecting the sequence of character * data and save it in the persistent storage, inside the current node * as the last vertex. The vertex name will be "__data__". */ if ((ds.Length() > 0) && !parser->HasSavedVertex()) { int rank = 0; e4_Node n; e4_Vertex v; /* * Get the current node - assume it is valid. */ parser->GetNode(n); /* * Add a vertex in the current node for holding the * character data string. Assume the current node is valid. */ if (!parser->GetNodeVertexCreator()->AddVertexRef(n, "__data__", E4_IOLAST, rank, ds.Get(), v)) { parser->FlagError("Can't add data to current node"); ds.Reset(); return false; } /* * Ensure that the parser state reflects that this * collection of character data sequence is done, by * resetting the dynamic string to empty. */ ds.Reset(); } } return true; } /* * Process the start of an element. */ bool e4_XMLInputProcessor::ProcessElementBegin(const char* name, const char** attributes) { int id = -1, startattr = 0, rank = 0, ud = 0, vud = 0, nud = 0; e4_Node n; e4_Node nn; e4_Node nna; e4_Vertex v; /* * Get the current node - guaranteed to be valid in here. */ parser->GetNode(n); /* * If this element represents a new format vertex, handle it * specially. */ if ((strcmp(name, "__vertex__") == 0) && (attributes != NULL) && (attributes[0] != NULL) && (strcmp(attributes[0], "name") == 0) && (attributes[1] != NULL) && (attributes[2] != NULL) && (strcmp(attributes[2], "type") == 0) && (attributes[3] != NULL) && (attributes[4] != NULL) && (strcmp(attributes[4], "length") == 0) && (attributes[5] != NULL)) { /* * For now we ignore the length=".." attribute. We may be able to use * it effectively later, who knows. */ /* * We're now within a vertex, so flag that. If the call to EnterVertex * fails, it can only be because we're already in one and we bail out. */ if (!parser->EnterVertex()) { return false; } /* * Check for optional __vertexuserdata__. */ if (attributes[6] == NULL) { ud = 0; } else { if ((strcmp(attributes[6], "__vertexuserdata__") == 0) && (attributes[7] != NULL) && (attributes[8] == NULL)) { ud = atoi(attributes[7]); } else { parser->FlagError("invalid vertex!"); return false; } } if (!parser->GetNodeVertexCreator()->AddVertex(n, attributes[1], attributes[3], ud)) { return false; } return true; } /* * If this element represents a vertex, handle it specially. */ if ((strcmp(name, "__vertex__") == 0) && (attributes != NULL) && (attributes[0] != NULL) && (strcmp(attributes[0], "name") == 0) && (attributes[1] != NULL) && (attributes[2] != NULL) && (strcmp(attributes[2], "type") == 0) && (attributes[3] != NULL) && (attributes[4] != NULL) && (strcmp(attributes[4], "value") == 0) && (attributes[5] != NULL)) { /* * We're now within a vertex, so flag that. If the call to EnterVertex * fails, it can only be because we're already in one and we bail out. */ if (!parser->EnterVertex()) { return false; } /* * Check for optional __vertexuserdata__. */ if (attributes[6] == NULL) { ud = 0; } else { if ((strcmp(attributes[6], "__vertexuserdata__") == 0) && (attributes[7] != NULL) && (attributes[8] == NULL)) { ud = atoi(attributes[7]); } else { parser->FlagError("invalid vertex!"); return false; } } if (!parser->GetNodeVertexCreator()->AddVertex(n, attributes[1], attributes[3], attributes[5], ud)) { return false; } return true; } /* * If this element represents a backwards reference to a node * that was already parsed, handle it specially. */ if ((strcmp(name, "__nodebackref__") == 0) && (attributes != NULL) && (attributes[0] != NULL) && (strcmp(attributes[0], "__nodeid__") == 0) && (attributes[1] != NULL) && (attributes[2] != NULL) && (strcmp(attributes[2], "__name__") == 0) && (attributes[3] != NULL)) { /* * Check for optional __vertexuserdata__. */ if (attributes[4] == NULL) { ud = 0; } else { if ((strcmp(attributes[4], "__vertexuserdata__") == 0) && (attributes[5] != NULL) && (attributes[6] == NULL)) { ud = atoi(attributes[5]); } else { parser->FlagError("invalid node back reference!"); return false; } } return parser->GetNodeVertexCreator()->AddNodeBackRef(n, attributes[3], attributes[1], ud); } /* * Parse an optional __nodeid__ attribute. */ if ((attributes != NULL) && (attributes[startattr] != NULL)) { if (strcmp(attributes[startattr], "__nodeid__") == 0) { id = atoi(attributes[startattr + 1]); startattr += 2; } } /* * Parse an optional __nodeuserdata__ spec. */ if ((attributes != NULL) && (attributes[startattr] != NULL)) { if (strcmp(attributes[startattr], "__nodeuserdata__") == 0) { nud = atoi(attributes[startattr + 1]); startattr += 2; } } /* * Parse an optional __vertexuserdata__ spec. */ if ((attributes != NULL) && (attributes[startattr] != NULL)) { if (strcmp(attributes[startattr], "__vertexuserdata__") == 0) { vud = atoi(attributes[startattr + 1]); startattr += 2; } } /* * Otherwise the new element will be represented as a new node. Create * the node. */ if (!parser->GetNodeVertexCreator()->AddNodeRef(n, name, E4_IOLAST, rank, nn, v, nud, vud)) { parser->FlagError("Can't add node"); return false; } /* * If a __nodeid__ was specified, hash the node. */ if (id != -1) { parser->GetNodeVertexCreator()->HashNode(nn, id); } /* * Parse attached attributes. These get turned into vertices inside * an embedded node named __attributes__. */ if ((ns != NULL) || ((attributes != NULL) && (attributes[startattr] != NULL))) { if (!parser->GetNodeVertexCreator()->AddNodeRef(nn, "__attributes__", E4_IOLAST, rank, nna, v, 0, 0)) { parser->FlagError("Can't add attributes"); return false; } /* * If there was a namespace declaration, it'll be left in the * ns instance variable by the handler. We pick it up * here and make an attribute out of it. */ if (ns != NULL) { if (!parser->GetNodeVertexCreator()-> AddVertexRef(nna, "__namespaceprefix__", E4_IOLAST, rank, ns, v)) { parser->FlagError("Can't add namespace prefix attribute"); return false; } if ((uri != NULL) && (!parser->GetNodeVertexCreator()-> AddVertexRef(nna, "__namespaceuri__", E4_IOLAST, rank, uri, v))) { parser->FlagError("Can't add namespace uri attribute"); return false; } } /* * If there were real attributes, make them into vertices in * the __attributes__ node. */ if ((attributes != NULL) && (attributes[startattr] != NULL)) { for (; attributes[startattr] != NULL; startattr += 2) { if (!parser->GetNodeVertexCreator()-> AddVertexRef(nna, attributes[startattr], E4_IOLAST, rank, attributes[startattr + 1], v)) { parser->FlagError("Can't add attribute"); return false; } } } } /* * Record the current depth of recursion. */ parser->IncrDepth(); /* * Set the current node (where nested elements are added) to 'nn'. */ parser->SetNode(nn); return true; } /* * Process the end of an element. */ bool e4_XMLInputProcessor::ProcessElementEnd(const char *name) { e4_Node n; e4_Node nn; /* * If the current element being closed was a vertex, handle * specially. */ if (parser->InVertex()) { parser->ExitVertex(); /* * We might be parsing a new style vertex, so we need to set * the saved vertex's value. */ if (parser->HasSavedVertex() && !parser->AssignVertex(ds)) { ds.Reset(); return false; } ds.Reset(); return true; } /* * If the current element being closed is a back reference, * don't pop the node stack. */ if (strcmp(name, "__nodebackref__") == 0) { return true; } /* * It's a regular node. Parse it and all sub-elements. */ if (!parser->GetNode(nn)) { parser->FlagError("Can't get current node from parser"); return false; } if (!nn.IsValid()) { parser->FlagError("Invalid node"); return false; } if (!nn.GetParent(n)) { parser->FlagError("Can't get parent of current node"); return false; } /* * We're done with the child node, reset the current node to its parent. * Also decrement the nesting level. */ parser->DecrDepth(); parser->SetNode(n); return true; } /* * Process a comment in the xml input. */ bool e4_XMLInputProcessor::ProcessComment(const char *comment) { e4_Node n; e4_Vertex v; int rank = 0; /* * If we are in a vertex-add, do not allow adding a comment! */ if (parser->InVertex()) { parser->FlagError("In vertex-add, cannot add comment"); return false; } /* * Check the current node is valid. */ if (!parser->GetNode(n)) { parser->FlagError("Can't get current node from parser"); return false; } if (!n.IsValid()) { parser->FlagError("Invalid node"); return false; } /* * Add a "__comment__" vertex with the comment string as value. */ if (!parser->GetNodeVertexCreator()->AddVertexRef(n, "__comment__", E4_IOLAST, rank, comment, v)) { parser->FlagError("Could not add comment"); return false; } return true; } /* * Process the XML declaration. */ bool e4_XMLInputProcessor::ProcessXMLDeclaration(const char* version, const char* encoding, int standalone) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Create a node named "__xml__". */ if ((!parser->GetNodeVertexCreator()->AddNodeRef(n, "__xml__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid())) { parser->FlagError("Could not add XML declaration"); return false; } /* * Potentially add three vertices named "__version__", "__encoding__" and * "__standalone__". */ if (version != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__version__", E4_IOLAST, rank, version, v)) { parser->FlagError("Could not add XML declaration"); return false; } } if (encoding != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__encoding__", E4_IOLAST, rank, encoding, v)) { parser->FlagError("Could not add XML declaration"); return false; } } if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__standalone__", E4_IOLAST, rank, standalone, v)) { parser->FlagError("Could not add XML declaration"); return false; } return true; } /* * Process the start of an XML DTD (Document Type Declaration) */ bool e4_XMLInputProcessor::ProcessDTDBegin(const char *doctypename, const char *sysid, const char *pubid, int hasinternalsubset) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Create a node named "__doctypedecl__". */ if ((!parser->GetNodeVertexCreator()->AddNodeRef(n, "__doctypedecl__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add DOCTYPE declaration"); return false; } /* * Add the specified vertices. */ if (doctypename != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__doctypename__", E4_IOLAST, rank, doctypename, v)) { parser->FlagError("Could not add DOCTYPE declaration"); return false; } } if (sysid != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__sysid__", E4_IOLAST, rank, sysid, v)) { parser->FlagError("Could not add DOCTYPE declaration"); return false; } } if (pubid != NULL) { if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__pubid__", E4_IOLAST, rank, pubid, v)) { parser->FlagError("Could not add DOCTYPE declaration"); return false; } } if (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__hasinternalsubset__", E4_IOLAST, rank, hasinternalsubset, v)) { parser->FlagError("Could not add DOCTYPE declaration"); return false; } /* * Record the current depth of recursion. */ parser->IncrDepth(); /* * Set the current node (where nested elements are added) to 'nn'. */ parser->SetNode(nn); return true; } /* * Process the end of an XML DTD (Document Type Declaration) */ bool e4_XMLInputProcessor::ProcessDTDEnd() { e4_Node n,nn; /* * Get the current node. */ parser->GetNode(n); /* * Get the parent of the current node. */ if ((!n.GetParent(nn)) || (!nn.IsValid())) { parser->FlagError("Could not close DOCTYPE declaration section"); return false; } /* * We're done with the child node, reset the current node to its parent. * Also decrement the nesting level. */ parser->DecrDepth(); parser->SetNode(nn); return true; } /* * Process processing instructions in the XML stream. */ bool e4_XMLInputProcessor::ProcessInstructions(const char* target, const char* data) { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Add a vertex named "__processinginstruction__" with a new node. */ if ((!parser->GetNodeVertexCreator()->AddNodeRef( n, "__processinginstruction__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add PROCESSINGINSTRUCTION section"); return false; } /* * Add two vertices, named "__target__" and "__data__". */ if ((!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__target__", E4_IOLAST, rank, target, v)) || (!parser->GetNodeVertexCreator()->AddVertexRef(nn, "__data__", E4_IOLAST, rank, data, v))) { parser->FlagError("Could not add PROCESSINGINSTRUCTION section"); return false; } return true; } /* * Process the start of an XML CDATA section. */ bool e4_XMLInputProcessor::ProcessCDATABegin() { e4_Node n, nn; e4_Vertex v; int rank = 0; /* * Get the current node. */ parser->GetNode(n); /* * Add a vertex named "__cdata__" with a new node. */ if ((!parser->GetNodeVertexCreator()->AddNodeRef(n, "__cdata__", E4_IOLAST, rank, nn, v, 0, 0)) || (!nn.IsValid()) || (!v.IsValid())) { parser->FlagError("Could not add CDATA section"); return false; } /* * Record the current depth of recursion. */ parser->IncrDepth(); /* * Set the current node (where nested elements are added) to 'nn'. */ parser->SetNode(nn); return true; } /* * Process the end of an XML CDATA section. */ bool e4_XMLInputProcessor::ProcessCDATAEnd() { e4_Node n, nn; /* * Get the current node. */ parser->GetNode(n); /* * Get the parent of this node. */ if (!n.GetParent(nn) || !nn.IsValid()) { parser->FlagError("Could not close CDATA section"); return false; } /* * We're done with the child node, reset the current node to its parent. * Also decrement the nesting level. */ parser->DecrDepth(); parser->SetNode(nn); return true; }