/*
 * e4xmlnvg.cpp --
 *
 *	This file contains the implementation of the e4_XMLNodeVertexGenerator
 *	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_XMLNodeVertexCreator class:                        *
 *                                                                         *
 ***************************************************************************
 */

/*
 * Default constructor:
 */

e4_XMLNodeVertexCreator::e4_XMLNodeVertexCreator()
    : parser(NULL)
{
    nodesSeen = NULL;
}

/*
 * Constructor taking parser as argument:
 */

e4_XMLNodeVertexCreator::e4_XMLNodeVertexCreator(e4_XMLParser *p)
    : parser(p)
{
    nodesSeen = e4_NewHashTable(E4_ONE_WORD_KEY);
}

/*
 * Destructor, declared virtual for derived classes.
 */

e4_XMLNodeVertexCreator::~e4_XMLNodeVertexCreator()
{
    if (nodesSeen != NULL) {
	e4_DeleteHashTable(nodesSeen);
    }
}

/*
 * This operation stores a unique ID for the new node in the nodesSeen
 * hash table so that subsequent back references to it can be made.
 */

void
e4_XMLNodeVertexCreator::HashNode(e4_Node nn, int id) const
{
   e4_NodeUniqueID nuid;
   e4_HashEntry *ePtr;
   int isnew, i;

   ePtr = E4_CREATEHASHENTRY(nodesSeen, (char *) id, &isnew);

   (void) nn.GetUniqueID(nuid);
   i = nuid.GetUniqueID();
   E4_SETHASHVALUE(ePtr, i);
}

/*
 * This operation adds a back reference to a node that was already
 * parsed.
 */

bool
e4_XMLNodeVertexCreator::AddNodeBackRef(e4_Node &n,
					const char *nm,
					const char *nid,
					int ud)
{
    int index, rank, id;
    e4_HashEntry *ePtr;
    e4_Node bn;
    e4_Vertex v;
    e4_Storage ss;

    index = atoi(nid);
    ePtr = E4_FINDHASHENTRY(nodesSeen, (char *) index);
    if (ePtr == NULL) {
	parser->FlagError("Invalid node back reference!");
	return false;
    }

    e4_NodeUniqueID nuid;
    id = (int) E4_GETHASHVALUE(ePtr);
    (void) parser->GetStorage(ss);
    nuid.SetUniqueID(id,ss);

    if ((!ss.GetNodeFromID(nuid, bn)) || (!bn.IsValid())) {
	parser->FlagError("Invalid node back reference!");
	return false;
    }

    if (!AddVertexRef(n, nm, E4_IOLAST, rank, bn, v) || !v.IsValid()) {
	parser->FlagError("Invalid node back reference!");
	return false;
    }
    v.SetUserData(ud);

    /*
     * Finished making the new vertex, fire the completion event.
     */

    (void) parser->CauseVertexCompletionEvent(v, NULL);

    return true;
}

/*
 * Add a vertex that will be set to a new node.
 */

bool
e4_XMLNodeVertexCreator::AddNodeRef(e4_Node &n,
				    const char* name,
				    e4_InsertOrder io, 
				    int &rank,
				    e4_Node &nn,
				    e4_Vertex &v,
				    int nud,
				    int vud)
{
    bool res = n.AddNodeRef(name, io, rank, nn, v);

    if (res) {
	nn.SetUserData(nud);
	v.SetUserData(vud);
    }
    return res;
}

/*
 * This operation adds a new style vertex to the current node.
 */

bool
e4_XMLNodeVertexCreator::AddVertex(e4_Node &n,
				   const char *vname,
				   const char *vtype,
				   int ud)
{
    int rank;
    e4_Vertex v;

    /*
     * Initially (error condition) we set the saved vertex to an invalid one.
     */

    GetParser()->SetSavedVertex(invalidVertex);

    /*
     * If its a string or binary vertex, add a vertex with a dummy value.
     * We also save the vertex inside the associated parser so that the
     * close vertex operation can set the actual data.
     */

    if (strcmp(vtype, "string") == 0) {
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, "abcd", v)) {
	    parser->FlagError("Can't add string vertex");
	    return false;
	}
	v.SetUserData(ud);
	GetParser()->SetSavedVertex(v);
	return true;
    }

    if (strcmp(vtype, "binary") == 0) {
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, "abcd", 4, v)) {
	    parser->FlagError("Can't add binary vertex");
	    return false;
	}
	v.SetUserData(ud);
	GetParser()->SetSavedVertex(v);
	return true;
    }

    /*
     * Only vertices of type "string" or "binary" have new style
     * input. If it's something else, punt.
     */

    return false;
}

/*
 * This operation adds a new vertex to the current node.
 */

bool
e4_XMLNodeVertexCreator::AddVertex(e4_Node &n,
				   const char *vname,
				   const char *vtype,
				   const char *vval,
				   int ud)
{
    int rank;
    int iv;
    double fv;
    byte *bytes;
    int len;
    e4_Vertex v;

    /*
     * Indicate that there is no saved vertex.
     */

    GetParser()->SetSavedVertex(invalidVertex);

    if (strcmp(vtype, "int") == 0) {
	iv = atoi(vval);
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, iv, v)) {
	    parser->FlagError("Can't add int vertex");
	    return false;
	}
	v.SetUserData(ud);

	/*
	 * Finished adding the new vertex, fire the completion event.
	 */

	parser->CauseVertexCompletionEvent(v, NULL);

	return true;
    }

    if (strcmp(vtype, "double") == 0) {
	fv = (double) atof(vval);
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, fv, v)) {
	    parser->FlagError("Can't add double vertex");
	    return false;
	}
	v.SetUserData(ud);

	/*
	 * Finished adding the new vertex, fire the completion event.
	 */

	parser->CauseVertexCompletionEvent(v, NULL);

	return true;
    }

    if (strcmp(vtype, "string") == 0) {
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, vval, v)) {
	    parser->FlagError("Can't add string vertex");
	    return false;
	}
	v.SetUserData(ud);
	return true;
    }

    if (strcmp(vtype, "binary") == 0) {
	bytes = base64_decode(vval, &len);
	if (bytes == NULL) {
	    parser->FlagError("Can't parse binary value");
	    return false;
	}
	if (!AddVertexRef(n, vname, E4_IOLAST, rank, bytes, len, v)) {
	    parser->FlagError("Can't add binary vertex");
	    return false;
	}
	v.SetUserData(ud);

	/*
	 * Finished adding the new vertex, fire the completion event.
	 */

	parser->CauseVertexCompletionEvent(v, NULL);

	return true;
    }

    parser->FlagError("Can't add vertex -- illegal type");
    return false;
}

/*
 * Add a vertex of type int to the specified storage
 */

bool
e4_XMLNodeVertexCreator::AddVertexRef(e4_Node &n,
				      const char* name,
				      e4_InsertOrder io,
				      int &rank,
				      int i,
				      e4_Vertex &v)
{
    return n.AddVertexRef(name, io, rank, i, v);
}

/*
 * Add a vertex of type double to the specified storage
 */

bool
e4_XMLNodeVertexCreator::AddVertexRef(e4_Node &n,
				      const char* name,
				      e4_InsertOrder io,
				      int &rank,
				      double d,
				      e4_Vertex &v)
{
    return n.AddVertexRef(name, io, rank, d, v);
}

/*
 * Add a vertex of type string.
 */

bool
e4_XMLNodeVertexCreator::AddVertexRef(e4_Node &n,
				      const char* name,
				      e4_InsertOrder io, int &rank,
				      const char *s,
				      e4_Vertex &v)
{
    return n.AddVertexRef(name, io, rank, s, v);
}

/*
 * Add a vertex of type binary.
 */

bool
e4_XMLNodeVertexCreator::AddVertexRef(e4_Node &n,
				      const char* name,
				      e4_InsertOrder io,
				      int &rank,
				      const void* bytes,
				      int nbytes, 
				      e4_Vertex &v)
{
    return n.AddVertexRef(name, io, rank, bytes, nbytes, v);
}

/*
 * Add a vertex with a value of the node specified.
 */

bool
e4_XMLNodeVertexCreator::AddVertexRef(e4_Node &n,
				      const char* name,
				      e4_InsertOrder io,
				      int &rank,
				      e4_Node vn,
				      e4_Vertex &v)
{
    return n.AddVertexRef(name, io, rank, vn, v);
}
