/*
 * com_e4graph_Node.cpp --
 *
 *	This file contains implementations of the natives of the
 *	class com.e4graph.Node.
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * 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 "e4graph.h"
#include "j4graph.h"

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Node_isValid1(JNIEnv *envp, jobject me, jint n,
			       jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	envp->ExceptionClear();
	return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexCount1(JNIEnv *envp, jobject me, jint n,
				   jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return -1;
    }
    return nn.VertexCount();
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexCountWithName1(JNIEnv *envp, jobject me, jint n,
					   jint st, jint g, jstring name)
{
    e4_Node nn;
    const char *vn;
    jboolean copy;
    int vc;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return -1;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    vc = nn.VertexCountWithName(vn);
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    return vc;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexCountWithType1(JNIEnv *envp, jobject me, 
					   jint n, jint st, jint g,
					   jint vt)
{
    e4_Node nn;
    e4_VertexType vtt;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return -1;
    }
    switch (vt) {
    case com_e4graph_Vertex_ILLEGAL:
        vtt = E4_VTUNKNOWN;
	break;
    case com_e4graph_Vertex_INTEGER:
        vtt = E4_VTINT;
	break;
    case com_e4graph_Vertex_DOUBLE:
        vtt = E4_VTDOUBLE;
	break;
    case com_e4graph_Vertex_STRING:
        vtt = E4_VTSTRING;
	break;
    case com_e4graph_Vertex_BYTES:
        vtt = E4_VTBINARY;
	break;
    case com_e4graph_Vertex_NODE:
        vtt = E4_VTNODE;
	break;
    default:
	envp->ThrowNew(clsIncorrectVertexTypeException, "Illegal vertex type");
	return -1;
    }
    return nn.VertexCountWithType(vtt);
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setIntNthVertex1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g,
				       jstring name, jint nth, jint val)
{
    e4_Node nn;
    const char *vn;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.SetNthVertex(vn, nth, (int) val)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setDoubleNthVertex1(JNIEnv *envp, jobject me,
					  jint n, jint st, jint g,
					  jstring name, jint nth, jdouble val)
{
    e4_Node nn;
    const char *vn;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.SetNthVertex(vn, nth, val)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setStringNthVertex1(JNIEnv *envp, jobject me, 
					  jint n, jint st, jint g,
					  jstring name, jint nth,
					  jstring val)
{
    e4_Node nn;
    const char *vn, *vc;
    jboolean ncopy, vcopy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    vn = envp->GetStringUTFChars(name, &ncopy);
    vc = envp->GetStringUTFChars(val, &vcopy);
    if (!nn.SetNthVertex(vn, nth, vc)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vcopy) {
	envp->ReleaseStringUTFChars(val, vc);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setBytesNthVertex1(JNIEnv *envp, jobject me,
					 jint n, jint st, jint g,
					 jstring name, jint nth,
					 jbyteArray val)
{
    e4_Node nn;
    const char *vn;
    const void *bytes;
    int nbytes;
    jboolean ncopy, vcopy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    vn = envp->GetStringUTFChars(name, &ncopy);
    nbytes = envp->GetArrayLength(val);
    bytes = (const void *) envp->GetByteArrayElements(val, &vcopy);
    if (!nn.SetNthVertex(vn, nth, bytes, nbytes)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vcopy) {
	envp->ReleaseByteArrayElements(val, (jbyte *) bytes, 0);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setNodeNthVertex1(JNIEnv *envp, jobject me,
					jint n, jint st, jint g,
					jstring name, jint nth,
					jint ni, jint nst, jint ng)
{
    e4_Node nn, nnp;
    const char *vn;
    jboolean copy;

    if (st != nst) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "node not in same storage as this node");
	return;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    if (!GetValidNode(envp, st, ng, ni, nnp)) {
	return;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.SetNthVertex(vn, nth, nnp)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_setNthNodeNode1(JNIEnv *envp, jobject me,
				      jint n, jint st, jint g,
				      jstring name, jint nth)
{
    e4_Node nn, nnp;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    e4_NodeUniqueID nuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.SetNthNode(vn, nth, nnp) || !nnp.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex name");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) nnp.GetUniqueID(nuid);
	return envp->NewObject(clsNode, nodeCMID, st,
			       g, nuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setIntVertexByRank1(JNIEnv *envp, jobject me,
					  jint n, jint st, jint g, 
					  jint rank, jint val)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    if (!nn.SetVertexByRank(rank, (int) val)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setDoubleVertexByRank1(JNIEnv *envp, jobject me,
					     jint n, jint st, jint g,
					     jint rank, jdouble val)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    if (!nn.SetVertexByRank(rank, val)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setStringVertexByRank1(JNIEnv *envp, jobject me,
					     jint n, jint st, jint g,
					     jint rank, jstring val)
{
    e4_Node nn;
    const char *vv;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    vv = envp->GetStringUTFChars(val, &copy);
    if (!nn.SetVertexByRank(rank, vv)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(val, vv);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setBytesVertexByRank1(JNIEnv *envp, jobject me,
					    jint n, jint st, jint g, 
					    jint rank, jbyteArray val)
{
    e4_Node nn;
    const void *bytes;
    int nbytes;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    nbytes = envp->GetArrayLength(val);
    bytes = (const void *) envp->GetByteArrayElements(val, &copy);
    if (!nn.SetVertexByRank(rank, bytes, nbytes)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
    }
    if (copy) {
	envp->ReleaseByteArrayElements(val, (jbyte *) bytes, 0);
    }
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setNodeVertexByRank1(JNIEnv *envp, jobject me,
					   jint n, jint st, jint g,
					   jint rank,
					   jint ni, jint stn, jint gn)
{
    e4_Node nn, nnp;

    if (st != stn) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "node not in same storage as this node");
	return;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    if (!GetValidNode(envp, st, gn, ni, nnp)) {
	return;
    }
    if (!nn.SetVertexByRank(rank, nnp)) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
    }
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_setNodeByRankNode1(JNIEnv *envp, jobject me,
					 jint n, jint st, jint g,
					 jint rank)
{
    e4_Node nn, nnp;
    e4_NodeUniqueID nuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.SetNodeByRank(rank, nnp) || !nnp.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "invalid vertex rank");
	return NULL;
    }
    (void) nnp.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addNodeVertex1(JNIEnv *envp, jobject me,
				     jint n, jint st, jint g,
				     jstring name, jint ioi, jint rank,
				     jint ni, jint nst, jint ng)
{
    e4_Node nn, nnp;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (st != nst) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "node not in same storage as this node");
	return NULL;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!GetValidNode(envp, st, ng, ni, nnp)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.AddVertexRef(vn, io, r, nnp, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st, 
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addIntVertex1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g, 
				    jstring name, jint ioi, jint rank,
				    jint val)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.AddVertexRef(vn, io, r, (int) val, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st, 
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addDoubleVertex1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g,
				       jstring name, jint ioi, jint rank,
				       jdouble val)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.AddVertexRef(vn, io, r, val, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st, 
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addStringVertex1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g,
				       jstring name, jint ioi, jint rank,
				       jstring val)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *vn, *vc;
    jboolean ncopy, vcopy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &ncopy);
    vc = envp->GetStringUTFChars(val, &vcopy);
    if (!nn.AddVertexRef(vn, io, r, vc, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vcopy) {
	envp->ReleaseStringUTFChars(val, vc);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st, 
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addBytesVertex1(JNIEnv *envp, jobject me,
				      jint n, jint st, jint g,
				      jstring name, jint ioi, jint rank,
				      jbyteArray val)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *vn;
    const void *bytes;
    int nbytes;
    jboolean ncopy, vcopy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &ncopy); 
    nbytes = envp->GetArrayLength(val);
    bytes = (const void *) envp->GetByteArrayElements(val, &vcopy);
    if (!nn.AddVertexRef(vn, io, r, bytes, nbytes, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vcopy) {
	envp->ReleaseByteArrayElements(val, (jbyte *) bytes, 0);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st, 
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_addVertexNode1(JNIEnv *envp, jobject me,
				     jint n, jint st, jint g,
				     jstring name, jint ioi, jint rank)
{
    e4_Node nn, nv;
    e4_NodeUniqueID nuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.AddNode(vn, io, r, nv) || !nv.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "could not add vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) nv.GetUniqueID(nuid);
	return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_moveVertex1(JNIEnv *envp, jobject me,
				  jint n, jint st, jint g,
				  jint vi, jint vst, jint vg,
				  jint ioi, jint rank)
{
    e4_Node nn;
    e4_Vertex vv;
    e4_InsertOrder io = (e4_InsertOrder) ioi;
    int r = (int) rank;

    if (st != vst) {
	envp->ThrowNew(clsNoSuchVertexException,
		       "vertex not in same storage as this node");
	return;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    if (!GetValidVertex(envp, st, vg, vi, vv)) {
	return;
    }
    if (!nn.MoveVertex(vv, io, r)) {
	envp->ThrowNew(clsNoSuchVertexException, "could not move vertex");
    }
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_getNthInt1(JNIEnv *envp, jobject me,
				 jint n, jint st, jint g,
				 jstring name, jint nth)
{
    e4_Node nn;
    int val = 0;
    const char *vn;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return val;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, val)) {
	envp->ThrowNew(clsIncorrectVertexTypeException, "not integer valued");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    return (jint) val;
}

JNIEXPORT jdouble JNICALL
Java_com_e4graph_Node_getNthDouble1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g,
				    jstring name, jint nth)
{
    e4_Node nn;
    double val = 0.0;
    const char *vn;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return val;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, val)) {
	envp->ThrowNew(clsIncorrectVertexTypeException, "not double valued");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    return (jdouble) val;
}

JNIEXPORT jstring JNICALL
Java_com_e4graph_Node_getNthString1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g,
				    jstring name, jint nth)
{
    e4_Node nn;
    const char *vv;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, vv)) {
	envp->ThrowNew(clsIncorrectVertexTypeException, "not string valued");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	return envp->NewStringUTF(vv);
    }
    return NULL;
}

JNIEXPORT jbyteArray JNICALL
Java_com_e4graph_Node_getNthBytes1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g,
				   jstring name, jint nth)
{
    e4_Node nn;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    int nbytes;
    const void *bytes;
    jbyteArray res;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, bytes, nbytes)) {
	envp->ThrowNew(clsIncorrectVertexTypeException, "not binary valued");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	res = envp->NewByteArray((jsize) nbytes);
	envp->SetByteArrayRegion(res, 0, (jsize) nbytes, (jbyte *) bytes);
	return res;
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getNthNode1(JNIEnv *envp, jobject me,
				  jint n, jint st, jint g,
				  jstring name, jint nth)
{
    e4_Node nn, nnp;
    e4_NodeUniqueID nuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, nnp) || !nnp.IsValid())  {
	envp->ThrowNew(clsIncorrectVertexTypeException, "not node valued");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	(void) nnp.GetUniqueID(nuid);
	return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getNthValue1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g,
				   jstring name, jint nth)
{
    e4_Node nn;
    e4_Value v;
    e4_NodeUniqueID nuid;
    const char *vn;
    jboolean copy;
    jboolean errored = JNI_FALSE;
    jobject obj;
    jbyteArray arr;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetNthVertex(vn, nth, v)) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (!errored) {
	obj = envp->NewObject(clsValue, valueCMID);
	envp->SetIntField(obj, typeID, (jint) v.vertexType);
	switch (v.vertexType) {
	case E4_VTINT:
	    envp->SetIntField(obj, integerValueID, v.u.i);
	    break;
	case E4_VTDOUBLE:
	    envp->SetDoubleField(obj, doubleValueID, v.u.d);
	    break;
	case E4_VTSTRING:
	    envp->SetObjectField(obj, 
				 stringValueID,
				 envp->NewStringUTF(v.u.s));
	    break;
	case E4_VTBINARY:
	    arr = envp->NewByteArray(v.u.b.nbytes);
	    envp->SetByteArrayRegion(arr, 0,
				     v.u.b.nbytes, (jbyte *) v.u.b.bytes);
	    envp->SetObjectField(obj, bytesValueID, arr);
	    break;
	case E4_VTNODE:
	    (void) v.n.GetUniqueID(nuid);
	    envp->SetObjectField(obj,
				 nodeValueID,
				 envp->NewObject(clsNode, nodeCMID, st,
						 g, nuid.GetUniqueID()));
	    break;
	}

	return obj;
    }
    return NULL;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_getIntByRank1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g,
				    jint rank)
{
    e4_Node nn;
    int val;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return 0;
    }
    if (!nn.GetVertexByRank(rank, val)) {
	envp->ThrowNew(clsNoSuchVertexException, "not integer valued");
	return 0;
    }
    return val;
}

JNIEXPORT jdouble JNICALL
Java_com_e4graph_Node_getDoubleByRank1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g,
				       jint rank)
{
    e4_Node nn;
    double val;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return 0.0;
    }
    if (!nn.GetVertexByRank(rank, val)) {
	envp->ThrowNew(clsNoSuchVertexException, "not double valued");
	return 0.0;
    }
    return val;
}

JNIEXPORT jstring JNICALL
Java_com_e4graph_Node_getStringByRank1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g,
				       jint rank)
{
    e4_Node nn;
    const char *val;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetVertexByRank(rank, val)) {
	envp->ThrowNew(clsNoSuchVertexException, "not string valued");
	return NULL;
    }
    return envp->NewStringUTF(val);
}

JNIEXPORT jbyteArray JNICALL
Java_com_e4graph_Node_getBytesByRank1(JNIEnv *envp, jobject me,
				      jint n, jint st, jint g,
				      jint rank)
{
    e4_Node nn;
    jbyteArray arr;
    const void *bytes;
    int nbytes;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetVertexByRank(rank, bytes, nbytes)) {
	envp->ThrowNew(clsNoSuchVertexException, "not binary valued");
	return NULL;
    }
    arr = envp->NewByteArray(nbytes);
    envp->SetByteArrayRegion(arr, 0, nbytes, (jbyte *) bytes);
    return arr;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getNodeByRank1(JNIEnv *envp, jobject me,
				     jint n, jint st, jint g,
				     jint rank)
{
    e4_Node nn, nnp;
    e4_NodeUniqueID nuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetVertexByRank(rank, nnp) || !nnp.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "not node valued");
	return NULL;
    }
    (void) nnp.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getValueByRank1(JNIEnv *envp, jobject me,
				      jint n, jint st, jint g,
				      jint rank)
{
    e4_Node nn;
    e4_Value v;
    e4_NodeUniqueID nuid;
    jobject obj;
    jbyteArray arr;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetVertexByRank(rank, v)) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
	return NULL;
    }
    obj = envp->NewObject(clsValue, valueCMID);
    envp->SetIntField(obj, typeID, (jint) v.vertexType);
    switch (v.vertexType) {
    case E4_VTINT:
	envp->SetIntField(obj, integerValueID, v.u.i);
	break;
    case E4_VTDOUBLE:
	envp->SetDoubleField(obj, doubleValueID, v.u.d);
	break;
    case E4_VTSTRING:
	envp->SetObjectField(obj, 
			     stringValueID,
			     envp->NewStringUTF(v.u.s));
	break;
    case E4_VTBINARY:
	arr = envp->NewByteArray(v.u.b.nbytes);
	envp->SetByteArrayRegion(arr, 0,
				 v.u.b.nbytes, (jbyte *) v.u.b.bytes);
	envp->SetObjectField(obj, bytesValueID, arr);
	break;
    case E4_VTNODE:
	(void) v.n.GetUniqueID(nuid);
	envp->SetObjectField(obj,
			     nodeValueID,
			     envp->NewObject(clsNode, nodeCMID, st,
					     g, nuid.GetUniqueID()));
	break;
    }

    return obj;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getVertex1(JNIEnv *envp, jobject me, 
				 jint n, jint st, jint g,
				 jstring name, jint nth)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    const char *nm;
    jboolean copy;
    jboolean errored = JNI_FALSE;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    nm = envp->GetStringUTFChars(name, &copy);
    if (!nn.GetVertexRef(nm, nth, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
	errored = JNI_TRUE;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, nm);
    }
    if (!errored) {
	(void) v.GetUniqueID(vuid);
	return envp->NewObject(clsVertex, vertexCMID, st,
			       g, vuid.GetUniqueID());
    }
    return NULL;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getVertexByRank1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g, 
				       jint rank)
{
    e4_Node nn;
    e4_Vertex v;
    e4_VertexUniqueID vuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetVertexRefByRank(rank, v) || !v.IsValid()) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
	return NULL;
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, st, g, vuid.GetUniqueID());
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexType1(JNIEnv *envp, jobject me,
				  jint n, jint st, jint g,
				  jstring name, jint nth)
{
    e4_Node nn;
    const char *vn;
    jboolean copy;
    e4_VertexType vt;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return (jint) E4_VTUNKNOWN;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    vt = nn.VertexType(vn, nth);
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vt == E4_VTUNKNOWN) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
    }
    return (jint) vt;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexTypeByRank1(JNIEnv *envp, jobject me,
					jint n, jint st, jint g,
					jint rank)
{
    e4_Node nn;
    e4_VertexType vt;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return (jint) E4_VTUNKNOWN;
    }
    vt = nn.VertexTypeByRank(rank);
    if (vt == E4_VTUNKNOWN) {
	envp->ThrowNew(clsNoSuchVertexException, "no such vertex");
    }
    return (jint) vt;
}

JNIEXPORT jstring JNICALL
Java_com_e4graph_Node_vertexName1(JNIEnv *envp, jobject me,
				  jint n, jint st, jint g,
				  jint rank)
{
    e4_Node nn;
    const char *nm;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    nm = nn.VertexName(rank);
    if (nm == NULL) {
	envp->ThrowNew(clsNoSuchVertexException, "no vertex with that rank");
	return NULL;
    }
    return envp->NewStringUTF(nm);
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_renameVertex1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g,
				    jint rank, jstring newname)
{
    e4_Node nn;
    const char *nnm;
    jboolean copy;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    nnm = envp->GetStringUTFChars(newname, &copy);
    if (!nn.RenameVertex(rank, nnm)) {
	envp->ThrowNew(clsNoSuchVertexException, "no vertex with that rank");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(newname, nnm);
    }
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_vertexRank1(JNIEnv *envp, jobject me, 
				  jint n, jint st, jint g,
				  jstring name, jint nth)
{
    e4_Node nn;
    const char *nm;
    jboolean copy;
    jint rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_VERTEXNOTFOUND;
    }
    nm = envp->GetStringUTFChars(name, &copy);
    rank = nn.VertexRank(nm, nth);
    if (rank == E4_VERTEXNOTFOUND) {
	envp->ThrowNew(clsNoSuchVertexException, "no vertex with that name");
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, nm);
    }
    return rank;
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Node_exists1(JNIEnv *envp, jobject me,
			      jint n, jint st, jint g,
			      jstring name, jint nth)
{
    e4_Node nn;
    const char *nm;
    jboolean copy;
    jint rank;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return JNI_FALSE;
    }
    nm = envp->GetStringUTFChars(name, &copy);
    rank = nn.VertexRank(nm, nth);
    if (copy) {
	envp->ReleaseStringUTFChars(name, nm);
    }
    if (rank == E4_VERTEXNOTFOUND) {
	return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_parent1(JNIEnv *envp, jobject me,
			      jint n, jint st, jint g,
			      jint nth)
{
    e4_Node nn, np;
    e4_NodeUniqueID nuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetParent(nth, np) || !np.IsValid()) {
	envp->ThrowNew(clsNoSuchNodeException, "no parent with that rank");
	return NULL;
    }
    (void) np.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_parentCount1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_NODENOTFOUND;
    }
    return nn.ParentCount();
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_occurrenceCount1(JNIEnv *envp, jobject me,
				       jint n, jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_NODENOTFOUND;
    }
    return nn.OccurrenceCount();
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_nodeOccurrenceCount1(JNIEnv *envp, jobject me,
					   jint n, jint st, jint g, 
					   jint pi, jint pst, jint pg)
{
    e4_Node nn, pn;

    if (st != pst) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "parent not in same storage as this node");
	return E4_NODENOTFOUND;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_NODENOTFOUND;
    }
    if (!GetValidNode(envp, st, pg, pi, pn)) {
	return E4_NODENOTFOUND;
    }
    return nn.OccurrenceCount(pn);
}

JNIEXPORT jint JNICALL Java_com_e4graph_Node_parentRank1(JNIEnv *envp,
							 jobject me,
							 jint n,
							 jint st,
							 jint g,
							 jint pi, 
							 jint pst,
							 jint pg)
{
    e4_Node nn, pn;

    if (st != pst) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "parent not in same storage as this node");
	return E4_NODENOTFOUND;
    }
    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_NODENOTFOUND;
    }
    if (!GetValidNode(envp, st, pg, pi, pn)) {
	return E4_NODENOTFOUND;
    }
    return nn.ParentRank(pn);
}

JNIEXPORT jint JNICALL Java_com_e4graph_Node_rankInParent1(JNIEnv *envp,
							   jobject me,
							   jint n, 
							   jint st,
							   jint g,
							   jint nth)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return E4_NODENOTFOUND;
    }
    return nn.GetRankInParent(nth);
}

JNIEXPORT jstring JNICALL
Java_com_e4graph_Node_nameInParent1(JNIEnv *envp, jobject me,
				    jint n, jint st, jint g,
				    jint nth)
{
    e4_Node nn;
    const char *nm;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    nm = nn.GetNameInParent(nth);
    if (nm == NULL) {
	envp->ThrowNew(clsNoSuchNodeException, "no such parent");
	return NULL;
    }
    return envp->NewStringUTF(nm);
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Node_isRoot1(JNIEnv *envp, jobject me,
			      jint n, jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return JNI_FALSE;
    }
    return nn.IsRoot() ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Node_getRootNode1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g)
{
    e4_Node nn, nr;
    e4_NodeUniqueID nuid;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return NULL;
    }
    if (!nn.GetRootNode(nr) || !nr.IsValid() || !nr.IsRoot()) {
	envp->ThrowNew(clsNoSuchNodeException, "cannot find root node");
	return NULL;
    }
    (void) nr.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, st, g, nuid.GetUniqueID());
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_detach1(JNIEnv *envp, jobject me,
			      jint n, jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    (void) nn.Detach();
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Node_isDetached1(JNIEnv *envp, jobject me,
				  jint n, jint st, jint g)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return JNI_FALSE;
    }
    return nn.IsDetached() ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Node_getUserData1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g)
{
    e4_Node nn;
    int ud;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return 0;
    }
    (void) nn.GetUserData(ud);
    return ud;
}

JNIEXPORT void JNICALL
Java_com_e4graph_Node_setUserData1(JNIEnv *envp, jobject me,
				   jint n, jint st, jint g,
				   jint nud)
{
    e4_Node nn;

    if (!GetValidNode(envp, st, g, n, nn)) {
	return;
    }
    (void) nn.SetUserData(nud);
}
