/*
 * 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;
}
